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/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..2d30faefa0b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,10 @@ +Follow this checklist to help us incorporate your contribution quickly and easily: + + - [ ] Make sure there is a [JIRA issue](https://issues.apache.org/jira/browse/SCB) filed for the change (usually before you start working on it). Trivial changes like typos do not require a JIRA issue. Your pull request should address just this issue, without pulling in other changes. + - [ ] Each commit in the pull request should have a meaningful subject line and body. + - [ ] Format the pull request title like `[SCB-XXX] Fixes bug in ApproximateQuantiles`, where you replace `SCB-XXX` with the appropriate JIRA issue. + - [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. + - [ ] Run `mvn clean install -Pit` to make sure basic checks pass. A more thorough check will be performed on your pull request automatically. + - [ ] If this contribution is large, please file an Apache [Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf). + +--- diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..7f7754fc299 --- /dev/null +++ b/.github/dependabot.yml @@ -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. +# +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "monthly" + open-pull-requests-limit: 20 diff --git a/.github/workflows/checkstyle.yml b/.github/workflows/checkstyle.yml new file mode 100644 index 00000000000..3f97b9f1818 --- /dev/null +++ b/.github/workflows/checkstyle.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: checkstyle +on: + pull_request: + branches: + - master + +jobs: + checkstyle: + timeout-minutes: 60 + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + - name: Set up jdk + uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'temurin' + - name: checkstyle + 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 new file mode 100644 index 00000000000..24118c35c99 --- /dev/null +++ b/.github/workflows/maven.yml @@ -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. +# + +name: Java CI with Maven + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + timeout-minutes: 60 + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v6 + - name: Set up jdk + uses: actions/setup-java@v5 + with: + java-version: '17.0.8' + distribution: 'temurin' + - 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 verify -Dcheckstyle.skip=true -B -Pdocker -Pjacoco -Pit -Pcoverage diff --git a/.github/workflows/rat_check.yml b/.github/workflows/rat_check.yml new file mode 100644 index 00000000000..c2db2508fea --- /dev/null +++ b/.github/workflows/rat_check.yml @@ -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. +# + +name: rat check +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + rat_check: + timeout-minutes: 60 + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + - name: Set up jdk + uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'temurin' + - name: rat check + run: mvn apache-rat:check -B -Pit,release 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/.gitignore b/.gitignore index b298460c12b..67e5a7dcf91 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,8 @@ __pycache__/ # IntelliJ, based on http://devnet.jetbrains.net/docs/DOC-1186 .idea/ *.iml +*.ipr +*.iws # logs and trace *.log @@ -75,3 +77,9 @@ __pycache__/ # SVN metadata .svn/ + +# Mac +.DS_Store + +# gradle +.gradle diff --git a/.travis.settings.xml b/.travis.settings.xml deleted file mode 100644 index ed649607336..00000000000 --- a/.travis.settings.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - ossrh - ${env.SONATYPE_USERNAME} - ${env.SONATYPE_PASSWORD} - - - diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f1490978137..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -sudo: required - -language: java - -services: - - docker - -script: - - mvn clean install -Ddocker.showLogs -Pdocker -Pjacoco -Pit -Pcoverage coveralls:report - -after_success: - - mvn deploy -DskipTests --settings .travis.settings.xml - -env: - global: - - secure: "agG8Yu9+2dNU0njxnupfORjYb3WABuJxumiHD0TEQDf2inb+uImuher22wGKq5fxkTIlPB1DobByuDMRtLknW+gBKFddiSgK6ymeX/vu7ukmgU6hPApy4UgMhHmSKR0wgR/vdLuysvNRXhyOcP7fFKu5I6ELu7jNiZNLVzC60N3jxUhqghX+m4W9Rig2kbEgi6ZyWw3MemHj2TtpXhBhYKXRyJddIGz0PiX3ZYXWU0erRN1w6CUC0NrvUiIs/ZMSWwR+SBLaGx4th6Lwov8+ZoPwmV2r9tAgZHgzMM5nAjNQBsUlpH07fjo0GSW9RTgoUX65nQJqwsUeLF9fq7KJxTS/5YsQJEY/TUP/NLAHJe0p8HhAHwh6t03GV4l+Jd4h7DmKklSJqv+4SkVKt8ACQvt450+PUQGF0MdkaOhVMzpWlfiSZSaeU0HM/kiZe6T0e6ZGcrBL/NrLDHlCpyc5eaheSYU3rnnU4f+QK3MmvckLXy8+DSZlWVBXJ759SlBjUWcpqEEt/XRJZCBEmpGOs5A6BNuRAU8A7c9Go92Tqs93rbxKgvAMkXK2P9H0kKs4Pxkc+lrTnihhuw8HEnDH5TsvtVibknH1jrwPp5w4Br59WWZ6rR5HKz9r9VUeWDfEGUiMdihAXRd7urLCc3JwsC0BwNzDGGAiDM1OdbyEALM=" - - secure: "MaLfZIH/DNWriiAWn0mGbbYN6D0iy4t+W6NQNAFDdzgkGcByrPOtZ+Gf2axrpjM184n0CHtnBFD9RLaK0fBnXYz3+vcbM4lsE0XkxjKkZZaBgJ86cwqt1t31Kmevn33m/oFck914I2VlzDFszL4ok/WFlj5/X7Xr0YEUxSrW7exRHRw28jB+j85JqzREA2LGGJD4samT1STPqFQmFg9d45PKonx9hboPc5peNSbJFM1HCHMG9HWLS8vEKWXqmPh7/3NyjHtUBl05bbC8xx5X3tTDB6cq5i5EdflaVNO5nAHxoT+FxX/GnjmXVHx7uPv6LClayKpViY+HlmoFbBZ0F8bVi43+Yjal2NacgJDNEf5IM1QtuymMgiepAGWJ5l1prXSGNosjLLQNWvxfc04aa2j3t+aPIlYEE7aQ1NMqRceCnKMOJeFln1J5vIx6iW83Db57wZJPowKRgmMUgwiH5sYRO5MIfsz8s1Y79xuSWYoBbb0rRrKkw4Bph6Dc5TOKeSby4OKI0j2ao0nRRFgcdxdyfcWyNjUQ/zo1PnlybZ+fuw0E1+EU5pr6iOYZT0O7GebJQ7Qg9uX81UssBOpj9kUJIikl/v/hWLzQ/xIpBQtkJtuCwXAfIZZuphnMPtBpDqwdgcZMHAyNdkIQPJG2IaHW25qQXySyL7keMxLUuZ0=" 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 8dada3edaf5..7898c758729 100644 --- a/LICENSE +++ b/LICENSE @@ -199,3 +199,50 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +======================================================================= +Apache ServiceComb Java Chassis Subcomponents: + +The Apache ServiceComb Java Chassis project contains subcomponents with +separate copyright notices and license terms. Your use of the source +for these subcomponents is subject to the terms and conditions of the +following licenses. + + +================================================================ +For foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/MimeTypesUtils.java + transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java +================================================================ +This product bundles files from vertx which is licensed under the Apache License v2. +For details, see https://github.com/vert-x3/vertx-web + +================================================================ +For swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/AbstractBaseIntegerProperty.java +================================================================ +This product bundles files from swagger which is licensed under the Apache License v2. +For details, see https://github.com/swagger-api/swagger-core + +================================================================ +For foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/ArrayFieldMapEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldMapEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldSchema.java + foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldTypeUtils.java + foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/HashFieldMapEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/ByteArrayInputEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/InputEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/OutputEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/package-info.java + foundations/foundation-protobuf/src/main/java/io/protostuff/ProtobufOutputEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaReader.java + 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 + +================================================================ +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 f547a72bd90..e4df59ae99a 100644 --- a/NOTICE +++ b/NOTICE @@ -1,335 +1,5 @@ -Copyright 2017 Huawei Technologies Co., Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. +Apache ServiceComb Java Chassis +Copyright 2017-2025 The Apache Software Foundation -================================================================ -Dependencies: - -Software:HornetQ 2.2.21 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://hornetq.jboss.org - -Software:JSON in JavaScript 20111019 - * LICENSE: - * http://www.thepublicdomain.org/ - * HOMEPAGE: - * http://www.json.org/ - -Software:jqplot 1.0.8r1250 - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * http://jqplot.org/ - -Software:Hibernate Validator 4.3.1 -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://hibernate.org/validator - -Software:Jackson 1.9.2 -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://jackson.codehaus.org/ - -Software:Apache Jakarta Commons DBCP 2.0.1 -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://commons.apache.org/ - -Software:Apache Jakarta Commons IO 2.4 -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://commons.apache.org/ - -Software:Eclipse AspectJ 1.8.5 -License:Mozilla Public License Version 1.1 - * LICENSE: - * http://www.mozilla.org/en-US/MPL/1.1 - * HOMEPAGE: - * http://www.eclipse.org/aspectj/ - -Software:Java Persistence API 2.0.0 -License:EPL V1.0 - * LICENSE: - * http://www.eclipse.org/legal/epl-v10.html - * HOMEPAGE: - * http://www.oracle.com/technetwork/java/javaee/tech/ - -Software:JBoss - Javassist 3.18.1-GA -License:LGPL V2.1;Apache License Version 2.0 - * LICENSE: - * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * https://github.com/jboss-javassist/javassist/tree/3.18 - -Software:kazoo 2.1 -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://kazoo.readthedocs.io/ - -Software:lua-cjson 2.1.0 -License:MIT License - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * https://luarocks.org/ - -Software:LuaJIT 2.0.4 -License:MIT License V2 - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * http://luarocks.org/ - -Software:mybatis 3.2.7 -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://www.mybatis.org/mybatis-3/ - -Software:mybatis-spring 1.2.0 -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://www.mybatis.org/ - -Software:fuzzy 0.1.1 -License:MIT - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * http://pypi.python.org/pypi/ - -Software:lodash 4.11.1 -License:MIT - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * https://lodash.com/ - -Software:angular.js 1.4.8 -License:MIT - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * https://angularjs.org/ - -Software:Node.js 4.4.6 -License:MIT - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * https://node.js.org/ - -Software:redux 3.4.0 -License:MIT - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * http://redux.js.org/ - -Software:camelcase 2.1.1 -License:MIT - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * http://www.camelcase.com/ - -Software:normalize.css 4.0.0 -License:MIT - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * http://necolas.github.io/normalize.css/ - -Software:Apache Curator 2.8.0 -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://curator.apache.org/ - -Software:okhttp 3.4.1 -License:Apache License 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://square.github.io/okhttp/ - -Software:spring cloud sleuth 1.0.1.RELEASE -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://www.spring.io/ - -Software:spring cloud stream 1.0.0.RELEASE -License:Apache License 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://www.spring.io/ - -Software:protostuff 1.0.7 -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://www.protostuff.io/ - -Software:jsoup 1.8.3 -License:MIT License - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * http://jsoup.org/ - -Software:restlet-framework-java 2.2.1 -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * https://github.com/restlet/restlet-framework-java - -Software:rxjava 1.1.6 -License:Apache License 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://reactive.io/ - -Software:netflix-commons 0.1.1 -License:Apache License 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * https://github.com/Netflix/netflix-commons - -Software:PyYAML - a YAML parser and emitter for Python 3.11 -License:MIT License - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * https://pypi.python.org/pypi/PyYAML/ - -Software:six 1.10.0 -License:MIT License - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * https://pypi.python.org/pypi/six/ - -Software:HttpAsyncClient 4.1.2 -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://hc.apache.org/httpcomponents-asyncclient-dev/index.html - -Software:spring cloud starter zuul 1.1.0RELEASE -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://www.spring.io/ - -Software:Hibernate Validator 5.3.2.Final -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://hibernate.org/validator - -Software:HttpComponents Core 4.4.4 -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://hc.apache.org/ - -Software:react-router 2.8.1 -License:The MIT License - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * https://www.npmjs.com/package/react-router/ - -Software:react-router-redux 4.0.7 -License:The MIT License - * LICENSE: - * http://mit-license.org/ - * HOMEPAGE: - * https://www.npmjs.com/package/react-router-redux/ - -Software:Jetty - Java HTTP Servlet Server Jetty - Java HTTP Servlet Server 9.2.21 -License:Apache License 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://jetty.mortbay.org - -Software:cal10n-api 0.8.1 -License:Apache License 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://cal10.qos.ch/ - -Software:protostuff 1.5.2 -License:Apache License 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://www.protostuff.io/ - -Software:Spring Cloud Commons 1.1.0.RELEASE -License:Apache License Version 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://www.spring.io/ - -Software:vis 4.17.0 -License:Apache License 2.0 & The MIT License - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 http://mit-license.org/ - * HOMEPAGE: - * http://visjs.org/ - -Software:Apache Tomcat 7.0.75 -License:Apache License 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://tomcat.apache.org/ - -Software:fastjson 1.2.29 -License:Apache License 2.0 - * LICENSE: - * http://www.apache.org/licenses/LICENSE-2.0 - * HOMEPAGE: - * http://code.alibabatech.com/wiki/fastjson \ No newline at end of file +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/README.md b/README.md index 574e691879b..b2fbc167625 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,97 @@ -# Java Chassis [![Build Status](https://travis-ci.org/ServiceComb/ServiceComb-Java-Chassis.svg?branch=master)](https://travis-ci.org/ServiceComb/ServiceComb-Java-Chassis?branch=master)[![Coverage Status](https://coveralls.io/repos/github/ServiceComb/ServiceComb-Java-Chassis/badge.svg?branch=master)](https://coveralls.io/github/ServiceComb/ServiceComb-Java-Chassis?branch=master) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.servicecomb/java-chassis-core/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Cio.servicecomb) -[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) +# 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) -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. -## Quick Start +> If you find this project helpful, please don't forget to `star` it. -Provider service: +# releases + +| 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 | + +Java Chassis core dependencies + +| 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 | + +> 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. + +> NOTICE: Java Chassis 1 reached its end of support now after it's first release from 2018. + +# Quick Start + +* Define API ```java -import io.servicecomb.*; -@RpcSchema(schemaId = "helloworld") -public class HelloWorldProvider implements HelloWorld { - public String sayHello(String name) { - return "Hello " + name; - } +@RequestMapping(path = "/provider") +public interface ProviderService { + @GetMapping("/sayHello") + String sayHello(@RequestParam("name") String name); } ``` -Consumer service: +* Provider service ```java -import io.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 = "ProviderController", schemaInterface = ProviderService.class) +public class ProviderController implements ProviderService { + @Override + public String sayHello(String name) { + return "Hello " + name; + } } ``` -## Documentation +* Consumer service +```java +@Configuration +public class ProviderServiceConfiguration { + @Bean + public ProviderService providerService() { + return Invoker.createProxy("provider", "ProviderController", ProviderService.class); + } +} +``` + +Invoke Provider service with RPC +```java +@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) . -Project documentation is available on the [ServiceComb website][servicecomb-website]. +# Documentation -[servicecomb-website]: http://servicecomb.io/ +Project documentation is available on the [ServiceComb Java Chassis Developer Guide][java-chassis-developer-guide]. -## Building +[java-chassis-developer-guide]: https://servicecomb.apache.org/references/java-chassis/zh_CN/ -You don’t need to build from source to use Java Chassis (binaries in repo.servicecomb.io), but if you want to try out the latest and greatest, Java Chassis can be easily built with the maven. You also need JDK 1.8. +# Building + + You don’t need to build from source to use Java Chassis (binaries in apache nexus ), but if you want to try out the latest and greatest, Java Chassis can be easily built with the maven. You also need JDK 17. mvn clean install The first build may take a longer than expected as Maven downloads all the dependencies. -## Automated Testing +# Automated Testing To build the docker image and run the integration tests with docker, you can use maven docker profile @@ -55,17 +101,16 @@ The first build may take a longer than expected as Maven downloads all the depen mvn clean install -Pdocker -Pit -Pdocker-machine -## Contact - -Bugs: [issues](https://github.com/ServiceComb/java-chassis/issues) +# Contact -mailing lists: [users](https://groups.google.com/forum/#!forum/servicecomb-users) , [developers](https://groups.google.com/forum/#!forum/servicecomb-developers) +Bugs: [issues](https://issues.apache.org/jira/browse/SCB) +mailing list: [subscribe](mailto:dev-subscribe@servicecomb.apache.org) [dev](https://lists.apache.org/list.html?dev@servicecomb.apache.org) -## Contributing -See CONTRIBUTING for details on submitting patches and the contribution workflow. +# Contributing -## Reporting Issues +See [CONTRIBUTING](http://servicecomb.apache.org/developers/contributing) for details on submitting patches and the contribution workflow. -See reporting bugs for details about reporting any issues. +# License +Licensed under an [Apache 2.0 license](LICENSE). diff --git a/README_ZH.md b/README_ZH.md new file mode 100644 index 00000000000..bfae9746c2b --- /dev/null +++ b/README_ZH.md @@ -0,0 +1,124 @@ +# 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。它包含如下特性: + +* 基于Open API的契约优先(API First)开发模式,满足开发过程可管理、开发规范可验证要求。 +* 多种开发风格,REST(JAX-RS、Spring MVC)和RPC等,高效支持遗留系统迁移和新系统开发场景。 +* 多种通信协议, HTTP over Vert.x、Http Over Servlet、Highway等,满足不同场景对于性能、韧性的需求。 +* 统一一致的服务提供者、服务消费者处理链,易于扩展新功能。 +* 提供服务发现、配置管理、熔断、限流、灰度发布等开箱即用的服务治理能力。 + +可以通过[设计选型参考](https://servicecomb.apache.org/references/java-chassis/zh_CN/start/design.html) 了解更多特性和设计原理。 + +> 如果发现项目能帮助到您,别忘了点击右上角`star`表示鼓励。 + +# 发布版本 + +| 版本火车 | 最新版本 | 编译的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 | 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 | + +> NOTICE: Open API 3.0.x 不兼容 2.0.x, 因此Java Chassis 2、Java Chassis 1不能与Java Chassis 3共存互访. 升级Java Chassis 3, 需要将相关的消费者、提供者和边缘服务同时升级. + +> NOTICE: Java Chassis 1 第一个版本于2018发布,已经停止更新. + +# 快速开始 + +* 定义服务契约 + +```java +@RequestMapping(path = "/provider") +public interface ProviderService { + @GetMapping("/sayHello") + String sayHello(@RequestParam("name") String name); +} +``` + +* 定义提供者 + +```java +@RestSchema(schemaId = "ProviderController", schemaInterface = ProviderService.class) +public class ProviderController implements ProviderService { + @Override + public String sayHello(String name) { + return "Hello " + name; + } +} +``` + +* 定义消费者 + +```java +@Configuration +public class ProviderServiceConfiguration { + @Bean + public ProviderService providerService() { + return Invoker.createProxy("provider", "ProviderController", ProviderService.class); + } +} +``` + +使用RPC方式访问提供者。 + +```java +@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 + +开发者可以通过MAVEN仓库使用Java Chassis。 如果需要构建项目,需要使用JDK 17版本,并预先安装maven。 + + mvn clean install + +# 运行测试用例 + +开发者需要预先安装docker。 + + mvn clean install -Pdocker -Pit + +使用docker machine。 + + mvn clean install -Pdocker -Pit -Pdocker-machine + +# 联系我们 + +报告缺陷: [issues](https://issues.apache.org/jira/browse/SCB) + +邮件列表: [subscribe](mailto:dev-subscribe@servicecomb.apache.org) [dev](https://lists.apache.org/list.html?dev@servicecomb.apache.org) + +# 参与代码提交 + +参考 [如何做贡献](http://servicecomb.apache.org/cn/developers/contributing). + +# License +Licensed under an [Apache 2.0 license](LICENSE). 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 new file mode 100644 index 00000000000..3813950a5c8 --- /dev/null +++ b/ci/checkstyle/checkstyle.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ci/checkstyle/suppressions.xml b/ci/checkstyle/suppressions.xml new file mode 100644 index 00000000000..57e56d56c35 --- /dev/null +++ b/ci/checkstyle/suppressions.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/ci/spotbugs/exclude.xml b/ci/spotbugs/exclude.xml new file mode 100644 index 00000000000..b7c53996e85 --- /dev/null +++ b/ci/spotbugs/exclude.xml @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/clients/README.md b/clients/README.md new file mode 100644 index 00000000000..53180a4a03b --- /dev/null +++ b/clients/README.md @@ -0,0 +1,13 @@ +# About + +This module implements common http clients for servicecomb-service-center, servicecomb-kie and other +3rd-party services. + +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-java-chassis, 可以用于 Spring Cloud, Dubbo 等项目。 diff --git a/clients/config-center-client/pom.xml b/clients/config-center-client/pom.xml new file mode 100644 index 00000000000..e7e2fa1fc2b --- /dev/null +++ b/clients/config-center-client/pom.xml @@ -0,0 +1,45 @@ + + + + + + clients + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + config-center-client + ServiceComb::Clients::Config Center Client + + + + + org.apache.servicecomb + http-client-common + + + + org.apache.servicecomb + config-clients-common + + + 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 new file mode 100644 index 00000000000..977c84ef63f --- /dev/null +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterAddressManager.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.config.center.client; + +import java.util.List; + +import org.apache.servicecomb.http.client.common.AbstractAddressManager; +import org.apache.servicecomb.http.client.common.URLEndPoint; +import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; + +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + +public class ConfigCenterAddressManager extends AbstractAddressManager { + + public ConfigCenterAddressManager(String projectName, List addresses, EventBus eventBus, String region, + String availableZone) { + super(projectName, addresses, region, availableZone); + eventBus.register(this); + } + + @Override + protected String normalizeUri(String endpoint) { + String address = new URLEndPoint(endpoint).toString(); + return formatAddress(address); + } + + @Subscribe + public void onRefreshEndpointEvent(RefreshEndpointEvent event) { + refreshEndpoint(event, RefreshEndpointEvent.CONFIG_CENTER_NAME); + } +} 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 new file mode 100644 index 00000000000..8ed31a5a61b --- /dev/null +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java @@ -0,0 +1,216 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; +import org.apache.servicecomb.config.common.exception.OperationException; +import org.apache.servicecomb.config.center.client.model.QueryConfigurationsRequest; +import org.apache.servicecomb.config.center.client.model.QueryConfigurationsResponse; +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.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); + + public static final String DEFAULT_APP_SEPARATOR = "@"; + + public static final String DEFAULT_SERVICE_SEPARATOR = "#"; + + public static final String REVISION = "revision"; + + public static final String APPLICATION_CONFIG = "application"; + + 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, String address) { + String dimensionsInfo = buildDimensionsInfo(request, true); + QueryConfigurationsResponse queryConfigurationsResponse = new QueryConfigurationsResponse(); + + Map configurations = new HashMap<>(); + + String uri = null; + try { + uri = address + "/configuration/items?dimensionsInfo=" + + HttpUtils.encodeURLParam(dimensionsInfo) + "&revision=" + request.getRevision(); + + Map headers = new HashMap<>(); + headers.put("x-environment", request.getEnvironment()); + HttpRequest httpRequest = new HttpRequest(uri, headers, null, + HttpRequest.GET); + + HttpResponse httpResponse = httpTransport.doRequest(httpRequest); + recordAndSendUnAuthorizedEvent(httpResponse, address); + if (httpResponse.getStatusCode() == HttpStatus.SC_OK) { + Map> allConfigMap = HttpUtils.deserialize( + httpResponse.getContent(), + new TypeReference>>() { + }); + + if (allConfigMap.get(REVISION) != null) { + queryConfigurationsResponse.setRevision((String) allConfigMap.get(REVISION).get("version")); + } + + 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); + return queryConfigurationsResponse; + } else if (httpResponse.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { + queryConfigurationsResponse.setChanged(false); + 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 { + throw new OperationException( + "read response failed. status:" + + httpResponse.getStatusCode() + + "; message:" + + httpResponse.getMessage() + + "; content:" + + httpResponse.getContent()); + } + } catch (IOException e) { + addressManager.recordFailState(address); + LOGGER.error("query configuration from {} failed, message={}", uri, e.getMessage()); + throw new OperationException("", 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 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 + + request.getApplication(); + if (withVersion && !StringUtils.isEmpty(request.getVersion())) { + result = result + DEFAULT_SERVICE_SEPARATOR + request + .getVersion(); + } + return result; + } + + private String buildDarkLaunchDimensionsInfo(QueryConfigurationsRequest request) { + return DARK_LAUNCH + request.getApplication(); + } +} 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 new file mode 100644 index 00000000000..7bfc43b4e05 --- /dev/null +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterManager.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.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.http.client.task.AbstractTask; +import org.apache.servicecomb.http.client.task.Task; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.EventBus; + +public class ConfigCenterManager extends AbstractTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCenterManager.class); + + private final ConfigCenterClient configCenterClient; + + private final EventBus eventBus; + + private QueryConfigurationsRequest queryConfigurationsRequest; + + private final ConfigConverter configConverter; + + private final 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) { + this.queryConfigurationsRequest = queryConfigurationsRequest; + } + + public void startConfigCenterManager() { + this.startTask(new PollConfigurationTask(0)); + schedulerCheckAddressAvailable("cc-addr-check", new CheckConfigCenterAddressTask(), + configCenterConfiguration.getRefreshIntervalInMillis()); + } + + class PollConfigurationTask implements Task { + int failCount; + + public PollConfigurationTask(int failCount) { + this.failCount = failCount; + } + + @Override + public void execute() { + try { + QueryConfigurationsResponse response = configCenterClient.queryConfigurations(queryConfigurationsRequest, + configCenterAddressManager.address()); + if (response.isChanged()) { + queryConfigurationsRequest.setRevision(response.getRevision()); + Map lastData = configConverter.updateData(response.getConfigurations()); + ConfigCenterConfigurationChangedEvent event = ConfigCenterConfigurationChangedEvent + .createIncremental(configConverter.getCurrentData(), lastData); + if (!event.getChanged().isEmpty()) { + eventBus.post(event); + } + } + startTask( + new BackOffSleepTask(configCenterConfiguration.getRefreshIntervalInMillis(), new PollConfigurationTask(0))); + } catch (Exception 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 new file mode 100644 index 00000000000..919e9f30cd7 --- /dev/null +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterOperation.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.config.center.client; + +import org.apache.servicecomb.config.common.exception.OperationException; +import org.apache.servicecomb.config.center.client.model.QueryConfigurationsRequest; +import org.apache.servicecomb.config.center.client.model.QueryConfigurationsResponse; + +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, String address); + + /** + * Check config center isolation address available + * + * @param address isolation address + */ + void checkAddressAvailable(String address); +} diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/model/ConfigCenterConfiguration.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/model/ConfigCenterConfiguration.java new file mode 100644 index 00000000000..0556d14fced --- /dev/null +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/model/ConfigCenterConfiguration.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.config.center.client.model; + +public class ConfigCenterConfiguration { + + private long refreshIntervalInMillis = 15000; + + public long getRefreshIntervalInMillis() { + return refreshIntervalInMillis; + } + + public ConfigCenterConfiguration setRefreshIntervalInMillis(long refreshIntervalInMillis) { + this.refreshIntervalInMillis = refreshIntervalInMillis; + return this; + } +} diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/model/QueryConfigurationsRequest.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/model/QueryConfigurationsRequest.java new file mode 100644 index 00000000000..81c5693fbf7 --- /dev/null +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/model/QueryConfigurationsRequest.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.config.center.client.model; + +public class QueryConfigurationsRequest { + private String environment; + + private String application; + + private String serviceName; + + private String version; + + private String revision; + + public String getEnvironment() { + return environment; + } + + public QueryConfigurationsRequest setEnvironment(String environment) { + this.environment = environment; + return this; + } + + public String getApplication() { + return application; + } + + public QueryConfigurationsRequest setApplication(String application) { + this.application = application; + return this; + } + + public String getServiceName() { + return serviceName; + } + + public QueryConfigurationsRequest setServiceName(String serviceName) { + this.serviceName = serviceName; + return this; + } + + public String getVersion() { + return version; + } + + public QueryConfigurationsRequest setVersion(String version) { + this.version = version; + return this; + } + + public String getRevision() { + return revision; + } + + public QueryConfigurationsRequest setRevision(String revision) { + this.revision = revision; + return this; + } +} diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/model/QueryConfigurationsResponse.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/model/QueryConfigurationsResponse.java new file mode 100644 index 00000000000..18b8ac65f8d --- /dev/null +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/model/QueryConfigurationsResponse.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.center.client.model; + +import java.util.Map; + +public class QueryConfigurationsResponse { + private String revision; + + private boolean changed; + + private Map configurations; + + public String getRevision() { + return revision; + } + + public QueryConfigurationsResponse setRevision(String revision) { + this.revision = revision; + return this; + } + + public boolean isChanged() { + return changed; + } + + public QueryConfigurationsResponse setChanged(boolean changed) { + this.changed = changed; + return this; + } + + public Map getConfigurations() { + return configurations; + } + + public QueryConfigurationsResponse setConfigurations( + Map configurations) { + this.configurations = configurations; + return this; + } +} diff --git a/clients/config-common/pom.xml b/clients/config-common/pom.xml new file mode 100644 index 00000000000..ff10b993583 --- /dev/null +++ b/clients/config-common/pom.xml @@ -0,0 +1,44 @@ + + + + + + clients + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + config-clients-common + ServiceComb::Clients::Config Common + + + + org.apache.servicecomb + http-client-common + + + org.springframework + spring-beans + + + + + diff --git a/clients/config-common/src/main/java/org/apache/servicecomb/config/common/ConfigConverter.java b/clients/config-common/src/main/java/org/apache/servicecomb/config/common/ConfigConverter.java new file mode 100644 index 00000000000..0d8973d8aeb --- /dev/null +++ b/clients/config-common/src/main/java/org/apache/servicecomb/config/common/ConfigConverter.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.util.CollectionUtils; + +public class ConfigConverter { + private Map currentData = Collections.emptyMap(); + + private Map lastRawData; + + private final List fileSources; + + public ConfigConverter(List fileSources) { + this.fileSources = fileSources; + } + + public Map getLastRawData() { + return this.lastRawData; + } + + public Map getCurrentData() { + return this.currentData; + } + + public Map updateData(Map rawData) { + Map lastData = this.currentData; + + this.lastRawData = rawData; + + if (CollectionUtils.isEmpty(fileSources)) { + this.currentData = rawData; + return lastData; + } + + Map fileProperties = new HashMap<>(); + fileSources.forEach(source -> { + if (rawData.get(source) != null) { + fileProperties.put(source, rawData.get(source)); + } + }); + + Map result = new HashMap<>(rawData.size()); + result.putAll(rawData); + fileProperties.forEach((k, v) -> result.putAll(createFileSource(v))); + this.currentData = result; + return lastData; + } + + private Map createFileSource(Object v) { + YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); + yamlFactory.setResources(new ByteArrayResource(v.toString().getBytes(StandardCharsets.UTF_8))); + return propertiesToMap(yamlFactory.getObject()); + } + + @SuppressWarnings("unchecked") + private Map propertiesToMap(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/clients/config-common/src/main/java/org/apache/servicecomb/config/common/exception/OperationException.java b/clients/config-common/src/main/java/org/apache/servicecomb/config/common/exception/OperationException.java new file mode 100644 index 00000000000..f4e26b50505 --- /dev/null +++ b/clients/config-common/src/main/java/org/apache/servicecomb/config/common/exception/OperationException.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.config.common.exception; + +public class OperationException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public OperationException(String message) { + super(message); + } + + public OperationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/clients/config-kie-client/pom.xml b/clients/config-kie-client/pom.xml new file mode 100644 index 00000000000..30dac44ba53 --- /dev/null +++ b/clients/config-kie-client/pom.xml @@ -0,0 +1,54 @@ + + + + + + clients + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + config-kie-client + ServiceComb::Clients::Kie Client + + + + org.apache.servicecomb + http-client-common + + + org.apache.servicecomb + config-clients-common + + + org.springframework + spring-beans + + + + com.google.guava + failureaccess + test + + + + + 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 new file mode 100644 index 00000000000..b98a7d3b2a1 --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java @@ -0,0 +1,252 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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; + +import org.apache.http.HttpStatus; + +import org.apache.servicecomb.config.common.exception.OperationException; +import org.apache.servicecomb.config.kie.client.model.ConfigConstants; +import org.apache.servicecomb.config.kie.client.model.ConfigurationsRequest; +import org.apache.servicecomb.config.kie.client.model.ConfigurationsResponse; +import org.apache.servicecomb.config.kie.client.model.KVDoc; +import org.apache.servicecomb.config.kie.client.model.KVResponse; +import org.apache.servicecomb.config.kie.client.model.KieAddressManager; +import org.apache.servicecomb.config.kie.client.model.KieConfiguration; +import org.apache.servicecomb.config.kie.client.model.ValueType; +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.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"; + + private final KieAddressManager addressManager; + + private final KieConfiguration kieConfiguration; + + 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) { + String url = buildUrl(request, address); + try { + if (kieConfiguration.isEnableLongPolling()) { + url += "&wait=" + kieConfiguration.getPollingWaitInSeconds() + "s"; + } + + 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); + return configurationsResponse; + } + if (httpResponse.getStatusCode() == HttpStatus.SC_BAD_REQUEST) { + throw new OperationException("Bad request for query configurations."); + } + if (httpResponse.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { + configurationsResponse.setChanged(false); + return configurationsResponse; + } + 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); + sb.append("/"); + sb.append(DEFAULT_KIE_API_VERSION); + sb.append("/"); + sb.append(kieConfiguration.getProject()); + sb.append("/kie/kv?"); + sb.append(request.getLabelsQuery()); + sb.append("&revision="); + sb.append(request.getRevision()); + if (request.isWithExact()) { + sb.append("&match=exact"); + } + return sb.toString(); + } + + 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()) + .forEach(resultMap::putAll); + return resultMap; + } + + private Map processValueType(KVDoc kvDoc) { + ValueType valueType; + try { + valueType = ValueType.valueOf(kvDoc.getValueType()); + } catch (IllegalArgumentException e) { + throw new OperationException("value type not support [" + kvDoc.getValue() + "]"); + } + Properties properties = new Properties(); + Map kvMap = new HashMap<>(); + try { + switch (valueType) { + case yml: + case yaml: + YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); + yamlFactory.setResources(new ByteArrayResource(kvDoc.getValue().getBytes(StandardCharsets.UTF_8))); + return toMap(yamlFactory.getObject()); + case properties: + properties.load(new StringReader(kvDoc.getValue())); + return toMap(properties); + case text: + case string: + default: + kvMap.put(kvDoc.getKey(), kvDoc.getValue()); + return kvMap; + } + } catch (Exception e) { + LOGGER.error("read config failed", e); + } + return Collections.emptyMap(); + } + + @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/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 new file mode 100644 index 00000000000..ed251613530 --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigManager.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.List; +import java.util.Map; +import java.util.concurrent.Executors; + +import org.apache.servicecomb.config.common.ConfigConverter; +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; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.EventBus; + +public class KieConfigManager extends AbstractTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(KieConfigManager.class); + + private static final long LONG_POLLING_INTERVAL = 1000; + + private final KieConfigOperation configKieClient; + + private final EventBus eventBus; + + private final ConfigConverter configConverter; + + private final List configurationsRequests; + + private final KieConfiguration kieConfiguration; + + private final KieAddressManager kieAddressManager; + + public KieConfigManager(KieConfigOperation configKieClient, EventBus eventBus, + KieConfiguration kieConfiguration, ConfigConverter configConverter, KieAddressManager kieAddressManager) { + super("config-center-configuration-task"); + this.configurationsRequests = ConfigurationsRequestFactory.buildConfigurationRequests(kieConfiguration); + this.configurationsRequests.sort(ConfigurationsRequest::compareTo); + this.configKieClient = configKieClient; + this.eventBus = eventBus; + this.configConverter = configConverter; + this.kieConfiguration = kieConfiguration; + this.kieAddressManager = kieAddressManager; + } + + public void firstPull() { + Map data = new HashMap<>(); + try { + firstQueryConfigurations(data); + } catch (Exception e) { + if (this.kieConfiguration.isFirstPullRequired()) { + throw e; + } else { + 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++; + } + } + + private void onDataChanged() { + Map latestData = new HashMap<>(); + this.configurationsRequests.forEach(r -> latestData.putAll(r.getLastRawData())); + + Map lastData = configConverter.updateData(latestData); + KieConfigurationChangedEvent event = KieConfigurationChangedEvent + .createIncremental(configConverter.getCurrentData(), lastData); + if (!event.getChanged().isEmpty()) { + eventBus.post(event); + } + } + + @Override + protected void initTaskPool(String taskName) { + this.taskPool = Executors.newFixedThreadPool(3, (task) -> + new Thread(task, 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 { + final int failCount; + + ConfigurationsRequest configurationsRequest; + + public PollConfigurationTask(int failCount, ConfigurationsRequest configurationsRequest) { + this.failCount = failCount; + this.configurationsRequest = configurationsRequest; + } + + @Override + public void execute() { + try { + 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))); + } else { + startTask(new BackOffSleepTask(kieConfiguration.getRefreshIntervalInMillis(), + new PollConfigurationTask(0, this.configurationsRequest))); + } + } catch (Exception 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 new file mode 100644 index 00000000000..135ad0c220a --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigOperation.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.config.kie.client; + +import org.apache.servicecomb.config.common.exception.OperationException; +import org.apache.servicecomb.config.kie.client.model.ConfigurationsRequest; +import org.apache.servicecomb.config.kie.client.model.ConfigurationsResponse; + +//此处支持配置中心扩展 +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, 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/ConfigConstants.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigConstants.java new file mode 100644 index 00000000000..81585ecdff6 --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigConstants.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.config.kie.client.model; + + +public class ConfigConstants { + + public static final String LABEL_ENV = "environment"; + + public static final String LABEL_APP = "app"; + + public static final String LABEL_SERVICE = "service"; + + public static final String LABEL_VERSION = "version"; + + public static final String STATUS_ENABLED = "enabled"; + + public static final String KEY_PROJECT = "project"; + + // ###### kie config center polling configuration############### // + public static final String KEY_ENABLELONGPOLLING = "enableLongPolling"; + + public static final String KEY_POLLINGWAITSEC = "pollingWaitInSeconds"; + +} 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 new file mode 100644 index 00000000000..b22f7ecaa23 --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequest.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.config.kie.client.model; + +import java.util.HashMap; +import java.util.Map; + +public class ConfigurationsRequest implements Comparable { + public static final String INITIAL_REVISION = "-1"; + + private int order; + + private String revision = INITIAL_REVISION; + + private boolean withExact; + + private String labelsQuery; + + private Map lastRawData = new HashMap<>(); + + public int getOrder() { + return order; + } + + public ConfigurationsRequest setOrder(int order) { + this.order = order; + return this; + } + + public String getRevision() { + return revision; + } + + public ConfigurationsRequest setRevision(String revision) { + this.revision = revision; + return this; + } + + public boolean isWithExact() { + return withExact; + } + + public ConfigurationsRequest setWithExact(boolean withExact) { + this.withExact = withExact; + return this; + } + + public String getLabelsQuery() { + return labelsQuery; + } + + public ConfigurationsRequest setLabelsQuery(String labelsQuery) { + this.labelsQuery = labelsQuery; + return this; + } + + public Map getLastRawData() { + return lastRawData; + } + + public ConfigurationsRequest setLastRawData(Map lastRawData) { + this.lastRawData = lastRawData; + return this; + } + + @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 new file mode 100644 index 00000000000..20c6ba3bacb --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequestFactory.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.model; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.http.client.common.HttpUtils; + +public class ConfigurationsRequestFactory { + private static final String KEY_APP = "app"; + + private static final String KEY_ENVIRONMENT = "environment"; + + private static final String KEY_SERVICE = "service"; + + private static final String KEY_VERSION = "version"; + + private static final int CUSTOM_ORDER = 100; + + private static final int VERSION_ORDER = 200; + + private static final int SERVICE_ORDER = 300; + + private static final int APP_ORDER = 400; + + public static List buildConfigurationRequests(KieConfiguration configuration) { + List result = new ArrayList<>(); + if (configuration.isEnableAppConfig()) { + result.add(createAppConfigurationsRequest(configuration)); + } + if (configuration.isEnableServiceConfig()) { + result.add(createServiceConfigurationsRequest(configuration)); + } + if (configuration.isEnableVersionConfig()) { + result.add(createVersionConfigurationsRequest(configuration)); + } + if (configuration.isEnableCustomConfig()) { + result.add(createCustomConfigurationsRequest(configuration)); + } + return result; + } + + private static ConfigurationsRequest createAppConfigurationsRequest(KieConfiguration configuration) { + return new ConfigurationsRequest() + .setOrder(APP_ORDER) + .setWithExact(true) + .setLabelsQuery(buildLabelQuery(buildLabelQueryItem(KEY_APP, configuration.getAppName()), + buildLabelQueryItem(KEY_ENVIRONMENT, configuration.getEnvironment()))); + } + + private static ConfigurationsRequest createServiceConfigurationsRequest(KieConfiguration configuration) { + return new ConfigurationsRequest() + .setOrder(SERVICE_ORDER) + .setWithExact(true) + .setLabelsQuery(buildLabelQuery(buildLabelQueryItem(KEY_APP, configuration.getAppName()), + buildLabelQueryItem(KEY_SERVICE, configuration.getServiceName()), + buildLabelQueryItem(KEY_ENVIRONMENT, configuration.getEnvironment()))); + } + + private static ConfigurationsRequest createVersionConfigurationsRequest(KieConfiguration configuration) { + return new ConfigurationsRequest() + .setOrder(VERSION_ORDER) + .setWithExact(true) + .setLabelsQuery(buildLabelQuery(buildLabelQueryItem(KEY_APP, configuration.getAppName()), + buildLabelQueryItem(KEY_SERVICE, configuration.getServiceName()), + buildLabelQueryItem(KEY_ENVIRONMENT, configuration.getEnvironment()), + buildLabelQueryItem(KEY_VERSION, configuration.getVersion()))); + } + + private static ConfigurationsRequest createCustomConfigurationsRequest(KieConfiguration configuration) { + return new ConfigurationsRequest() + .setOrder(CUSTOM_ORDER) + .setWithExact(false) + .setLabelsQuery( + buildLabelQuery(buildLabelQueryItem(configuration.getCustomLabel(), configuration.getCustomLabelValue()))); + } + + private static String buildLabelQuery(String... labels) { + StringBuilder result = new StringBuilder(); + for (String label : labels) { + result.append(label); + result.append("&"); + } + return result.toString(); + } + + private static String buildLabelQueryItem(String key, String value) { + try { + return "label=" + HttpUtils.encodeURLParam(key + ":" + value); + } catch (IOException e) { + throw new IllegalArgumentException("unexpected param", e); + } + } +} diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsResponse.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsResponse.java new file mode 100644 index 00000000000..157f2e1f419 --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsResponse.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.kie.client.model; + +import java.util.Map; + +public class ConfigurationsResponse { + private String revision; + + private boolean changed; + + private Map configurations; + + public String getRevision() { + return revision; + } + + public ConfigurationsResponse setRevision(String revision) { + this.revision = revision; + return this; + } + + public boolean isChanged() { + return changed; + } + + public ConfigurationsResponse setChanged(boolean changed) { + this.changed = changed; + return this; + } + + public Map getConfigurations() { + return configurations; + } + + public ConfigurationsResponse setConfigurations( + Map configurations) { + this.configurations = configurations; + return this; + } +} 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 new file mode 100644 index 00000000000..3059a581c61 --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KVDoc.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.model; + +import com.fasterxml.jackson.annotation.JsonAlias; + +import java.util.HashMap; +import java.util.Map; + +public class KVDoc { + + private String id; + + private String check; + + private String domain; + + private String key; + + @JsonAlias("label_id") + private String labelId; + + private Map labels = new HashMap<>(); + + private String value; + + @JsonAlias("value_type") + private String valueType; + + private String status; + + @JsonAlias("update_time") + private long updateTime; + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getCheck() { + return check; + } + + public String getDomain() { + return domain; + } + + public String getLabelId() { + return labelId; + } + + public Map getLabels() { + return labels; + } + + public String getValue() { + return value; + } + + public void setCheck(String check) { + this.check = check; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public void setLabelId(String labelId) { + this.labelId = labelId; + } + + public void setLabels(Map labels) { + this.labels = labels; + } + + public void setValueType(String valueType) { + this.valueType = valueType; + } + + public void setValue(String value) { + this.value = 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/KVResponse.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KVResponse.java new file mode 100644 index 00000000000..e728acbe660 --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KVResponse.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.config.kie.client.model; + +import java.util.List; + +public class KVResponse { + + private List data; + + private LabelDocResponse label; + + private Integer total; + + public Integer getTotal() { + return total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public List getData() { + return data; + } + + public LabelDocResponse getLabel() { + return label; + } + + public void setData(List data) { + this.data = data; + } + + public void setLabel(LabelDocResponse label) { + this.label = label; + } +} 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 new file mode 100644 index 00000000000..d8c069ee9b1 --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.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.config.kie.client.model; + +import java.util.List; + +import org.apache.servicecomb.http.client.common.AbstractAddressManager; +import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; + +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + +public class KieAddressManager extends AbstractAddressManager { + + public KieAddressManager(List addresses, EventBus eventBus, String region, String availableZone) { + super(addresses, region, availableZone); + eventBus.register(this); + } + + @Subscribe + public void onRefreshEndpointEvent(RefreshEndpointEvent event) { + refreshEndpoint(event, RefreshEndpointEvent.KIE_NAME); + } +} diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieConfiguration.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieConfiguration.java new file mode 100644 index 00000000000..9c75f2d09c2 --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieConfiguration.java @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.model; + +public class KieConfiguration { + private boolean enableLongPolling; + + private int pollingWaitInSeconds; + + private int refreshIntervalInMillis = 15000; + + private String project; + + private String appName; + + private String serviceName; + + private String environment; + + private String version; + + private boolean enableAppConfig; + + private boolean enableServiceConfig; + + private boolean enableVersionConfig; + + private boolean enableCustomConfig; + + private String customLabelValue; + + private String customLabel; + + private boolean firstPullRequired; + + public String getAppName() { + return appName; + } + + public KieConfiguration setAppName(String appName) { + this.appName = appName; + return this; + } + + public String getServiceName() { + return serviceName; + } + + public KieConfiguration setServiceName(String serviceName) { + this.serviceName = serviceName; + return this; + } + + public String getEnvironment() { + return environment; + } + + public KieConfiguration setEnvironment(String environment) { + this.environment = environment; + return this; + } + + public String getCustomLabelValue() { + return customLabelValue; + } + + public KieConfiguration setCustomLabelValue(String customLabelValue) { + this.customLabelValue = customLabelValue; + return this; + } + + public boolean isEnableAppConfig() { + return enableAppConfig; + } + + public KieConfiguration setEnableAppConfig(boolean enableAppConfig) { + this.enableAppConfig = enableAppConfig; + return this; + } + + public boolean isEnableServiceConfig() { + return enableServiceConfig; + } + + public KieConfiguration setEnableServiceConfig(boolean enableServiceConfig) { + this.enableServiceConfig = enableServiceConfig; + return this; + } + + public boolean isEnableCustomConfig() { + return enableCustomConfig; + } + + public KieConfiguration setEnableCustomConfig(boolean enableCustomConfig) { + this.enableCustomConfig = enableCustomConfig; + return this; + } + + public String getCustomLabel() { + return customLabel; + } + + public KieConfiguration setCustomLabel(String customLabel) { + this.customLabel = customLabel; + return this; + } + + public boolean isEnableLongPolling() { + return enableLongPolling; + } + + public KieConfiguration setEnableLongPolling(boolean enableLongPolling) { + this.enableLongPolling = enableLongPolling; + return this; + } + + public boolean isEnableVersionConfig() { + return enableVersionConfig; + } + + public KieConfiguration setEnableVersionConfig(boolean enableVersionConfig) { + this.enableVersionConfig = enableVersionConfig; + return this; + } + + public int getPollingWaitInSeconds() { + return pollingWaitInSeconds; + } + + public KieConfiguration setPollingWaitInSeconds(int pollingWaitInSeconds) { + this.pollingWaitInSeconds = pollingWaitInSeconds; + return this; + } + + public String getProject() { + return project; + } + + public KieConfiguration setProject(String project) { + this.project = project; + return this; + } + + public boolean isFirstPullRequired() { + return firstPullRequired; + } + + public KieConfiguration setFirstPullRequired(boolean firstPullRequired) { + this.firstPullRequired = firstPullRequired; + return this; + } + + public int getRefreshIntervalInMillis() { + return refreshIntervalInMillis; + } + + public KieConfiguration setRefreshIntervalInMillis(int refreshIntervallnMillis) { + this.refreshIntervalInMillis = refreshIntervallnMillis; + return this; + } + + public String getVersion() { + return version; + } + + public KieConfiguration setVersion(String version) { + this.version = version; + return this; + } +} diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/LabelDocResponse.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/LabelDocResponse.java new file mode 100644 index 00000000000..2b9e055de0f --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/LabelDocResponse.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.config.kie.client.model; + +import com.fasterxml.jackson.annotation.JsonAlias; + +import java.util.HashMap; +import java.util.Map; + +public class LabelDocResponse { + + @JsonAlias("label_id") + private String labelId; + + private Map labels = new HashMap<>(); + + public String getLabelId() { + return labelId; + } + + public Map getLabels() { + return labels; + } + + public void setLabelId(String labelId) { + this.labelId = labelId; + } + + public void setLabels(Map labels) { + this.labels = labels; + } +} 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 new file mode 100644 index 00000000000..23cbce3b367 --- /dev/null +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ValueType.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.config.kie.client.model; + +public enum ValueType { + yml, + yaml, + ini, + string, + text, + json, + properties, + xml +} diff --git a/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequestTest.java b/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequestTest.java new file mode 100644 index 00000000000..47c152fa1df --- /dev/null +++ b/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/ConfigurationsRequestTest.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.config.kie.client.model; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +class ConfigurationsRequestTest { + + @Test + void getLastRawData() { + ConfigurationsRequest configurationsRequest = new ConfigurationsRequest(); + Map lastRawData = configurationsRequest.getLastRawData(); + Assertions.assertEquals(lastRawData.size(), 0); + } +} 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 new file mode 100644 index 00000000000..c8bf1f57a29 --- /dev/null +++ b/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/KieAddressManagerTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.model; + +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.http.client.event.RefreshEndpointEvent; + +import com.google.common.eventbus.EventBus; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class KieAddressManagerTest { + + private static final List addresses = new ArrayList<>(); + + private static KieAddressManager addressManager1; + + @Test + 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(), "", ""); + Field addressManagerField = addressManager1.getClass().getSuperclass().getDeclaredField("index"); + addressManagerField.setAccessible(true); + addressManagerField.set(addressManager1, 0); + + Assertions.assertNotNull(addressManager1); + + List addresses = addressManager1.getAddresses(); + Assertions.assertEquals(2, addresses.size()); + Assertions.assertEquals("http://127.0.0.1:30103", addresses.get(0)); + + Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); + Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); + } + + @Test + public void onRefreshEndpointEvent() { + List addressAZ = new ArrayList<>(); + addressAZ.add("http://127.0.0.3:30100"); + List addressRG = new ArrayList<>(); + addressRG.add("http://127.0.0.4:30100"); + Map> zoneAndRegion = new HashMap<>(); + zoneAndRegion.put("sameZone", addressAZ); + zoneAndRegion.put("sameRegion", addressRG); + addressManager1 = new KieAddressManager(addresses, new EventBus(), "", ""); + RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "KIE"); + addressManager1.refreshEndpoint(event, "KIE"); + + List availableZone = addressManager1.getAvailableZone(); + Assertions.assertEquals("http://127.0.0.3:30100", availableZone.get(0)); + + List availableRegion = addressManager1.getAvailableRegion(); + Assertions.assertEquals("http://127.0.0.4:30100", availableRegion.get(0)); + } +} diff --git a/clients/dashboard-client/pom.xml b/clients/dashboard-client/pom.xml new file mode 100644 index 00000000000..fb40407b82a --- /dev/null +++ b/clients/dashboard-client/pom.xml @@ -0,0 +1,50 @@ + + + + + + clients + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + dashboard-client + ServiceComb::Clients::Dashboard Client + + + + org.apache.servicecomb + http-client-common + + + org.springframework + spring-beans + + + + com.google.guava + failureaccess + test + + + + + 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 new file mode 100644 index 00000000000..6897e02acd4 --- /dev/null +++ b/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardAddressManager.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.dashboard.client; + + +import java.util.List; + +import org.apache.servicecomb.http.client.common.AbstractAddressManager; +import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; + +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + + +public class DashboardAddressManager extends AbstractAddressManager { + + public DashboardAddressManager(List addresses, EventBus eventBus, String region, String availableZone) { + super(addresses, region, availableZone); + eventBus.register(this); + } + + @Subscribe + public void onRefreshEndpointEvent(RefreshEndpointEvent event) { + refreshEndpoint(event, RefreshEndpointEvent.CSE_MONITORING_NAME); + } +} diff --git a/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardClient.java b/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardClient.java new file mode 100644 index 00000000000..2fe84429cb7 --- /dev/null +++ b/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardClient.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.dashboard.client; + +import org.apache.http.HttpStatus; +import org.apache.servicecomb.dashboard.client.model.MonitorData; +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.common.HttpUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DashboardClient implements DashboardOperation { + private static final Logger LOGGER = LoggerFactory.getLogger(DashboardClient.class); + + protected HttpTransport httpTransport; + + private final DashboardAddressManager addressManager; + + public DashboardClient(DashboardAddressManager addressManager, HttpTransport httpTransport) { + this.httpTransport = httpTransport; + this.addressManager = addressManager; + } + + @Override + public void sendData(String url, MonitorData data) { + String address = addressManager.address(); + try { + HttpRequest httpRequest = new HttpRequest(address + url, null, HttpUtils.serialize(data), HttpRequest.POST); + HttpResponse httpResponse = httpTransport.doRequest(httpRequest); + if (httpResponse.getStatusCode() != HttpStatus.SC_OK) { + LOGGER.error("send data to [{}] failed, status code [{}], message [{}]", url, httpResponse.getStatusCode() + , httpResponse.getContent()); + } + } catch (Exception e) { + LOGGER.error("send data to [{}] failed", url, e); + } + } +} diff --git a/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardOperation.java b/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardOperation.java new file mode 100644 index 00000000000..733ebfc4676 --- /dev/null +++ b/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardOperation.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.dashboard.client; + +import org.apache.servicecomb.dashboard.client.model.MonitorData; + +public interface DashboardOperation { + void sendData(String url, MonitorData monitorData); +} diff --git a/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/model/InterfaceInfo.java b/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/model/InterfaceInfo.java new file mode 100644 index 00000000000..1a72c8fe747 --- /dev/null +++ b/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/model/InterfaceInfo.java @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.dashboard.client.model; + +public class InterfaceInfo { + private String name; + + private String desc; + + private double qps; + + private int latency; + + private int l995; + + private int l99; + + private int l90; + + private int l75; + + private int l50; + + private int l25; + + private int l5; + + private double rate; + + private double failureRate; + + private long total; + + private boolean isCircuitBreakerOpen; + + private long failure; + + private long shortCircuited; + + private long semaphoreRejected; + + private long threadPoolRejected; + + private long countTimeout; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public double getQps() { + return qps; + } + + public void setQps(double qps) { + this.qps = qps; + } + + public int getLatency() { + return latency; + } + + public void setLatency(int latency) { + this.latency = latency; + } + + public int getL995() { + return l995; + } + + public void setL995(int l995) { + this.l995 = l995; + } + + public int getL99() { + return l99; + } + + public void setL99(int l99) { + this.l99 = l99; + } + + public int getL90() { + return l90; + } + + public void setL90(int l90) { + this.l90 = l90; + } + + public int getL75() { + return l75; + } + + public void setL75(int l75) { + this.l75 = l75; + } + + public int getL50() { + return l50; + } + + public void setL50(int l50) { + this.l50 = l50; + } + + public int getL25() { + return l25; + } + + public void setL25(int l25) { + this.l25 = l25; + } + + public int getL5() { + return l5; + } + + public void setL5(int l5) { + this.l5 = l5; + } + + public double getRate() { + return rate; + } + + public void setRate(double rate) { + this.rate = rate; + } + + public double getFailureRate() { + return failureRate; + } + + public void setFailureRate(double failureRate) { + this.failureRate = failureRate; + } + + public long getTotal() { + return total; + } + + public void setTotal(long total) { + this.total = total; + } + + public boolean isCircuitBreakerOpen() { + return isCircuitBreakerOpen; + } + + public void setCircuitBreakerOpen(boolean circuitBreakerOpen) { + isCircuitBreakerOpen = circuitBreakerOpen; + } + + public long getFailure() { + return failure; + } + + public void setFailure(long failure) { + this.failure = failure; + } + + public long getShortCircuited() { + return shortCircuited; + } + + public void setShortCircuited(long shortCircuited) { + this.shortCircuited = shortCircuited; + } + + public long getSemaphoreRejected() { + return semaphoreRejected; + } + + public void setSemaphoreRejected(long semaphoreRejected) { + this.semaphoreRejected = semaphoreRejected; + } + + public long getThreadPoolRejected() { + return threadPoolRejected; + } + + public void setThreadPoolRejected(long threadPoolRejected) { + this.threadPoolRejected = threadPoolRejected; + } + + public long getCountTimeout() { + return countTimeout; + } + + public void setCountTimeout(long countTimeout) { + this.countTimeout = countTimeout; + } +} diff --git a/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/model/MonitorData.java b/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/model/MonitorData.java new file mode 100644 index 00000000000..d02facd5daa --- /dev/null +++ b/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/model/MonitorData.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.dashboard.client.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class MonitorData { + public static final double PERCENTAGE_995 = 99.5; + + public static final double PERCENTAGE_99 = 99; + + public static final double PERCENTAGE_90 = 90; + + public static final double PERCENTAGE_75 = 75; + + public static final double PERCENTAGE_50 = 50; + + public static final double PERCENTAGE_25 = 25; + + public static final double PERCENTAGE_5 = 5; + + public static final int SCALE_VAL = 1; + + public static final double DEFAULT_SUCCESS_RATE = 1.0d; + + public static final int CONVERSION = 1000; + + private String appId; + + private String version; + + private String name; + + private String serviceId; + + private String environment; + + private String instance; + + private String instanceId; + + private int thread; + + private double cpu; + + private double loadAverage; + + private long uptime; + + private Map memory; + + private List interfaces = new ArrayList<>(); + + private Map customs; + + public void addInterfaceInfo(InterfaceInfo interfaceInfo) { + interfaces.add(interfaceInfo); + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public String getInstance() { + return instance; + } + + public void setInstance(String instance) { + this.instance = instance; + } + + public String getInstanceId() { + return instanceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public int getThreadCount() { + return thread; + } + + public void setThreadCount(int threadCount) { + this.thread = threadCount; + } + + public double getCpu() { + return cpu; + } + + public void setCpu(double cpu) { + this.cpu = cpu; + } + + public double getLoadAverage() { + return loadAverage; + } + + public void setLoadAverage(double loadAverage) { + this.loadAverage = loadAverage; + } + + public long getUptime() { + return uptime; + } + + public void setUptime(long uptime) { + this.uptime = uptime; + } + + public Map getMemory() { + return memory; + } + + public void setMemory(Map memory) { + this.memory = memory; + } + + public List getInterfaces() { + return interfaces; + } + + public void setInterfaces(List interfaces) { + this.interfaces = interfaces; + } + + public Map getCustoms() { + return customs; + } + + public void setCustoms(Map customs) { + this.customs = customs; + } + + public String getEnvironment() { + return environment; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } +} 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 new file mode 100644 index 00000000000..fdb7a23922b --- /dev/null +++ b/clients/dashboard-client/src/test/java/org/apache/servicecomb/dashboard/client/AddressManagerTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.dashboard.client; + +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.http.client.event.RefreshEndpointEvent; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.google.common.eventbus.EventBus; + +class AddressManagerTest { + + private static final List addresses = new ArrayList<>(); + + private static DashboardAddressManager addressManager1; + + @Test + 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(), "", ""); + Field addressManagerField = addressManager1.getClass().getSuperclass().getDeclaredField("index"); + addressManagerField.setAccessible(true); + addressManagerField.set(addressManager1, 0); + + Assertions.assertNotNull(addressManager1); + + List addresses = addressManager1.getAddresses(); + Assertions.assertEquals(2, addresses.size()); + Assertions.assertEquals("http://127.0.0.1:30103", addresses.get(0)); + + Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); + Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); + } + + @Test + public void onRefreshEndpointEvent() { + List addressAZ = new ArrayList<>(); + addressAZ.add("http://127.0.0.3:30100"); + List addressRG = new ArrayList<>(); + addressRG.add("http://127.0.0.4:30100"); + Map> zoneAndRegion = new HashMap<>(); + zoneAndRegion.put("sameZone", addressAZ); + zoneAndRegion.put("sameRegion", addressRG); + addressManager1 = new DashboardAddressManager(addresses, new EventBus(), "", ""); + RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseMonitoring"); + addressManager1.refreshEndpoint(event, "CseMonitoring"); + + List availableZone = addressManager1.getAvailableZone(); + Assertions.assertEquals("http://127.0.0.3:30100", availableZone.get(0)); + + List availableRegion = addressManager1.getAvailableRegion(); + Assertions.assertEquals("http://127.0.0.4:30100", availableRegion.get(0)); + } +} diff --git a/clients/http-client-common/pom.xml b/clients/http-client-common/pom.xml new file mode 100644 index 00000000000..51d982e470c --- /dev/null +++ b/clients/http-client-common/pom.xml @@ -0,0 +1,75 @@ + + + + + + clients + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + http-client-common + ServiceComb::Clients::HTTP Client Common + + + + + org.apache.httpcomponents + httpclient + + + + org.slf4j + slf4j-api + + + + com.fasterxml.jackson.core + jackson-databind + + + + org.apache.commons + commons-lang3 + + + + com.google.guava + guava + + + + org.apache.servicecomb + foundation-ssl + + + + org.apache.servicecomb + foundation-spi + + + + org.java-websocket + Java-WebSocket + + + diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/auth/DefaultRequestAuthHeaderProvider.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/auth/DefaultRequestAuthHeaderProvider.java new file mode 100644 index 00000000000..a701a53695b --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/auth/DefaultRequestAuthHeaderProvider.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.http.client.auth; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.auth.SignRequest; + +public class DefaultRequestAuthHeaderProvider implements RequestAuthHeaderProvider { + @Override + public Map loadAuthHeader(SignRequest signRequest) { + return new HashMap<>(0); + } +} diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/auth/RequestAuthHeaderProvider.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/auth/RequestAuthHeaderProvider.java new file mode 100644 index 00000000000..131bea7aa79 --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/auth/RequestAuthHeaderProvider.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.http.client.auth; + +import java.util.Map; + +import org.apache.servicecomb.foundation.auth.SignRequest; + +public interface RequestAuthHeaderProvider { + Map loadAuthHeader(SignRequest signRequest); +} 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 new file mode 100644 index 00000000000..3c5d6214be8 --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/AbstractAddressManager.java @@ -0,0 +1,348 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.common; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +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.eventbus.EventBus; + +public class AbstractAddressManager { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAddressManager.class); + + public static final String DEFAULT_PROJECT = "default"; + + public static final String V4_PREFIX = "/v4/"; + + private static final String V3_PREFIX = "/v3/"; + + private static final String ZONE = "availableZone"; + + private static final String REGION = "region"; + + private static final int ISOLATION_THRESHOLD = 3; + + private volatile List addresses = new ArrayList<>(); + + // when all addresses are isolation, it will use this for polling. + private final List defaultAddress = new ArrayList<>(); + + private final List defaultIsolationAddress = new ArrayList<>(); + + private int index; + + private String projectName; + + // recording continuous times of failure of an address. + private final Map addressFailureStatus = new ConcurrentHashMap<>(); + + private volatile List availableZone = new ArrayList<>(); + + private final List isolationZoneAddress = new ArrayList<>(); + + private volatile List availableRegion = new ArrayList<>(); + + private final List isolationRegionAddress = new ArrayList<>(); + + private volatile boolean addressAutoRefreshed = false; + + private final Object lock = new Object(); + + private final Random random = new Random(); + + private EventBus eventBus; + + public AbstractAddressManager(List addresses, String ownRegion, String ownAvailableZone) { + this.projectName = DEFAULT_PROJECT; + 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); + } + + 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; + parseAndInitAddresses(addresses, ownRegion, ownAvailableZone, true); + this.index = !addresses.isEmpty() ? getRandomIndex() : 0; + } + + private int getRandomIndex() { + return random.nextInt(addresses.size()); + } + + 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 + Map getAddressFailureStatus() { + return addressFailureStatus; + } + + public List getAddresses() { + return addresses; + } + + public List getAvailableZone() { + return availableZone; + } + + public List getAvailableRegion() { + return availableRegion; + } + + public String formatUrl(String url, boolean absoluteUrl, String address) { + return absoluteUrl ? address + url : formatAddress(address) + url; + } + + public boolean sslEnabled() { + return address().startsWith("https://"); + } + + protected List transformAddress(List addresses) { + return addresses.stream().map(this::formatAddress).collect(Collectors.toList()); + } + + protected String formatAddress(String address) { + try { + return getUrlPrefix(address) + HttpUtils.encodeURLParam(this.projectName); + } catch (Exception e) { + throw new IllegalStateException("not possible"); + } + } + + protected String getUrlPrefix(String address) { + return address + V3_PREFIX; + } + + public String address() { + if (!addressAutoRefreshed) { + return getDefaultAddress(); + } else { + return getAvailableZoneAddress(); + } + } + + private String getDefaultAddress() { + if (!addresses.isEmpty()) { + return getCurrentAddress(addresses); + } + 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); + } + + 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); + } + + private String getCurrentAddress(List addresses) { + synchronized (this) { + this.index++; + if (this.index >= addresses.size()) { + this.index = 0; + } + return addresses.get(index); + } + } + + private List getZoneOrRegionAddress() { + List results = new ArrayList<>(); + if (!availableZone.isEmpty()) { + results.addAll(availableZone); + } else { + results.addAll(availableRegion); + } + return results; + } + + 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); + } + } + + public void resetFailureStatus(String address) { + addressFailureStatus.put(address, 0); + } + + public void recordFailState(String address) { + synchronized (lock) { + if (!addressFailureStatus.containsKey(address)) { + addressFailureStatus.put(address, 1); + return; + } + int number = addressFailureStatus.get(address) + 1; + if (number < ISOLATION_THRESHOLD) { + addressFailureStatus.put(address, number); + } else { + removeAddress(address); + } + } + } + + //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) { + if (addresses.remove(address)) { + LOGGER.warn("isolation default address [{}]", address); + defaultIsolationAddress.add(address); + } + return; + } + 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; + } + if (availableRegion.remove(address)) { + LOGGER.warn("isolation same region address [{}]", address); + isolationRegionAddress.add(address); + } + } + + public void setEventBus(EventBus eventBus) { + this.eventBus = eventBus; + } + + public List getIsolationAddresses() { + List isolationAddresses = new ArrayList<>(defaultIsolationAddress); + isolationAddresses.addAll(isolationZoneAddress); + isolationAddresses.addAll(isolationRegionAddress); + return isolationAddresses; + } + + public String compareAndGetAddress(String host) { + for (String address : defaultAddress) { + if (isAddressHostSame(address, host)) { + return address; + } + } + return ""; + } + + private boolean isAddressHostSame(String address, String host) { + if (StringUtils.isEmpty(host)) { + return false; + } + 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); + } + return false; + } +} diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpConfiguration.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpConfiguration.java new file mode 100644 index 00000000000..59b6b4c2cc8 --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpConfiguration.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.http.client.common; + +import org.apache.servicecomb.foundation.ssl.SSLCustom; +import org.apache.servicecomb.foundation.ssl.SSLOption; + +public interface HttpConfiguration { + class SSLProperties { + private boolean enabled; + + private SSLOption sslOption; + + private SSLCustom sslCustom; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public SSLOption getSslOption() { + return sslOption; + } + + public void setSslOption(SSLOption sslOption) { + this.sslOption = sslOption; + } + + public SSLCustom getSslCustom() { + return sslCustom; + } + + public void setSslCustom(SSLCustom sslCustom) { + this.sslCustom = sslCustom; + } + } +} diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpRequest.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpRequest.java new file mode 100644 index 00000000000..35e14df06c1 --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpRequest.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.common; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.entity.StringEntity; + +public class HttpRequest { + + public static final String GET = "GET"; + + public static final String POST = "POST"; + + public static final String DELETE = "DELETE"; + + public static final String PUT = "PUT"; + + private String method; + + private final String url; + + private Map headers; + + private final String content; + + public HttpRequest(String url, Map headers, String content, String method) { + this.url = url; + this.headers = headers; + this.content = content; + this.method = method; + } + + public String getUrl() { + return url; + } + + public Map getHeaders() { + return headers; + } + + public void addHeader(String name, String value) { + if (headers == null) { + headers = new HashMap<>(); + } + headers.put(name, value); + } + + public String getContent() { + return content; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public HttpUriRequest getRealRequest() { + + HttpUriRequest httpUriRequest = null; + + switch (method) { + case GET: { + httpUriRequest = new HttpGet(url); + break; + } + case POST: { + httpUriRequest = new HttpPost(url); + if (content != null) { + ((HttpPost) httpUriRequest).setEntity(new StringEntity(content, "UTF-8")); + } + break; + } + case DELETE: { + httpUriRequest = new HttpDelete(url); + break; + } + case PUT: { + httpUriRequest = new HttpPut(url); + if (content != null) { + ((HttpPut) httpUriRequest).setEntity(new StringEntity(content, "UTF-8")); + } + break; + } + default: { + httpUriRequest = RequestBuilder.create(method).build(); + } + } + + Optional.ofNullable(httpUriRequest).ifPresent(request -> headers.forEach(request::addHeader)); + + return httpUriRequest; + } +} diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpResponse.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpResponse.java new file mode 100644 index 00000000000..c2a414290d3 --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpResponse.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.http.client.common; + +import org.apache.http.Header; + +public class HttpResponse { + + private int statusCode; + + private String message; + + private String content; + + private Header[] headers; + + public HttpResponse() { + + } + + HttpResponse(int statusCode, String message, String content, Header[] headers) { + this.statusCode = statusCode; + this.content = content; + this.message = message; + this.headers = headers; + } + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getHeader(String key) { + if (headers == null) { + return null; + } + for (Header header : headers) { + if (header.getName().equals(key)) { + return header.getValue(); + } + } + return null; + } +} diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransport.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransport.java new file mode 100644 index 00000000000..731fdbe4e7e --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransport.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.http.client.common; + +import java.io.IOException; +import java.util.Map; + +/** + * Created by on 2019/10/16. + */ +public interface HttpTransport { + + HttpResponse get(HttpRequest request) throws IOException; + + HttpResponse post(HttpRequest request) throws IOException; + + HttpResponse put(HttpRequest request) throws IOException; + + HttpResponse delete(HttpRequest request) throws IOException; + + HttpResponse doRequest(HttpRequest request) throws IOException; + + void addHeaders(Map headers); +} diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportFactory.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportFactory.java new file mode 100644 index 00000000000..b1179321b3f --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportFactory.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.http.client.common; + +import org.apache.http.client.config.RequestConfig; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.servicecomb.foundation.ssl.SSLManager; +import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; +import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; + +public class HttpTransportFactory { + // All parameters set to 5 seconds now. + public static final int CONNECT_TIMEOUT = 5000; + + public static final int CONNECTION_REQUEST_TIMEOUT = 5000; + + public static final int SOCKET_TIMEOUT = 5000; + + public static final int MAX_TOTAL = 100; + + public static final int DEFAULT_MAX_PER_ROUTE = 10; + + private HttpTransportFactory() { + } + + public static HttpTransport createHttpTransport(HttpConfiguration.SSLProperties sslProperties, + RequestAuthHeaderProvider requestAuthHeaderProvider, HttpClientBuilder httpClientBuilder) { + PoolingHttpClientConnectionManager connectionManager = getPoolingHttpClientConnectionManager(sslProperties); + httpClientBuilder.setConnectionManager(connectionManager).disableCookieManagement(); + return new HttpTransportImpl(httpClientBuilder.build(), requestAuthHeaderProvider); + } + + public static HttpTransport createHttpTransport(HttpConfiguration.SSLProperties sslProperties, + RequestAuthHeaderProvider requestAuthHeaderProvider, RequestConfig config) { + PoolingHttpClientConnectionManager connectionManager = getPoolingHttpClientConnectionManager(sslProperties); + + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). + setDefaultRequestConfig(config). + setConnectionManager(connectionManager). + disableCookieManagement(); + + return new HttpTransportImpl(httpClientBuilder.build(), requestAuthHeaderProvider); + } + + private static PoolingHttpClientConnectionManager getPoolingHttpClientConnectionManager(SSLProperties sslProperties) { + //register http/https socket factory + RegistryBuilder builder = RegistryBuilder.create(); + builder.register("http", PlainConnectionSocketFactory.INSTANCE); + if (sslProperties.isEnabled()) { + builder.register("https", + new SSLConnectionSocketFactory( + SSLManager.createSSLContext(sslProperties.getSslOption(), sslProperties.getSslCustom()), + NoopHostnameVerifier.INSTANCE)); + } + Registry connectionSocketFactoryRegistry = builder.build(); + + //connection pool management + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager( + connectionSocketFactoryRegistry); + connectionManager.setMaxTotal(MAX_TOTAL); + connectionManager.setDefaultMaxPerRoute(DEFAULT_MAX_PER_ROUTE); + return connectionManager; + } + + public static HttpTransport createHttpTransport(HttpConfiguration.SSLProperties sslProperties, + RequestAuthHeaderProvider requestAuthHeaderProvider) { + return createHttpTransport(sslProperties, requestAuthHeaderProvider, defaultRequestConfig().build()); + } + + public static RequestConfig.Builder defaultRequestConfig() { + return RequestConfig.custom() + .setConnectTimeout(CONNECT_TIMEOUT) + .setConnectionRequestTimeout( + CONNECTION_REQUEST_TIMEOUT) + .setSocketTimeout(SOCKET_TIMEOUT); + } +} 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 new file mode 100644 index 00000000000..507d8d1c556 --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportImpl.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.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"; + + private static final String HEADER_USER_AGENT = "User-Agent"; + + private HttpClient httpClient; + + private Map globalHeaders; + + private final RequestAuthHeaderProvider requestAuthHeaderProvider; + + public HttpTransportImpl(HttpClient httpClient, RequestAuthHeaderProvider requestAuthHeaderProvider) { + this.httpClient = httpClient; + this.requestAuthHeaderProvider = requestAuthHeaderProvider; + } + + public HttpClient getHttpClient() { + return httpClient; + } + + // for testing. + void setHttpClient(HttpClient httpClient) { + this.httpClient = httpClient; + } + + @Override + public HttpResponse get(HttpRequest request) throws IOException { + request.setMethod(HttpRequest.GET); + return doRequest(request); + } + + @Override + public HttpResponse post(HttpRequest request) throws IOException { + request.setMethod(HttpRequest.POST); + return doRequest(request); + } + + @Override + public HttpResponse put(HttpRequest request) throws IOException { + request.setMethod(HttpRequest.PUT); + return doRequest(request); + } + + @Override + public HttpResponse delete(HttpRequest request) throws IOException { + request.setMethod(HttpRequest.DELETE); + return doRequest(request); + } + + public HttpResponse doRequest(HttpRequest httpRequest) throws IOException { + //add header + httpRequest.addHeader(HEADER_CONTENT_TYPE, "application/json"); + httpRequest.addHeader(HEADER_USER_AGENT, "microservice-client/1.0.0"); + + if (globalHeaders != null) { + globalHeaders.forEach(httpRequest::addHeader); + } + + httpRequest.getHeaders().putAll(requestAuthHeaderProvider.loadAuthHeader(createSignRequest(httpRequest.getUrl()))); + + //get Http response + org.apache.http.HttpResponse response = httpClient.execute(httpRequest.getRealRequest()); + + return new HttpResponse(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase(), + response.getEntity() == null ? null : + EntityUtils.toString(response.getEntity(), "UTF-8"), + response.getAllHeaders()); + } + + 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 + public void addHeaders(Map headers) { + this.globalHeaders = headers; + } +} 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 new file mode 100644 index 00000000000..5f259cc3053 --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpUtils.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.common; + +import java.io.IOException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.codec.binary.Hex; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +public final class HttpUtils { + private static final String ALGORITHM_HMACSHA256 = "HmacSHA256"; + + private static final ObjectMapper MAPPER = new MessageObjectMapper(); + + public static T deserialize(String content, Class clazz) throws IOException { + return MAPPER.readValue(content, clazz); + } + + public static T deserialize(String content, TypeReference clazz) throws IOException { + return MAPPER.readValue(content, clazz); + } + + public static String serialize(Object value) throws IOException { + return MAPPER.writeValueAsString(value); + } + + public static JsonNode readTree(String content) throws IOException { + return MAPPER.readTree(content); + } + + public static String encodeURLParam(String value) throws IOException { + if (value == null) { + return ""; + } + 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 { + Mac mac = Mac.getInstance(ALGORITHM_HMACSHA256); + SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), + ALGORITHM_HMACSHA256); + mac.init(secretKey); + return Hex.encodeHexString(mac.doFinal(data.getBytes(StandardCharsets.UTF_8))); + } +} diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/MessageObjectMapper.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/MessageObjectMapper.java new file mode 100644 index 00000000000..4eb85472643 --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/MessageObjectMapper.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.http.client.common; + +import com.fasterxml.jackson.core.JsonParser.Feature; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +public class MessageObjectMapper extends ObjectMapper { + private static final long serialVersionUID = 189026839992490564L; + + public MessageObjectMapper() { + getFactory().disable(Feature.AUTO_CLOSE_SOURCE); + // Enable features that can tolerance errors and not enable those make more constraints for compatible reasons. + // Developers can use validation api to do more checks. + disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + // no view annotations shouldn't be included in JSON + this._deserializationConfig = this._deserializationConfig.without(MapperFeature.DEFAULT_VIEW_INCLUSION); + this._serializationConfig = this._serializationConfig.without(MapperFeature.DEFAULT_VIEW_INCLUSION); + disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + } +} diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/SSLSocketFactoryExt.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/SSLSocketFactoryExt.java new file mode 100644 index 00000000000..db5f18afee7 --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/SSLSocketFactoryExt.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.common; + +import javax.net.ssl.SSLSocketFactory; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +public class SSLSocketFactoryExt extends SSLSocketFactory { + private final SSLSocketFactory sslSocketFactory; + + private final String host; + + private final int port; + + public SSLSocketFactoryExt(SSLSocketFactory factory, String host, int port) { + this.sslSocketFactory = factory; + this.host = host; + this.port = port; + } + + @Override + public String[] getDefaultCipherSuites() { + return this.sslSocketFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return this.sslSocketFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket() throws IOException { + return this.sslSocketFactory.createSocket(host, port); + } + + @Override + public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { + return this.sslSocketFactory.createSocket(socket, host, port, autoClose); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + return this.sslSocketFactory.createSocket(host, port); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, + UnknownHostException { + return this.sslSocketFactory.createSocket(host, port, localHost, localPort); + } + + @Override + public Socket createSocket(InetAddress localHost, int port) throws IOException { + return this.sslSocketFactory.createSocket(localHost, port); + } + + @Override + public Socket createSocket(InetAddress localHost, int localPort, InetAddress localHost1, int localPort1) + throws IOException { + return this.sslSocketFactory.createSocket(localHost, localPort, localHost1, localPort1); + } +} 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 new file mode 100644 index 00000000000..e3e53bad06a --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/URLEndPoint.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.common; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; + +public class URLEndPoint { + private static final String SSL_ENABLED_KEY = "sslEnabled"; + + private static final String HTTP_KEY = "http://"; + + private static final String HTTPS_KEY = "https://"; + + private final boolean sslEnabled; + + private final Map> queries; + + private final String hostOrIp; + + private final int port; + + public URLEndPoint(String endpoint) { + URI uri = URI.create(endpoint); + hostOrIp = uri.getHost(); + if (uri.getPort() < 0) { + throw new IllegalArgumentException("port not specified."); + } + port = uri.getPort(); + queries = splitQuery(uri); + if (endpoint.contains(HTTPS_KEY)) { + sslEnabled = true; + } else { + sslEnabled = Boolean.parseBoolean(getFirst(SSL_ENABLED_KEY)); + } + } + + public static Map> splitQuery(URI uri) { + final Map> queryPairs = new LinkedHashMap<>(); + List pairs = URLEncodedUtils.parse(uri, StandardCharsets.UTF_8); + for (NameValuePair pair : pairs) { + List list = queryPairs.computeIfAbsent(pair.getName(), name -> new ArrayList<>()); + list.add(pair.getValue()); + } + return queryPairs; + } + + public String getFirst(String key) { + List values = queries.get(key); + if (values == null) { + return null; + } + return values.get(0); + } + + @Override + public String toString() { + if (sslEnabled) { + return HTTPS_KEY + hostOrIp + ":" + port; + } + return HTTP_KEY + hostOrIp + ":" + port; + } +} diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/WebSocketListener.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/WebSocketListener.java new file mode 100644 index 00000000000..de79c2f7ddd --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/WebSocketListener.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.http.client.common; + +import org.java_websocket.handshake.ServerHandshake; + +public interface WebSocketListener { + void onMessage(String s); + + void onError(Exception e); + + void onClose(int code, String reason, boolean remote); + + void onOpen(ServerHandshake serverHandshake); +} diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/WebSocketTransport.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/WebSocketTransport.java new file mode 100644 index 00000000000..ab630c609be --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/WebSocketTransport.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.http.client.common; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; + +import javax.net.ssl.SSLSocketFactory; + +import org.apache.servicecomb.foundation.ssl.SSLManager; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.drafts.Draft_6455; +import org.java_websocket.handshake.ServerHandshake; + +public class WebSocketTransport extends WebSocketClient { + public static final int CONNECT_TIMEOUT = 5000; + + private final WebSocketListener webSocketListener; + + public WebSocketTransport(String serverUri, HttpConfiguration.SSLProperties sslProperties, + Map headers, WebSocketListener webSocketListener) + throws URISyntaxException { + super(new URI(serverUri), new Draft_6455(), headers, CONNECT_TIMEOUT); + + if (sslProperties.isEnabled()) { + SSLSocketFactory sslSocketFactory = SSLManager + .createSSLSocketFactory(sslProperties.getSslOption(), sslProperties.getSslCustom()); + URI uri = new URI(serverUri); + setSocketFactory(new SSLSocketFactoryExt(sslSocketFactory, uri.getHost(), uri.getPort())); + } + + this.webSocketListener = webSocketListener; + } + + @Override + public void onOpen(ServerHandshake serverHandshake) { + this.webSocketListener.onOpen(serverHandshake); + } + + @Override + public void onMessage(String s) { + this.webSocketListener.onMessage(s); + } + + @Override + public void onClose(int code, String reason, boolean remote) { + this.webSocketListener.onClose(code, reason, remote); + } + + @Override + public void onError(Exception e) { + this.webSocketListener.onError(e); + } +} 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/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/OperationEvents.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/OperationEvents.java new file mode 100644 index 00000000000..69896f750ec --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/OperationEvents.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.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/event/RefreshEndpointEvent.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/RefreshEndpointEvent.java new file mode 100644 index 00000000000..4e4f1be648e --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/RefreshEndpointEvent.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RefreshEndpointEvent { + + public static final String SERVICE_CENTER_NAME = "SERVICECENTER"; + + public static final String KIE_NAME = "KIE"; + + public static final String CONFIG_CENTER_NAME = "CseConfigCenter"; + + public static final String CSE_MONITORING_NAME = "CseMonitoring"; + + private static final String SAME_ZONE = "sameZone"; + + private static final String SAME_REGION = "sameRegion"; + + private Map> zoneAndRegion = new HashMap<>(); + + private String name; + + public RefreshEndpointEvent(Map> zoneAndRegion, String name) { + this.zoneAndRegion = zoneAndRegion; + this.name = name; + } + + public List getSameZone() { + if (zoneAndRegion.get(SAME_ZONE).isEmpty()) { + return new ArrayList<>(); + } + return zoneAndRegion.get(SAME_ZONE); + } + + public List getSameRegion() { + if (zoneAndRegion.get(SAME_REGION).isEmpty()) { + return new ArrayList<>(); + } + return zoneAndRegion.get(SAME_REGION); + } + + public Map> getZoneAndRegion() { + return zoneAndRegion; + } + + public void setZoneAndRegion(Map> zoneAndRegion) { + this.zoneAndRegion = zoneAndRegion; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} 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 new file mode 100644 index 00000000000..cc10ae8e142 --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/task/AbstractTask.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.task; + +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; + +public class AbstractTask { + public class BackOffSleepTask implements Task { + private static final long BASE = 3000; + + private static final long MAX = 10 * 60 * 1000; + + long waitTime; + + Task nextTask; + + public BackOffSleepTask(int failedCount, Task nextTask) { + this.waitTime = failedCount * failedCount * BASE; + this.nextTask = nextTask; + } + + public BackOffSleepTask(long waitTime, Task nextTask) { + this.waitTime = waitTime; + this.nextTask = nextTask; + } + + @Override + public void execute() { + long time = Math.min(MAX, waitTime); + try { + Thread.sleep(time); + } catch (InterruptedException e) { + LOGGER.error("unexpected interrupt during sleep", e); + } + startTask(nextTask); + } + } + + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTask.class); + + protected ExecutorService taskPool; + + 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")); + } + + protected void initTaskPool(String taskName) { + this.taskPool = Executors.newSingleThreadExecutor((task) -> + 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) { + if (!running) { + return; + } + + try { + this.taskPool.execute(() -> { + try { + task.execute(); + } catch (Throwable e) { + LOGGER.error("unexpected error execute task {}", task.getClass().getName(), e); + } + }); + } catch (RejectedExecutionException e) { + LOGGER.error("execute task rejected {}", task.getClass().getName(), e); + } + } + + public void stop() { + try { + 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/task/Task.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/task/Task.java new file mode 100644 index 00000000000..bd009717175 --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/task/Task.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.http.client.task; + +public interface Task { + void execute(); +} 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 new file mode 100644 index 00000000000..b14d6827cb8 --- /dev/null +++ b/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/AbstractAddressManagerTest.java @@ -0,0 +1,328 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.common; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; +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 AbstractAddressManagerTest { + + private static final List addresses = new ArrayList<>(); + + private static AbstractAddressManager addressManager1; + + private static AbstractAddressManager addressManager2; + + private static AbstractAddressManager addressManager3; + + @BeforeEach + 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, "", ""); + 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 + public void tearDown() { + addresses.clear(); + addressManager1 = null; + addressManager2 = null; + addressManager3 = null; + } + + @Test + public void abstractAddressManagerTest() { + Assertions.assertNotNull(addressManager1); + Assertions.assertNotNull(addressManager2); + Assertions.assertNotNull(addressManager3); + + Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); + Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); + } + + @Test + public void recordStateTest() throws ExecutionException { + List addressAZ = new ArrayList<>(); + addressAZ.add("http://127.0.0.3:30100"); + List addressRG = new ArrayList<>(); + addressRG.add("http://127.0.0.4:30100"); + Map> zoneAndRegion = new HashMap<>(); + zoneAndRegion.put("sameZone", addressAZ); + zoneAndRegion.put("sameRegion", addressRG); + RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST"); + AbstractAddressManager addressManager = new AbstractAddressManager(addresses, "", "") {}; + + addressManager.refreshEndpoint(event, "TEST"); + + String address = "http://127.0.0.3:30100"; + addressManager.recordFailState(address); + + Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); + + addressManager.recordFailState(address); + Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); + + // test fail 2 times ,it will not be isolated + addressManager.resetFailureStatus(address); + Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); + + // test recodeStatus times + Map recodeStatus = addressManager.getAddressFailureStatus(); + Assertions.assertEquals(0, (int) recodeStatus.get("http://127.0.0.3:30100")); + + // test fail 3 times ,it will be isolated + addressManager.recordFailState(address); + addressManager.recordFailState(address); + addressManager.recordFailState(address); + Assertions.assertEquals("http://127.0.0.4:30100", addressManager.address()); + + // test restore isolation + 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()); + } + + + @Test + public void testMultipleThread() throws Exception { + + AbstractAddressManager addressManager = new AbstractAddressManager(addresses, "", ""); + String address = "http://127.0.0.3:30100"; + + CountDownLatch latch = new CountDownLatch(2); + for (int i = 0; i < 2; i++) { + new Thread(() -> { + addressManager.recordFailState(address); + latch.countDown(); + }).start(); + } + latch.await(30, TimeUnit.SECONDS); + + Map recodeStatus = addressManager.getAddressFailureStatus(); + Assertions.assertEquals(2, (int) recodeStatus.get("http://127.0.0.3:30100")); + } + + @Test + public void addressForOnlyDefaultTest() { + Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); + Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); + + Assertions.assertEquals("https://127.0.0.2:30103/v3/project", addressManager2.address()); + Assertions.assertEquals("http://127.0.0.1:30103/v3/project", addressManager2.address()); + + Assertions.assertEquals("https://127.0.0.2:30103/v3/default", addressManager3.address()); + Assertions.assertEquals("http://127.0.0.1:30103/v3/default", addressManager3.address()); + } + + @Test + public void addressForOnlyAzTest() { + List addressAZ = new ArrayList<>(); + addressAZ.add("http://127.0.0.1:30100"); + addressAZ.add("https://127.0.0.2:30100"); + addressAZ.add("rest://127.0.0.1:30100?sslEnabled=true"); + addressAZ.add("rest://127.0.0.2:30100?sslEnabled=false"); + + Map> zoneAndRegion = new HashMap<>(); + zoneAndRegion.put("sameZone", addressAZ); + zoneAndRegion.put("sameRegion", new ArrayList<>()); + RefreshEndpointEvent event1 = new RefreshEndpointEvent(zoneAndRegion, "TEST"); + addressManager1.refreshEndpoint(event1, "TEST"); + + Assertions.assertEquals("https://127.0.0.2:30100", addressManager1.address()); + Assertions.assertEquals("https://127.0.0.1:30100", addressManager1.address()); + Assertions.assertEquals("http://127.0.0.2:30100", addressManager1.address()); + Assertions.assertEquals("http://127.0.0.1:30100", addressManager1.address()); + Assertions.assertEquals("https://127.0.0.2:30100", addressManager1.address()); + } + + @Test + public void addressForOnlyRegionTest() { + List addressRG = new ArrayList<>(); + addressRG.add("rest://127.0.0.5:30100?sslEnabled=true"); + addressRG.add("rest://127.0.0.6:30100"); + addressRG.add("http://127.0.0.7:30100"); + addressRG.add("https://127.0.0.8:30100"); + Map> zoneAndRegion = new HashMap<>(); + zoneAndRegion.put("sameZone", new ArrayList<>()); + zoneAndRegion.put("sameRegion", addressRG); + RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST"); + addressManager1.refreshEndpoint(event, "TEST"); + + Assertions.assertEquals("http://127.0.0.6:30100", addressManager1.address()); + Assertions.assertEquals("http://127.0.0.7:30100", addressManager1.address()); + Assertions.assertEquals("https://127.0.0.8:30100", addressManager1.address()); + Assertions.assertEquals("https://127.0.0.5:30100", addressManager1.address()); + Assertions.assertEquals("http://127.0.0.6:30100", addressManager1.address()); + } + + @Test + public void addressForAzAndRegionTest() { + List addressAZ = new ArrayList<>(); + addressAZ.add("rest://127.0.0.1:30100?sslEnabled=true"); + addressAZ.add("https://127.0.0.2:30100"); + List addressRG = new ArrayList<>(); + addressRG.add("rest://127.0.0.3:30100?sslEnabled=true"); + addressRG.add("https://127.0.0.4:30100"); + Map> zoneAndRegion = new HashMap<>(); + zoneAndRegion.put("sameZone", addressAZ); + zoneAndRegion.put("sameRegion", addressRG); + RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST"); + addressManager1.refreshEndpoint(event, "TEST"); + + Assertions.assertEquals("https://127.0.0.2:30100", addressManager1.address()); + Assertions.assertEquals("https://127.0.0.1:30100", addressManager1.address()); + Assertions.assertEquals("https://127.0.0.2:30100", addressManager1.address()); + + addressManager1.removeAddress("https://127.0.0.2:30100"); + addressManager1.removeAddress("https://127.0.0.1:30100"); + Assertions.assertEquals("https://127.0.0.3:30100", addressManager1.address()); + Assertions.assertEquals("https://127.0.0.4:30100", addressManager1.address()); + Assertions.assertEquals("https://127.0.0.3:30100", addressManager1.address()); + + addressManager1.removeAddress("https://127.0.0.4:30100"); + addressManager1.removeAddress("https://127.0.0.3:30100"); + Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); + Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); + Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); + } + + @Test + public void sslEnabledTest() { + Assertions.assertTrue(addressManager1.sslEnabled()); + Assertions.assertFalse(addressManager1.sslEnabled()); + Assertions.assertTrue(addressManager1.sslEnabled()); + + Assertions.assertTrue(addressManager2.sslEnabled()); + Assertions.assertFalse(addressManager2.sslEnabled()); + Assertions.assertTrue(addressManager2.sslEnabled()); + } + + @Test + public void transformAddressTest() { + List address = new ArrayList<>(); + address.add("rest://127.0.0.1:30100?sslEnabled=true"); + address.add("rest://127.0.0.2:30100"); + address.add("http://127.0.0.3:30100"); + address.add("https://127.0.0.4:30100"); + + List formAddress = addressManager2.transformAddress(address); + + Assertions.assertEquals("rest://127.0.0.1:30100?sslEnabled=true/v3/project", formAddress.get(0)); + Assertions.assertEquals("rest://127.0.0.2:30100/v3/project", formAddress.get(1)); + Assertions.assertEquals("http://127.0.0.3:30100/v3/project", formAddress.get(2)); + Assertions.assertEquals("https://127.0.0.4:30100/v3/project", formAddress.get(3)); + + formAddress = addressManager3.transformAddress(address); + Assertions.assertEquals("rest://127.0.0.1:30100?sslEnabled=true/v3/default", formAddress.get(0)); + Assertions.assertEquals("rest://127.0.0.2:30100/v3/default", formAddress.get(1)); + Assertions.assertEquals("http://127.0.0.3:30100/v3/default", formAddress.get(2)); + Assertions.assertEquals("https://127.0.0.4:30100/v3/default", formAddress.get(3)); + } + + @Test + public void getUrlPrefixTest() { + Assertions.assertEquals("http://127.0.0.3:30100/v3/", addressManager2.getUrlPrefix("http://127.0.0.3:30100")); + Assertions.assertEquals("http://127.0.0.3:30100/v3/", addressManager3.getUrlPrefix("http://127.0.0.3:30100")); + } + + @Test + public void refreshEndpointTest() { + List addressAZ = new ArrayList<>(); + addressAZ.add("rest://127.0.0.1:30100"); + Map> zoneAndRegion = new HashMap<>(); + zoneAndRegion.put("sameZone", addressAZ); + zoneAndRegion.put("sameRegion", new ArrayList<>()); + RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST"); + + addressManager1.refreshEndpoint(event, "KIE"); + Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); + Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); + Assertions.assertEquals("https://127.0.0.2:30103", addressManager1.address()); + + addressManager2.refreshEndpoint(event, "TEST"); + Assertions.assertEquals("http://127.0.0.1:30100", addressManager2.address()); + Assertions.assertEquals("http://127.0.0.1:30100", addressManager2.address()); + } + + @Test + public void normalizeIPV4Test() { + String uri = addressManager1.normalizeUri("rest://127.0.0.1:30100?sslEnabled=true"); + Assertions.assertEquals("https://127.0.0.1:30100", uri); + + uri = addressManager1.normalizeUri("rest://127.0.0.1:30100?sslEnabled=false"); + Assertions.assertEquals("http://127.0.0.1:30100", uri); + + uri = addressManager1.normalizeUri("rest://127.0.0.1:30100"); + Assertions.assertEquals("http://127.0.0.1:30100", uri); + } + + @Test + public void normalizeIPV6Test() { + String uri = addressManager1.normalizeUri("rest://[2008::7:957f:b2d6:1af4:a1f8]:30100?sslEnabled=true"); + Assertions.assertEquals("https://[2008::7:957f:b2d6:1af4:a1f8]:30100", uri); + + 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/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/HttpTransportImplTest.java b/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/HttpTransportImplTest.java new file mode 100644 index 00000000000..2530b80145a --- /dev/null +++ b/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/HttpTransportImplTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.http.client.common; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.http.HttpVersion; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class HttpTransportImplTest { + + @Test + public void TestHttpTransport() throws IOException { + HttpClient httpClient = mock(HttpClient.class); + RequestAuthHeaderProvider requestAuthHeaderProvider = mock(RequestAuthHeaderProvider.class); + org.apache.http.HttpResponse httpResponse = mock(org.apache.http.HttpResponse.class); + StatusLine statusLine = mock(StatusLine.class); + when(statusLine.getStatusCode()).thenReturn(200); + when(statusLine.getProtocolVersion()).thenReturn(HttpVersion.HTTP_1_1); + when(statusLine.getReasonPhrase()).thenReturn("OK"); + + when(httpResponse.getStatusLine()).thenReturn(statusLine); + when(httpResponse.getEntity()).thenReturn(new StringEntity("Test", ContentType.APPLICATION_JSON)); + + when(httpClient.execute(Mockito.any())).thenReturn(httpResponse); + + HttpTransportImpl httpTransport = new HttpTransportImpl(httpClient, requestAuthHeaderProvider); + Map extraHeaders = new HashMap<>(); + extraHeaders.put("test", "testContext"); + httpTransport.addHeaders(extraHeaders); + + HttpRequest httpRequest = new HttpRequest("111", null, null, HttpRequest.GET); + HttpResponse actualResponse = httpTransport.get(httpRequest); + + Assertions.assertNotNull(actualResponse); + Assertions.assertEquals(200, actualResponse.getStatusCode()); + Assertions.assertEquals("OK", actualResponse.getMessage()); + Assertions.assertEquals("Test", actualResponse.getContent()); + } +} diff --git a/clients/http-client-common/src/test/resources/tls/client.p12 b/clients/http-client-common/src/test/resources/tls/client.p12 new file mode 100644 index 00000000000..acd63412397 Binary files /dev/null and b/clients/http-client-common/src/test/resources/tls/client.p12 differ diff --git a/clients/http-client-common/src/test/resources/tls/server.jks b/clients/http-client-common/src/test/resources/tls/server.jks new file mode 100644 index 00000000000..f3429ed696e Binary files /dev/null and b/clients/http-client-common/src/test/resources/tls/server.jks differ diff --git a/clients/pom.xml b/clients/pom.xml new file mode 100644 index 00000000000..49f481934cc --- /dev/null +++ b/clients/pom.xml @@ -0,0 +1,43 @@ + + + + + + + org.apache.servicecomb + java-chassis-parent + 3.4.0-SNAPSHOT + ../parents/default + + 4.0.0 + + clients + ServiceComb::Client + pom + + http-client-common + service-center-client + config-common + config-kie-client + config-center-client + dashboard-client + + + diff --git a/clients/service-center-client/README.md b/clients/service-center-client/README.md new file mode 100644 index 00000000000..a6b4c6d4936 --- /dev/null +++ b/clients/service-center-client/README.md @@ -0,0 +1,97 @@ +## ServiceComb-Service-Center Client for Java + + 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 + ``` + + org.apache.servicecomb + service-center-client + + ``` + + +### Basic Usage + +#### Case 1: Health check +```Java +ServiceCenterClient client = new ServiceCenterClient(); + +//get service-center instance +MicroserviceInstancesResponse instances = client.getServiceCenterInstances(); +``` + +#### Case 2: Register microservice +```Java +ServiceCenterClient client = new ServiceCenterClient(); + +// state Microservice object, and you have to set a serviceName +Microservice microservice = new Microservice(); +microservice.setServiceName("Test"); +microservice.setServiceId("111111"); + +//register microservice +String response = client.registerMicroservice(microservice); +``` + +#### Case 3: Get microservice list +```Java +ServiceCenterClient client = new ServiceCenterClient(); + +//get services lists +MicroservicesResponse services = client.getMicroserviceList(); +``` + +#### Case 4: Register service instance +```Java +ServiceCenterClient client = new ServiceCenterClient(); + +//state MicroserviceInstance object, you have to set existed serviceId +MicroserviceInstance instance = new MicroserviceInstance(); +instance.setServiceId("222222"); + +//register service instanceId, return instanceId +String instanceId = client.registerMicroserviceInstance(instance, "222222"); +``` + +#### Case 5: Get service instances list +```Java +ServiceCenterClient client = new ServiceCenterClient(); + +// get instances list with servcieId being "222222" +MicroserviceInstancesResponse instances = client.getMicroserviceInstanceList("222222"); +``` + +#### Case 6: Heartbeats +```Java +ServiceCenterClient client = new ServiceCenterClient(); + +//all services and all instances send heartbeats +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())); + } +} +``` + +#### 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) + + +### More development + +- Support All Service-center HTTP API + +### Contact +Bugs/Feature : [issues](https://github.com/apache/servicecomb-java-chassis/issues) diff --git a/clients/service-center-client/pom.xml b/clients/service-center-client/pom.xml new file mode 100755 index 00000000000..ccc3900e9db --- /dev/null +++ b/clients/service-center-client/pom.xml @@ -0,0 +1,46 @@ + + + + + + clients + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + service-center-client + ServiceComb::Clients::Service Center Client + + + + + org.apache.servicecomb + http-client-common + + + + com.google.guava + failureaccess + test + + + diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/CacheableServiceCenterOperation.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/CacheableServiceCenterOperation.java new file mode 100644 index 00000000000..68741333b24 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/CacheableServiceCenterOperation.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.service.center.client; + +public interface CacheableServiceCenterOperation { + +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/DiscoveryEvents.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/DiscoveryEvents.java new file mode 100644 index 00000000000..e45720f5ebb --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/DiscoveryEvents.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.service.center.client; + +import java.util.List; + +import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; + +public abstract class DiscoveryEvents { + public static class InstanceChangedEvent extends DiscoveryEvents { + private final String appName; + + private final String serviceName; + + private final List instances; + + public InstanceChangedEvent(String appName, String serviceName, List instances) { + this.appName = appName; + this.serviceName = serviceName; + this.instances = instances; + } + + public String getAppName() { + return appName; + } + + public String getServiceName() { + return serviceName; + } + + public List getInstances() { + return instances; + } + } + + /** + * internal events to ask for a immediate instance pull + */ + public static class PullInstanceEvent extends DiscoveryEvents { + + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/RegistrationEvents.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/RegistrationEvents.java new file mode 100644 index 00000000000..d411913dfed --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/RegistrationEvents.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.service.center.client; + +import org.apache.servicecomb.service.center.client.model.Microservice; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; + +public abstract class RegistrationEvents { + protected final boolean success; + + protected final Microservice microservice; + + protected RegistrationEvents(boolean success, Microservice microservice) { + this.success = success; + this.microservice = microservice; + } + + public boolean isSuccess() { + return this.success; + } + + public Microservice getMicroservice() { + return microservice; + } + + public static class MicroserviceRegistrationEvent extends RegistrationEvents { + public MicroserviceRegistrationEvent(boolean success, Microservice microservice) { + super(success, microservice); + } + } + + public static class SchemaRegistrationEvent extends RegistrationEvents { + public SchemaRegistrationEvent(boolean success, Microservice microservice) { + super(success, microservice); + } + } + + public static class MicroserviceInstanceRegistrationEvent extends RegistrationEvents { + protected final MicroserviceInstance microserviceInstance; + + public MicroserviceInstanceRegistrationEvent(boolean success, Microservice microservice, + MicroserviceInstance microserviceInstance) { + super(success, microservice); + this.microserviceInstance = microserviceInstance; + } + } + + public static class HeartBeatEvent extends RegistrationEvents { + protected final MicroserviceInstance microserviceInstance; + + public HeartBeatEvent(boolean success, Microservice microservice, + MicroserviceInstance microserviceInstance) { + super(success, microservice); + this.microserviceInstance = microserviceInstance; + } + } +} 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 new file mode 100644 index 00000000000..b19db919e81 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManager.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.service.center.client; + +import java.util.List; + +import org.apache.servicecomb.http.client.common.AbstractAddressManager; +import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; + +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + +public class ServiceCenterAddressManager extends AbstractAddressManager { + public ServiceCenterAddressManager(String projectName, List addresses, EventBus eventBus, String region, + String availableZone) { + super(projectName, addresses, region, availableZone); + eventBus.register(this); + } + + @Override + protected List transformAddress(List addresses) { + return addresses; + } + + @Override + protected String getUrlPrefix(String address) { + return address + V4_PREFIX; + } + + @Subscribe + public void onRefreshEndpointEvent(RefreshEndpointEvent event) { + refreshEndpoint(event, RefreshEndpointEvent.SERVICE_CENTER_NAME); + } +} 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 new file mode 100755 index 00000000000..97aeaa9931a --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java @@ -0,0 +1,569 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.service.center.client; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.List; +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; +import org.apache.servicecomb.http.client.common.HttpResponse; +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.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; +import org.apache.servicecomb.service.center.client.model.Microservice; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstanceResponse; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstanceStatus; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstancesResponse; +import org.apache.servicecomb.service.center.client.model.MicroserviceResponse; +import org.apache.servicecomb.service.center.client.model.MicroservicesResponse; +import org.apache.servicecomb.service.center.client.model.ModifySchemasRequest; +import org.apache.servicecomb.service.center.client.model.RbacTokenRequest; +import org.apache.servicecomb.service.center.client.model.RbacTokenResponse; +import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceInstanceResponse; +import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceResponse; +import org.apache.servicecomb.service.center.client.model.SchemaInfo; +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; + +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 final ServiceCenterAddressManager addressManager; + + public ServiceCenterClient(ServiceCenterRawClient httpClient, ServiceCenterAddressManager addressManager) { + this.httpClient = httpClient; + this.addressManager = addressManager; + } + + public ServiceCenterClient setEventBus(EventBus eventBus) { + addressManager.setEventBus(eventBus); + this.httpClient.setEventBus(eventBus); + return this; + } + + public ServiceCenterClient(ServiceCenterAddressManager addressManager, + SSLProperties sslProperties, + RequestAuthHeaderProvider requestAuthHeaderProvider, + String tenantName, + 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 + public MicroserviceInstancesResponse getServiceCenterInstances() { + try { + HttpResponse response = httpClient.getHttpRequest("/registry/health", null, null); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return HttpUtils.deserialize(response.getContent(), MicroserviceInstancesResponse.class); + } + throw new OperationException( + "get service-center instances fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "get service-center instances fails", e); + } + } + + @Override + public RegisteredMicroserviceResponse registerMicroservice(Microservice microservice) { + try { + CreateMicroserviceRequest request = new CreateMicroserviceRequest(); + request.setService(microservice); + HttpResponse response = httpClient + .postHttpRequest("/registry/microservices", null, HttpUtils.serialize(request)); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return HttpUtils.deserialize(response.getContent(), RegisteredMicroserviceResponse.class); + } + throw new OperationException( + "register service fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "register service fails", e); + } + } + + @Override + public MicroservicesResponse getMicroserviceList() { + try { + HttpResponse response = httpClient.getHttpRequest("/registry/microservices", null, null); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return HttpUtils.deserialize(response.getContent(), MicroservicesResponse.class); + } + throw new OperationException( + "get service List fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "get service List fails", e); + } + } + + @Override + public RegisteredMicroserviceResponse queryServiceId(Microservice microservice) { + try { + URIBuilder uriBuilder = new URIBuilder("/registry/existence"); + uriBuilder.setParameter("type", "microservice"); + uriBuilder.setParameter("appId", microservice.getAppId()); + uriBuilder.setParameter("serviceName", microservice.getServiceName()); + uriBuilder.setParameter("version", microservice.getVersion()); + uriBuilder.setParameter("env", microservice.getEnvironment()); + + HttpResponse response = httpClient.getHttpRequest(uriBuilder.build().toString(), null, null); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return HttpUtils.deserialize(response.getContent(), RegisteredMicroserviceResponse.class); + } + LOGGER.info("Query serviceId fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + return null; + } catch (IOException e) { + throw new OperationException( + "query serviceId fails", e); + } catch (URISyntaxException e) { + throw new OperationException( + "build url failed.", e); + } + } + + @Override + public Microservice getMicroserviceByServiceId(String serviceId) { + try { + HttpResponse response = httpClient.getHttpRequest("/registry/microservices/" + serviceId, null, null); + if (response.getStatusCode() == HttpStatus.SC_OK) { + MicroserviceResponse microserviceResponse = HttpUtils + .deserialize(response.getContent(), MicroserviceResponse.class); + return microserviceResponse.getService(); + } + throw new OperationException( + "get service message fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "get service message fails", e); + } + } + + @Override + public RegisteredMicroserviceInstanceResponse registerMicroserviceInstance(MicroserviceInstance instance) { + try { + CreateMicroserviceInstanceRequest request = new CreateMicroserviceInstanceRequest(); + request.setInstance(instance); + HttpResponse response = httpClient + .postHttpRequest("/registry/microservices/" + instance.getServiceId() + "/instances", null, + HttpUtils.serialize(request)); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return HttpUtils.deserialize(response.getContent(), RegisteredMicroserviceInstanceResponse.class); + } + throw new OperationException( + "register service instance fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "register service instance fails", e); + } + } + + @Override + public FindMicroserviceInstancesResponse findMicroserviceInstance(String consumerId, String appId, String serviceName, + String versionRule, + String revision) { + try { + Map headers = new HashMap<>(); + headers.put("X-ConsumerId", consumerId); + HttpResponse response = httpClient + .getHttpRequest("/registry/instances?appId=" + URLEncoder.encode(appId, "UTF-8") + + "&serviceName=" + HttpUtils.encodeURLParam(serviceName) + + "&version=" + HttpUtils.encodeURLParam(versionRule) + + "&rev=" + HttpUtils.encodeURLParam(revision) + , headers, null); + FindMicroserviceInstancesResponse result = new FindMicroserviceInstancesResponse(); + if (response.getStatusCode() == HttpStatus.SC_OK) { + result.setModified(true); + result.setRevision(response.getHeader("X-Resource-Revision")); + result.setMicroserviceInstancesResponse( + HttpUtils.deserialize(response.getContent(), MicroserviceInstancesResponse.class)); + return result; + } + if (response.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { + result.setModified(false); + return result; + } + 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() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "get service instances list fails", e); + } + } + + @Override + public MicroserviceInstancesResponse getMicroserviceInstanceList(String serviceId) { + try { + HttpResponse response = httpClient + .getHttpRequest("/registry/microservices/" + serviceId + "/instances", null, null); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return HttpUtils.deserialize(response.getContent(), MicroserviceInstancesResponse.class); + } + throw new OperationException( + "get service instances list fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "get service instances list fails", e); + } + } + + @Override + public MicroserviceInstance getMicroserviceInstance(String serviceId, String instanceId) { + try { + HttpResponse response = httpClient + .getHttpRequest("/registry/microservices/" + serviceId + "/instances/" + instanceId, null, null); + if (response.getStatusCode() == HttpStatus.SC_OK) { + MicroserviceInstanceResponse instanceResponse = HttpUtils + .deserialize(response.getContent(), MicroserviceInstanceResponse.class); + return instanceResponse.getInstance(); + } + throw new OperationException( + "get service instance message fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "get service instance message fails", e); + } + } + + @Override + public void deleteMicroserviceInstance(String serviceId, String instanceId) { + try { + HttpResponse response = httpClient + .deleteHttpRequest("/registry/microservices/" + serviceId + "/instances/" + instanceId, null, null); + if (response.getStatusCode() == HttpStatus.SC_OK) { + LOGGER.info("Delete service instance successfully."); + return; + } + throw new OperationException( + "delete service instance fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "delete service instance fails", e); + } + } + + @Override + public boolean updateMicroserviceInstanceStatus(String serviceId, String instanceId, + MicroserviceInstanceStatus status) { + try { + HttpResponse response = httpClient.putHttpRequest( + "/registry/microservices/" + serviceId + "/instances/" + instanceId + "/status?value=" + status, null, null); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return true; + } + throw new OperationException( + "update service instance status fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "update service instance status fails", e); + } + } + + @Override + public void sendHeartBeats(HeartbeatsRequest heartbeatsRequest) { + try { + HttpResponse response = httpClient + .putHttpRequest("/registry/heartbeats", null, HttpUtils.serialize(heartbeatsRequest)); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return; + } + throw new OperationException( + "heartbeats fails, statusCode = " + response.getStatusCode() + "; message = " + response.getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "heartbeats fails ", e); + } + } + + @Override + public boolean sendHeartBeat(String serviceId, String instanceId) { + try { + HttpResponse response = httpClient + .putHttpRequest("/registry/microservices/" + serviceId + "/instances/" + instanceId + "/heartbeat", + null, null); + + if (response.getStatusCode() == HttpStatus.SC_OK) { + return true; + } + throw new OperationException( + "heartbeats fails, statusCode = " + response.getStatusCode() + "; message = " + response.getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "heartbeats fails ", e); + } + } + + @Override + public List getServiceSchemasList(String serviceId, boolean withContent) { + String url = "/registry/microservices/" + serviceId + "/schemas"; + if (withContent) { + url = url + "?withSchema=1"; + } + + try { + HttpResponse response = httpClient + .getHttpRequest(url, null, null); + if (response.getStatusCode() == HttpStatus.SC_OK) { + GetSchemaListResponse getSchemaResponse = HttpUtils + .deserialize(response.getContent(), GetSchemaListResponse.class); + return getSchemaResponse.getSchemas(); + } + throw new OperationException( + "get service schemas list fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "get service schemas list fails", e); + } + } + + /** + * Get one schema context of service + * + * @param serviceId + * @param schemaId + * @return + * @throws OperationException + */ + public String getServiceSchemaContext(String serviceId, String schemaId) { + try { + HttpResponse response = httpClient + .getHttpRequest("/registry/microservices/" + serviceId + "/schemas/" + schemaId, null, null); + if (response.getStatusCode() == HttpStatus.SC_OK) { + GetSchemaResponse getSchemaResponse = HttpUtils.deserialize(response.getContent(), GetSchemaResponse.class); + return getSchemaResponse.getSchema(); + } + throw new OperationException( + "get service schema context fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "get service schemas context fails", e); + } + } + + @Override + public boolean registerSchema(String serviceId, String schemaId, CreateSchemaRequest schema) { + try { + HttpResponse response = httpClient + .putHttpRequest("/registry/microservices/" + serviceId + "/schemas/" + schemaId, null, + HttpUtils.serialize(schema)); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return true; + } + throw new OperationException( + "update service schema fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "update service schema fails", e); + } + } + + @Override + public boolean updateServiceSchemaContext(String serviceId, SchemaInfo schemaInfo) { + try { + CreateSchemaRequest request = new CreateSchemaRequest(); + request.setSchema(schemaInfo.getSchema()); + request.setSummary(schemaInfo.getSummary()); + HttpResponse response = httpClient + .putHttpRequest("/registry/microservices/" + serviceId + "/schemas/" + schemaInfo.getSchemaId(), null, + HttpUtils.serialize(request)); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return true; + } + throw new OperationException( + "update service schema fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "update service schema fails", e); + } + } + + @Override + public boolean batchUpdateServiceSchemaContext(String serviceId, ModifySchemasRequest modifySchemasRequest) { + try { + HttpResponse response = httpClient + .postHttpRequest("/registry/microservices/" + serviceId + "/schemas", null, + HttpUtils.serialize(modifySchemasRequest)); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return true; + } + throw new OperationException( + "update service schema fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "update service schema fails", e); + } + } + + @Override + public RbacTokenResponse queryToken(RbacTokenRequest request, String host) { + try { + String queryAddress = addressManager.compareAndGetAddress(host); + HttpResponse response = httpClient + .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); + return result; + } + if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) { + RbacTokenResponse result = new RbacTokenResponse(); + result.setStatusCode(response.getStatusCode()); + return result; + } + if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED || + response.getStatusCode() == HttpStatus.SC_FORBIDDEN) { + RbacTokenResponse result = new RbacTokenResponse(); + result.setStatusCode(response.getStatusCode()); + ErrorMessage errorMessage = HttpUtils.deserialize(response.getContent(), ErrorMessage.class); + result.setErrorCode(errorMessage.getErrorCode()); + return result; + } + throw new OperationException( + "query token failed, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "query token failed", e); + } + } + + @Override + 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; + } + throw new OperationException( + "update service properties fails, statusCode = " + response.getStatusCode() + "; message = " + response + .getMessage() + + "; content = " + response.getContent()); + } catch (IOException e) { + throw new OperationException( + "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 new file mode 100644 index 00000000000..a1ab524c187 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterDiscovery.java @@ -0,0 +1,265 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.service.center.client; + +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; +import org.apache.servicecomb.http.client.task.Task; +import org.apache.servicecomb.service.center.client.DiscoveryEvents.InstanceChangedEvent; +import org.apache.servicecomb.service.center.client.DiscoveryEvents.PullInstanceEvent; +import org.apache.servicecomb.service.center.client.model.FindMicroserviceInstancesResponse; +import org.apache.servicecomb.service.center.client.model.Microservice; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + +public class ServiceCenterDiscovery extends AbstractTask { + + public static final int MAX_INTERVAL = 600000; + + public static final int MIN_INTERVAL = 1000; + + private static final String ALL_VERSION = "0+"; + + private static volatile boolean pullInstanceTaskOnceInProgress = false; + + public static class SubscriptionKey { + final String appId; + + final String serviceName; + + public SubscriptionKey(String appId, String serviceName) { + this.appId = appId; + this.serviceName = serviceName; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SubscriptionKey that = (SubscriptionKey) o; + return appId.equals(that.appId) && + serviceName.equals(that.serviceName); + } + + @Override + public int hashCode() { + return Objects.hash(appId, serviceName); + } + } + + public static class SubscriptionValue { + String revision; + + List instancesCache; + } + + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterDiscovery.class); + + private final ServiceCenterClient serviceCenterClient; + + private final EventBus eventBus; + + private String myselfServiceId; + + private final Map instancesCache = new ConcurrentHashMap<>(); + + private final Map microserviceCache = new ConcurrentHashMap<>(); + + private long pollInterval = 15000; + + private boolean started = false; + + 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; + this.eventBus = eventBus; + this.eventBus.register(this); + } + + public ServiceCenterDiscovery setPollInterval(long interval) { + if (interval > MAX_INTERVAL || interval < MIN_INTERVAL) { + return this; + } + this.pollInterval = interval; + return this; + } + + public void updateMyselfServiceId(String myselfServiceId) { + this.myselfServiceId = myselfServiceId; + } + + public void startDiscovery() { + if (!started) { + started = true; + startTask(new PullInstanceTask()); + } + } + + public void registerIfNotPresent(SubscriptionKey subscriptionKey) { + if (this.instancesCache.get(subscriptionKey) == null) { + synchronized (lock) { + if (this.instancesCache.get(subscriptionKey) == null) { + SubscriptionValue value = new SubscriptionValue(); + pullInstance(subscriptionKey, value, false); + this.instancesCache.put(subscriptionKey, value); + } + } + } + } + + public List getInstanceCache(SubscriptionKey key) { + return this.instancesCache.get(key).instancesCache; + } + + @Subscribe + public void onPullInstanceEvent(PullInstanceEvent event) { + // to avoid too many pulls queued. + if (pullInstanceTaskOnceInProgress) { + return; + } + pullInstanceTaskOnceInProgress = true; + startTask(new PullInstanceOnceTask()); + } + + private void pullInstance(SubscriptionKey k, SubscriptionValue v, boolean sendChangedEvent) { + if (myselfServiceId == null) { + // registration not ready + return; + } + + try { + FindMicroserviceInstancesResponse instancesResponse = serviceCenterClient + .findMicroserviceInstance(myselfServiceId, k.appId, k.serviceName, ALL_VERSION, v.revision); + if (instancesResponse.isModified()) { + 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={}.", + instancesResponse.getRevision(), + instanceToString(instances), + v.revision, + instanceToString(v.instancesCache), + k.appId, + k.serviceName + ); + v.instancesCache = instances; + v.revision = instancesResponse.getRevision(); + if (sendChangedEvent) { + eventBus.post(new InstanceChangedEvent(k.appId, k.serviceName, + v.instancesCache)); + } + } + } catch (Exception e) { + LOGGER.warn("find service {}#{} instance failed, remaining local instances cache [{}], cause message: {}", + k.appId, k.serviceName, instanceToString(v.instancesCache), e.getMessage()); + } + } + + private void setMicroserviceInfo(List instances) { + instances.forEach(instance -> { + Microservice microservice = microserviceCache + .computeIfAbsent(instance.getServiceId(), id -> { + try { + return serviceCenterClient.getMicroserviceByServiceId(id); + } catch (Exception e) { + LOGGER.error("Find microservice by id={} failed", id, e); + throw e; + } + }); + instance.setMicroservice(microservice); + }); + } + + class PullInstanceTask implements Task { + @Override + public void execute() { + pullAllInstance(); + + 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() { + try { + pullAllInstance(); + } finally { + pullInstanceTaskOnceInProgress = false; + } + } + } + + private synchronized void pullAllInstance() { + 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) { + if (instances == null) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + for (MicroserviceInstance instance : instances) { + for (String endpoint : instance.getEndpoints()) { + sb.append(endpoint.length() > 64 ? endpoint.substring(0, 64) : endpoint); + sb.append("|"); + } + sb.append(instance.getStatus()); + 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 new file mode 100644 index 00000000000..fd39a4ce963 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterOperation.java @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.service.center.client; + +import java.util.List; +import java.util.Map; + +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; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstanceStatus; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstancesResponse; +import org.apache.servicecomb.service.center.client.model.MicroservicesResponse; +import org.apache.servicecomb.service.center.client.model.ModifySchemasRequest; +import org.apache.servicecomb.service.center.client.model.RbacTokenRequest; +import org.apache.servicecomb.service.center.client.model.RbacTokenResponse; +import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceInstanceResponse; +import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceResponse; +import org.apache.servicecomb.service.center.client.model.SchemaInfo; + +public interface ServiceCenterOperation { + /** + * Query service center instances. + * + * In configuration file, only one address of service center instances can be configured, and + * other instances can be discovered by calling this method. + * + * @return MicroserviceInstancesResponse + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + MicroserviceInstancesResponse getServiceCenterInstances(); + + /** + * Register microservcie. + * + * @return RegisterMicroserviceResponse + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + RegisteredMicroserviceResponse registerMicroservice(Microservice microservice); + + /** + * find all registered microservice of service-center. + * + * @return MicroserviceResponse + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + MicroservicesResponse getMicroserviceList(); + + /** + * Get microservice information by service id. + * + * @return Microservice + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + Microservice getMicroserviceByServiceId(String serviceId); + + /** + * query Microservice ID by appId, serviceName, version and environment. + * + * @return Microservice ID, null if microservice does not exists. + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + RegisteredMicroserviceResponse queryServiceId(Microservice microservice); + + /** + * Register microservice instances to service-center + * + * Notice: microserviceInstance' service id must be set before calling this method. + * + * @return instanceId + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + RegisteredMicroserviceInstanceResponse registerMicroserviceInstance(MicroserviceInstance microserviceInstance); + + /** + * Find microservice instances of service-center + * + * @return MicroserviceInstancesResponse + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + MicroserviceInstancesResponse getMicroserviceInstanceList(String serviceId); + + /** + * Get microservice instance by id + * + * @return MicroserviceInstance + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + MicroserviceInstance getMicroserviceInstance(String serviceId, String instanceId); + + /** + * Find MicroserviceInstance by properties. + * + * @return FindMicroserviceInstancesResponse + * @throws OperationException If some problems happened to contact service center or non http 200 returned.n + */ + FindMicroserviceInstancesResponse findMicroserviceInstance(String consumerId, String appId, String serviceName, + String versionRule, + String revision); + + /** + * Delete a microservice instance + * + * @throws OperationException + */ + void deleteMicroserviceInstance(String serviceId, String instanceId); + + /** + * Update status of microservice Instance + * + * @return if update is successful + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + boolean updateMicroserviceInstanceStatus(String serviceId, String instanceId, + MicroserviceInstanceStatus status); + + /** + * register schema. + * @return if register is successful + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + boolean registerSchema(String serviceId, String schemaId, CreateSchemaRequest schema); + + /** + * update schema context of service + * + * @return if update is successful + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + boolean updateServiceSchemaContext(String serviceId, SchemaInfo schemaInfo); + + /** + * batch update schema context of service + * + * @return if update is successful + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + boolean batchUpdateServiceSchemaContext(String serviceId, ModifySchemasRequest modifySchemasRequest); + + /** + * Batch send heartbeats to service-center + * + * @param heartbeatsRequest + * @throws OperationException + */ + void sendHeartBeats(HeartbeatsRequest heartbeatsRequest); + + /** + * send heart beat of this instance. + * @return if heartbeat is successful + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + boolean sendHeartBeat(String serviceId, String instanceId); + + /** + * Get schemas list of service + * + * @throws OperationException + */ + List getServiceSchemasList(String serviceId, boolean withContent); + + /** + * query token using user confidential + * + * @return if heartbeat is successful + * @throws OperationException If some problems happened to contact service center or non http 200 returned. + */ + RbacTokenResponse queryToken(RbacTokenRequest request, String host); + + /** + * Update properties of microservice + * + * @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, + 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 new file mode 100755 index 00000000000..e233f15fb52 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.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.service.center.client; + +import java.io.IOException; +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; + this.tenantName = tenantName; + 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, ""); + } + + 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, ""); + } + + public HttpResponse putHttpRequest(String url, Map headers, String content) throws IOException { + 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, ""); + } + + private HttpResponse doHttpRequest(String url, boolean absoluteUrl, Map headers, String content, + String method, String queryAddress) throws IOException { + String address = StringUtils.isEmpty(queryAddress) ? addressManager.address() : queryAddress; + String formatUrl = addressManager.formatUrl(url, absoluteUrl, address); + HttpRequest httpRequest = buildHttpRequest(formatUrl, headers, content, method); + HttpResponse httpResponse; + try { + httpResponse = httpTransport.doRequest(httpRequest); + recordAndSendUnAuthorizedEvent(httpResponse, address); + return httpResponse; + } catch (IOException e) { + addressManager.recordFailState(address); + String retryAddress = addressManager.address(); + formatUrl = addressManager.formatUrl(url, absoluteUrl, retryAddress); + LOGGER.warn("send request to {} failed and retry to {} once. ", address, retryAddress, e); + httpRequest = new HttpRequest(formatUrl, headers, content, method); + try { + httpResponse = httpTransport.doRequest(httpRequest); + recordAndSendUnAuthorizedEvent(httpResponse, retryAddress); + return httpResponse; + } catch (IOException ioException) { + addressManager.recordFailState(retryAddress); + LOGGER.warn("retry to {} failed again. ", retryAddress, e); + throw ioException; + } + } + } + + private void recordAndSendUnAuthorizedEvent(HttpResponse response, String address) { + if (this.eventBus != null && response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { + LOGGER.warn("request unauthorized from server [{}], message [{}]", address, response.getMessage()); + addressManager.recordFailState(address); + this.eventBus.post(new UnAuthorizedOperationEvent(address)); + } else { + addressManager.recordSuccessState(address); + } + } + + public void checkAddressAvailable(String address) { + ServiceCombServiceAvailableUtils.checkAddressAvailable(addressManager, address, httpTransport, ADDRESS_CHECK_PATH); + } + + private HttpRequest buildHttpRequest(String url, 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; + + private HttpTransport httpTransport; + + private ServiceCenterAddressManager addressManager; + + public Builder() { + } + + public Builder setTenantName(String tenantName) { + this.tenantName = tenantName; + return this; + } + + public Builder setHttpTransport(HttpTransport httpTransport) { + this.httpTransport = httpTransport; + return this; + } + + public Builder setAddressManager(ServiceCenterAddressManager addressManager) { + this.addressManager = addressManager; + return this; + } + + public ServiceCenterRawClient build() { + return new ServiceCenterRawClient(tenantName, httpTransport, addressManager); + } + } +} 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 new file mode 100644 index 00000000000..93988ea8948 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRegistration.java @@ -0,0 +1,318 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.service.center.client; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.http.client.task.AbstractTask; +import org.apache.servicecomb.http.client.task.Task; +import org.apache.servicecomb.service.center.client.RegistrationEvents.HeartBeatEvent; +import org.apache.servicecomb.service.center.client.RegistrationEvents.MicroserviceInstanceRegistrationEvent; +import org.apache.servicecomb.service.center.client.RegistrationEvents.MicroserviceRegistrationEvent; +import org.apache.servicecomb.service.center.client.RegistrationEvents.SchemaRegistrationEvent; +import org.apache.servicecomb.service.center.client.model.CreateSchemaRequest; +import org.apache.servicecomb.service.center.client.model.Microservice; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; +import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceInstanceResponse; +import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceResponse; +import org.apache.servicecomb.service.center.client.model.SchemaInfo; +import org.apache.servicecomb.service.center.client.model.ServiceCenterConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.EventBus; + +public class ServiceCenterRegistration extends AbstractTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterRegistration.class); + + public static final int MAX_INTERVAL = 600000; + + public static final int MIN_INTERVAL = 1000; + + private final ServiceCenterClient serviceCenterClient; + + private final EventBus eventBus; + + private Microservice microservice; + + private MicroserviceInstance microserviceInstance; + + private List schemaInfos = new ArrayList<>(); + + private final ServiceCenterConfiguration serviceCenterConfiguration; + + private long heartBeatInterval = 15000; + + private long heartBeatRequestTimeout = 5000; + + public ServiceCenterRegistration(ServiceCenterClient serviceCenterClient, ServiceCenterConfiguration + serviceCenterConfiguration, EventBus eventBus) { + super("service-center-registration-task"); + this.serviceCenterClient = serviceCenterClient; + this.serviceCenterConfiguration = serviceCenterConfiguration; + this.eventBus = eventBus; + } + + public ServiceCenterRegistration setMicroserviceInstance(MicroserviceInstance microserviceInstance) { + this.microserviceInstance = microserviceInstance; + return this; + } + + public ServiceCenterRegistration setMicroservice(Microservice microservice) { + this.microservice = microservice; + return this; + } + + public ServiceCenterRegistration setHeartBeatInterval(long interval) { + if (interval > MAX_INTERVAL || interval < MIN_INTERVAL) { + return this; + } + this.heartBeatInterval = interval; + return this; + } + + public ServiceCenterRegistration setHeartBeatRequestTimeout(long timeout) { + if (timeout > MAX_INTERVAL || timeout < MIN_INTERVAL) { + return this; + } + this.heartBeatRequestTimeout = timeout; + return this; + } + + public ServiceCenterRegistration setSchemaInfos(List schemaInfos) { + this.schemaInfos = 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 { + int failedCount; + + RegisterMicroserviceTask(int failedCount) { + this.failedCount = failedCount; + } + + @Override + public void execute() { + try { + RegisteredMicroserviceResponse serviceResponse = serviceCenterClient.queryServiceId(microservice); + if (serviceResponse == null) { + RegisteredMicroserviceResponse response = serviceCenterClient.registerMicroservice(microservice); + if (StringUtils.isEmpty(response.getServiceId())) { + LOGGER.error("register microservice failed, and will try again."); + eventBus.post(new MicroserviceRegistrationEvent(false, microservice)); + startTask(new BackOffSleepTask(failedCount + 1, new RegisterMicroserviceTask(failedCount + 1))); + return; + } + microservice.setServiceId(response.getServiceId()); + microserviceInstance.setServiceId(response.getServiceId()); + microserviceInstance.setMicroservice(microservice); + eventBus.post(new MicroserviceRegistrationEvent(true, microservice)); + startTask(new RegisterSchemaTask(0)); + } else { + Microservice newMicroservice = serviceCenterClient.getMicroserviceByServiceId(serviceResponse.getServiceId()); + + Map propertiesTemp = microservice.getProperties(); + microservice.setProperties(newMicroservice.getProperties()); + microservice.getProperties().putAll(propertiesTemp); + if (serviceCenterClient.updateMicroserviceProperties(serviceResponse.getServiceId(), + microservice.getProperties(), microservice.getFramework())) { + LOGGER.info( + "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=[{}], " + + "frameworkVersion [{}]", microservice.getProperties(), microservice.getFramework().getVersion()); + } + + microservice.setServiceId(serviceResponse.getServiceId()); + microserviceInstance.setServiceId(serviceResponse.getServiceId()); + microserviceInstance.setMicroservice(microservice); + if (isSwaggerDifferent(newMicroservice)) { + if (serviceCenterConfiguration.isCanOverwriteSwagger()) { + LOGGER.warn("Service has already registered, but schema ids not equal, try to register it again"); + eventBus.post(new MicroserviceRegistrationEvent(true, microservice)); + startTask(new RegisterSchemaTask(0)); + return; + } + if (serviceCenterConfiguration.isIgnoreSwaggerDifferent()) { + LOGGER.warn("Service has already registered, but schema ids not equal. Ignore and continue to register"); + } else { + throw new IllegalStateException( + "Service has already registered, but schema ids not equal, stop register. " + + "Change the microservice version or delete the old microservice info and try again."); + } + } + eventBus.post(new MicroserviceRegistrationEvent(true, microservice)); + startTask(new RegisterMicroserviceInstanceTask(0)); + } + } catch (IllegalStateException e) { + throw e; + } catch (Exception e) { + LOGGER.error("register microservice failed, and will try again.", e); + eventBus.post(new MicroserviceRegistrationEvent(false, microservice)); + startTask(new BackOffSleepTask(failedCount + 1, new RegisterMicroserviceTask(failedCount + 1))); + } + } + } + + private boolean isSwaggerDifferent(Microservice newMicroservice) { + return !isListEquals(newMicroservice.getSchemas(), microservice.getSchemas()); + } + + private boolean isListEquals(List one, List two) { + return one.size() == two.size() && one.containsAll(two) && two.containsAll(one); + } + + class RegisterSchemaTask implements Task { + int failedCount; + + RegisterSchemaTask(int failedCount) { + this.failedCount = failedCount; + } + + @Override + public void execute() { + try { + if (schemaInfos == null || schemaInfos.isEmpty()) { + LOGGER.warn("no schemas defined for this microservice."); + eventBus.post(new SchemaRegistrationEvent(true, microservice)); + startTask(new RegisterMicroserviceInstanceTask(0)); + return; + } + + for (SchemaInfo schemaInfo : schemaInfos) { + CreateSchemaRequest request = new CreateSchemaRequest(); + request.setSchema(schemaInfo.getSchema()); + request.setSummary(schemaInfo.getSummary()); + if (!serviceCenterClient.registerSchema(microservice.getServiceId(), schemaInfo.getSchemaId(), request)) { + LOGGER.error("register schema content failed, and will try again."); + eventBus.post(new SchemaRegistrationEvent(false, microservice)); + // back off by multiply + startTask(new BackOffSleepTask(failedCount + 1, new RegisterSchemaTask((failedCount + 1) * 2))); + return; + } + } + + eventBus.post(new SchemaRegistrationEvent(true, microservice)); + startTask(new RegisterMicroserviceInstanceTask(0)); + } catch (Exception e) { + LOGGER.error("register schema content failed, and will try again.", e); + eventBus.post(new SchemaRegistrationEvent(false, microservice)); + // back off by multiply + startTask(new BackOffSleepTask(failedCount + 1, new RegisterSchemaTask((failedCount + 1) * 2))); + } + } + } + + class RegisterMicroserviceInstanceTask implements Task { + int failedCount; + + RegisterMicroserviceInstanceTask(int failedCount) { + this.failedCount = failedCount; + } + + @Override + public void execute() { + try { + RegisteredMicroserviceInstanceResponse instance = serviceCenterClient + .registerMicroserviceInstance(microserviceInstance); + if (instance == null) { + LOGGER.error("register microservice instance failed, and will try again."); + eventBus.post(new MicroserviceInstanceRegistrationEvent(false, microservice, microserviceInstance)); + startTask(new BackOffSleepTask(failedCount + 1, new RegisterMicroserviceInstanceTask(failedCount + 1))); + } else { + microserviceInstance.setInstanceId(instance.getInstanceId()); + LOGGER.info("register microservice successfully, service id={}, instance id={}", microservice.getServiceId(), + microserviceInstance.getInstanceId()); + eventBus.post(new MicroserviceInstanceRegistrationEvent(true, microservice, microserviceInstance)); + startTask(new SendHeartBeatTask(0)); + } + } catch (Exception e) { + LOGGER.error("register microservice instance failed, and will try again.", e); + eventBus.post(new MicroserviceInstanceRegistrationEvent(false, microservice, microserviceInstance)); + startTask(new BackOffSleepTask(failedCount + 1, new RegisterMicroserviceInstanceTask(failedCount + 1))); + } + } + } + + class SendHeartBeatTask implements Task { + private static final int FAILED_RETRY = 3; + + int failedCount; + + SendHeartBeatTask(int failedCount) { + this.failedCount = failedCount; + } + + @Override + public void execute() { + try { + if (failedCount >= FAILED_RETRY) { + eventBus.post(new HeartBeatEvent(false, microservice, microserviceInstance)); + startTask(new RegisterMicroserviceTask(0)); + return; + } + + if (!serviceCenterClient.sendHeartBeat(microservice.getServiceId(), microserviceInstance.getInstanceId())) { + 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 { + // wait 10 * 3000 ms and send heart beat again. + eventBus.post(new HeartBeatEvent(true, microservice, microserviceInstance)); + startTask( + new BackOffSleepTask(Math.max(heartBeatInterval, heartBeatRequestTimeout), new SendHeartBeatTask(0))); + } + } catch (Exception 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 new file mode 100644 index 00000000000..9f24a74a55a --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.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.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; +import org.apache.servicecomb.http.client.common.WebSocketTransport; +import org.apache.servicecomb.service.center.client.DiscoveryEvents.PullInstanceEvent; +import org.java_websocket.handshake.ServerHandshake; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.EventBus; + +public class ServiceCenterWatch implements WebSocketListener { + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterWatch.class); + + private static final String HTTP = "http://"; + + private static final String HTTPS = "https://"; + + private static final String WS = "ws://"; + + private static final String WSS = "wss://"; + + private static final String WATCH = "/v4/%s/registry/microservices/%s/watcher"; + + private static final long SLEEP_BASE = 3000; + + private static final long SLEEP_MAX = 10 * 60 * 10000; + + private final ServiceCenterAddressManager addressManager; + + private final SSLProperties sslProperties; + + private final RequestAuthHeaderProvider requestAuthHeaderProvider; + + private final String tenantName; + + private final Map extraGlobalHeaders; + + private WebSocketTransport webSocketTransport; + + private String project; + + private String serviceId; + + private AtomicInteger continuousError = new AtomicInteger(0); + + private final AtomicBoolean reconnecting = new AtomicBoolean(false); + + private final EventBus eventBus; + + private String currentServerUri; + + private final ExecutorService connector = Executors.newFixedThreadPool(1, (r) -> new + Thread(r, "web-socket-connector")); + + public ServiceCenterWatch(ServiceCenterAddressManager addressManager, + SSLProperties sslProperties, + RequestAuthHeaderProvider requestAuthHeaderProvider, + String tenantName, + Map extraGlobalHeaders, + EventBus eventBus) { + this.addressManager = addressManager; + this.sslProperties = sslProperties; + this.requestAuthHeaderProvider = requestAuthHeaderProvider; + this.tenantName = tenantName; + this.extraGlobalHeaders = extraGlobalHeaders; + this.eventBus = eventBus; + } + + public void startWatch(String project, String serviceId) { + this.project = project; + this.serviceId = serviceId; + + startWatch(); + } + + private void startWatch() { + connector.submit(() -> { + backOff(); + String address = addressManager.address(); + try { + Map headers = new HashMap<>(); + headers.put("x-domain-name", this.tenantName); + headers.putAll(this.extraGlobalHeaders); + headers.putAll(this.requestAuthHeaderProvider.loadAuthHeader(createSignRequest(address))); + currentServerUri = convertAddress(address); + LOGGER.info("start watch to address {}", currentServerUri); + webSocketTransport = new WebSocketTransport(currentServerUri, sslProperties, + headers, this); + webSocketTransport.connectBlocking(); + addressManager.recordSuccessState(address); + } catch (Exception e) { + addressManager.recordFailState(address); + LOGGER.error("start watch failed. ", e); + } + }); + } + + 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)) { + return WS + address.substring(HTTP.length()) + url; + } + + if (address.startsWith(HTTPS)) { + return WSS + address.substring(HTTPS.length()) + url; + } + return address + url; + } + + public void stop() { + if (webSocketTransport != null) { + webSocketTransport.close(); + } + } + + private void reconnect() { + if (reconnecting.getAndSet(true)) { + return; + } + continuousError.incrementAndGet(); + if (webSocketTransport != null) { + webSocketTransport.close(); + } + startWatch(); + } + + private void backOff() { + if (this.continuousError.get() <= 0) { + return; + } + try { + Thread.sleep(Math.min(SLEEP_MAX, this.continuousError.get() * this.continuousError.get() * SLEEP_BASE)); + } catch (InterruptedException e) { + // do not care + } + } + + @Override + public void onMessage(String s) { + LOGGER.info("web socket receive message [{}], start query instance", s); + this.eventBus.post(new PullInstanceEvent()); + } + + @Override + public void onError(Exception e) { + LOGGER.warn("web socket receive error [{}], will restart.", e.getMessage()); + reconnect(); + } + + @Override + public void onClose(int code, String reason, boolean remote) { + LOGGER.warn("web socket closed, code={}, reason={}.", code, reason); + } + + @Override + public void onOpen(ServerHandshake serverHandshake) { + LOGGER.info("web socket connected to server {}, status={}, message={}", currentServerUri, + serverHandshake.getHttpStatus(), + serverHandshake.getHttpStatusMessage()); + continuousError.set(0); + reconnecting.set(false); + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/exception/OperationException.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/exception/OperationException.java new file mode 100644 index 00000000000..95cb1a7849a --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/exception/OperationException.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.service.center.client.exception; + +public class OperationException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public OperationException() { + } + + public OperationException(String message) { + super(message); + } + + public OperationException(String message, Throwable cause) { + super(message, cause); + } + + public OperationException(Throwable cause) { + super(cause); + } + + public OperationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/BasePath.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/BasePath.java new file mode 100755 index 00000000000..892f3f393f0 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/BasePath.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.service.center.client.model; + +import java.util.Map; + +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/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/CreateMicroserviceInstanceRequest.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/CreateMicroserviceInstanceRequest.java new file mode 100644 index 00000000000..a0e853fed1b --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/CreateMicroserviceInstanceRequest.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.service.center.client.model; + +public class CreateMicroserviceInstanceRequest { + private MicroserviceInstance instance; + + public MicroserviceInstance getInstance() { + return instance; + } + + public void setInstance(MicroserviceInstance instance) { + this.instance = instance; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/CreateMicroserviceRequest.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/CreateMicroserviceRequest.java new file mode 100644 index 00000000000..a234638f19e --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/CreateMicroserviceRequest.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.service.center.client.model; + +public class CreateMicroserviceRequest { + private Microservice service; + + public Microservice getService() { + return service; + } + + public void setService(Microservice service) { + this.service = service; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/CreateSchemaRequest.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/CreateSchemaRequest.java new file mode 100644 index 00000000000..39108645df1 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/CreateSchemaRequest.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.service.center.client.model; + +public class CreateSchemaRequest { + private String schema; + + private String summary; + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/DataCenterInfo.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/DataCenterInfo.java new file mode 100755 index 00000000000..d89f6f5cda5 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/DataCenterInfo.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.service.center.client.model; + +public class DataCenterInfo { + + 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/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/ErrorMessage.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/ErrorMessage.java new file mode 100644 index 00000000000..5e0735cef5d --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/ErrorMessage.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.service.center.client.model; + +public class ErrorMessage { + private String errorCode; + + private String errorMessage; + + private String detail; + + 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; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/FindMicroserviceInstancesResponse.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/FindMicroserviceInstancesResponse.java new file mode 100644 index 00000000000..3b9dd474d82 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/FindMicroserviceInstancesResponse.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.service.center.client.model; + +public class FindMicroserviceInstancesResponse { + private boolean modified; + + private String revision; + + private MicroserviceInstancesResponse microserviceInstancesResponse; + + public boolean isModified() { + return modified; + } + + public void setModified(boolean modified) { + this.modified = modified; + } + + public String getRevision() { + return revision; + } + + public void setRevision(String revision) { + this.revision = revision; + } + + public MicroserviceInstancesResponse getMicroserviceInstancesResponse() { + return microserviceInstancesResponse; + } + + public void setMicroserviceInstancesResponse( + MicroserviceInstancesResponse microserviceInstancesResponse) { + this.microserviceInstancesResponse = microserviceInstancesResponse; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/Framework.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/Framework.java new file mode 100755 index 00000000000..797b33d6cf7 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/Framework.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.service.center.client.model; + +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/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/GetSchemaListResponse.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/GetSchemaListResponse.java new file mode 100644 index 00000000000..1a0af177715 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/GetSchemaListResponse.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.service.center.client.model; + +import java.util.List; + +public class GetSchemaListResponse { + private List schemas; + + public List getSchemas() { + return schemas; + } + + public void setSchemas(List schemas) { + this.schemas = schemas; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/GetSchemaResponse.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/GetSchemaResponse.java new file mode 100644 index 00000000000..c91bcfa8c79 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/GetSchemaResponse.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.service.center.client.model; + +public class GetSchemaResponse { + private String schema; + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/HealthCheck.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/HealthCheck.java new file mode 100755 index 00000000000..78cf8c5fce4 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/HealthCheck.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.service.center.client.model; + +public class HealthCheck { + + private HealthCheckMode mode; + + private int port; + + private int interval; + + private int times; + + public HealthCheckMode getMode() { + return mode; + } + + public void setMode(HealthCheckMode mode) { + this.mode = mode; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public int getInterval() { + return interval; + } + + public void setInterval(int interval) { + this.interval = interval; + } + + public int getTimes() { + return times; + } + + public void setTimes(int times) { + this.times = times; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/HealthCheckMode.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/HealthCheckMode.java new file mode 100755 index 00000000000..eb77afc6420 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/HealthCheckMode.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.service.center.client.model; + +public enum HealthCheckMode { + unknown, + push, + pull +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/HeartbeatsRequest.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/HeartbeatsRequest.java new file mode 100755 index 00000000000..9f4ea115cfe --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/HeartbeatsRequest.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.service.center.client.model; + +import java.util.ArrayList; +import java.util.List; + +public class HeartbeatsRequest { + private List Instances; + + public HeartbeatsRequest(String serviceId, String instanceId) { + List instances = new ArrayList<>(); + instances.add(new InstancesRequest(serviceId, instanceId)); + this.Instances = instances; + } + + public List getInstances() { + return Instances; + } + + public void setInstances(List instances) { + this.Instances = instances; + } + + public void addInstances(InstancesRequest instancesRequest) { + if (this.Instances != null) { + this.Instances.add(instancesRequest); + } else { + this.Instances = new ArrayList<>(); + this.Instances.add(instancesRequest); + } + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/InstancesRequest.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/InstancesRequest.java new file mode 100755 index 00000000000..2eb65de5327 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/InstancesRequest.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.service.center.client.model; + +public class InstancesRequest { + private String serviceId; + + private String instanceId; + + public InstancesRequest(String serviceId, String instanceId) { + this.serviceId = serviceId; + this.instanceId = instanceId; + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public String getInstanceId() { + return instanceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } +} 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 new file mode 100755 index 00000000000..a034e57569f --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/Microservice.java @@ -0,0 +1,215 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.service.center.client.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonRootName; + +@JsonRootName("service") +public class Microservice { + + private String serviceId; + + private Framework framework; + + private String registerBy; + + private String environment; + + private String appId; + + private String serviceName; + + /** + * for governance + * when invoke cross app, if not use alias name, then {microservice}:{schema}:{operation} will conflict + */ + private String alias; + + private String version; + + private String description; + + private String level; + + private List schemas = new ArrayList<>(); + + private List paths = new ArrayList<>(); + + private MicroserviceStatus status = MicroserviceStatus.UP; + + private Map properties = new HashMap<>(); + + private List instances; + + private String timestamp; + + private String modTimestamp; + + public Microservice() { + + } + + public Microservice(String serviceName) { + this.serviceName = serviceName; + } + + public List getInstances() { + return instances; + } + + public void setInstances(List instances) { + this.instances = instances; + } + + public Framework getFramework() { + return framework; + } + + public void setFramework(Framework framework) { + this.framework = framework; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public String getRegisterBy() { + return registerBy; + } + + public void setRegisterBy(String registerBy) { + this.registerBy = registerBy; + } + + public String getEnvironment() { + return environment; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getLevel() { + return level; + } + + public void setLevel(String level) { + this.level = level; + } + + public List getSchemas() { + return schemas; + } + + public void setSchemas(List schemas) { + this.schemas = schemas; + } + + public MicroserviceStatus getStatus() { + return status; + } + + public void setStatus(MicroserviceStatus status) { + this.status = status; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + + public String getModTimestamp() { + return modTimestamp; + } + + public void setModTimestamp(String modTimestamp) { + this.modTimestamp = modTimestamp; + } + + public List getPaths() { + return paths; + } + + 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 new file mode 100755 index 00000000000..7f03c314043 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstance.java @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.service.center.client.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonRootName; + +@JsonRootName("instance") +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; + + private String serviceId; + + private String version; + + private List endpoints = new ArrayList<>(); + + private String hostName; + + private MicroserviceInstanceStatus status; + + private Map properties = new HashMap<>(); // reserved key list: region|az|stage|group + + private HealthCheck healthCheck; + + private DataCenterInfo dataCenterInfo; + + private String timestamp; + + private String modTimestamp; + + @JsonIgnore + private Microservice microservice; + + public String getInstanceId() { + return instanceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public List getEndpoints() { + return endpoints; + } + + public void setEndpoints(List endpoints) { + this.endpoints = endpoints; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public MicroserviceInstanceStatus getStatus() { + return status; + } + + public void setStatus(MicroserviceInstanceStatus status) { + this.status = status; + } + + public HealthCheck getHealthCheck() { + return healthCheck; + } + + public void setHealthCheck(HealthCheck healthCheck) { + this.healthCheck = healthCheck; + } + + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + + public String getModTimestamp() { + return modTimestamp; + } + + public void setModTimestamp(String modTimestamp) { + this.modTimestamp = modTimestamp; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public DataCenterInfo getDataCenterInfo() { + return dataCenterInfo; + } + + public void setDataCenterInfo(DataCenterInfo dataCenterInfo) { + this.dataCenterInfo = dataCenterInfo; + } + + public void setMicroservice(Microservice microservice) { + this.microservice = microservice; + } + + @JsonIgnore + public Microservice getMicroservice() { + return this.microservice; + } + + @JsonIgnore + public String getServiceName() { + return this.microservice.getServiceName(); + } + + @JsonIgnore + 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/MicroserviceInstanceResponse.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstanceResponse.java new file mode 100644 index 00000000000..387ad90297d --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstanceResponse.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.service.center.client.model; + +public class MicroserviceInstanceResponse { + private MicroserviceInstance instance; + + public MicroserviceInstance getInstance() { + return instance; + } + + public void setInstance(MicroserviceInstance instance) { + this.instance = instance; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstanceStatus.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstanceStatus.java new file mode 100755 index 00000000000..1449e088976 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstanceStatus.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.service.center.client.model; + +public enum MicroserviceInstanceStatus { + STARTING, + TESTING, + UP, + OUTOFSERVICE, + DOWN, + UNKNOWN +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstancesResponse.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstancesResponse.java new file mode 100755 index 00000000000..cb1eb76317d --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceInstancesResponse.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.service.center.client.model; + +import java.util.List; + +public class MicroserviceInstancesResponse { + + private List instances; + + public List getInstances() { + return instances; + } + + public void setInstances(List instances) { + this.instances = instances; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceResponse.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceResponse.java new file mode 100644 index 00000000000..00c95a1a3f2 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceResponse.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.service.center.client.model; + +public class MicroserviceResponse { + private Microservice service; + + public Microservice getService() { + return service; + } + + public void setService(Microservice service) { + this.service = service; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceStatus.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceStatus.java new file mode 100755 index 00000000000..ab3b8b5f3ac --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroserviceStatus.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.service.center.client.model; + +public enum MicroserviceStatus { + UNKNOWN, + UP, + DOWN +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroservicesResponse.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroservicesResponse.java new file mode 100755 index 00000000000..f85ad19fab8 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/MicroservicesResponse.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.service.center.client.model; + +import java.util.List; + +public class MicroservicesResponse { + + private List services; + + public List getServices() { + return services; + } + + public void setServices(List services) { + this.services = services; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/ModifySchemasRequest.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/ModifySchemasRequest.java new file mode 100644 index 00000000000..1bd3531d897 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/ModifySchemasRequest.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.service.center.client.model; + +import java.util.List; + +public class ModifySchemasRequest { + private List schemas; + + public List getSchemas() { + return schemas; + } + + public void setSchemas(List schemas) { + this.schemas = schemas; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RbacTokenRequest.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RbacTokenRequest.java new file mode 100644 index 00000000000..cd926364f41 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RbacTokenRequest.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.service.center.client.model; + +public class RbacTokenRequest { + private String name; + + private String password; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("RbacTokenRequest{"); + sb.append("name='").append(name).append('\''); + sb.append(", password='").append(password).append('\''); + sb.append('}'); + return sb.toString(); + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RbacTokenResponse.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RbacTokenResponse.java new file mode 100644 index 00000000000..1b699d3ae30 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RbacTokenResponse.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.service.center.client.model; + +public class RbacTokenResponse { + private int statusCode; + + private String token; + + private String errorCode; + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RegisteredMicroserviceInstanceResponse.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RegisteredMicroserviceInstanceResponse.java new file mode 100644 index 00000000000..88dbe50cecb --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RegisteredMicroserviceInstanceResponse.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.service.center.client.model; + +public class RegisteredMicroserviceInstanceResponse { + private String instanceId; + + public String getInstanceId() { + return instanceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RegisteredMicroserviceResponse.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RegisteredMicroserviceResponse.java new file mode 100644 index 00000000000..d4a6d5c5a5d --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/RegisteredMicroserviceResponse.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.service.center.client.model; + +public class RegisteredMicroserviceResponse { + private String serviceId; + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/SchemaInfo.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/SchemaInfo.java new file mode 100755 index 00000000000..40180433d0b --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/SchemaInfo.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.service.center.client.model; + +public class SchemaInfo { + private String schema; + + private String schemaId; + + private String summary; + + public SchemaInfo() { + + } + + public SchemaInfo(String schemaId, String schema, String summary) { + this.schemaId = schemaId; + this.schema = schema; + this.summary = summary; + } + + public String getSchema() { + return schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public String getSchemaId() { + return schemaId; + } + + public void setSchemaId(String schemaId) { + this.schemaId = schemaId; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/ServiceCenterConfiguration.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/ServiceCenterConfiguration.java new file mode 100644 index 00000000000..bc7618aa0f5 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/ServiceCenterConfiguration.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.service.center.client.model; + +public class ServiceCenterConfiguration { + + /** + * for registration service + * when swagger is different between local with remote serviceCenter. if ignoreSwaggerDifferent is true. + * it will ignore the different and continue the program. otherwise, the program will be stop. + */ + private boolean ignoreSwaggerDifferent; + + private boolean canOverwriteSwagger = true; + + public boolean isIgnoreSwaggerDifferent() { + return ignoreSwaggerDifferent; + } + + public boolean isCanOverwriteSwagger(){ + return canOverwriteSwagger; + } + + public ServiceCenterConfiguration setIgnoreSwaggerDifferent(boolean ignoreSwaggerDifferent) { + this.ignoreSwaggerDifferent = ignoreSwaggerDifferent; + return this; + } + + public ServiceCenterConfiguration setCanOverwriteSwagger(boolean canOverwriteSwagger) { + this.canOverwriteSwagger = canOverwriteSwagger; + return this; + } +} 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 new file mode 100644 index 00000000000..d7d4b093930 --- /dev/null +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/UpdatePropertiesRequest.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.service.center.client.model; + +import java.util.Map; + +public class UpdatePropertiesRequest { + private Map properties; + + private Framework framework; + + public Map getProperties() { + return properties; + } + + 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 new file mode 100644 index 00000000000..a2888cfad1b --- /dev/null +++ b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManagerTest.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.service.center.client; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; + +import com.google.common.eventbus.EventBus; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class ServiceCenterAddressManagerTest { + + private static final List addresses = new ArrayList<>(); + + private static ServiceCenterAddressManager addressManager1; + + private static ServiceCenterAddressManager addressManager2; + + + @Test + public void getUrlPrefix() { + addresses.add("http://127.0.0.1:30103"); + addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus(), "", ""); + + Assertions.assertNotNull(addressManager1); + + List addresses = addressManager1.getAddresses(); + Assertions.assertEquals(1, addresses.size()); + Assertions.assertEquals("http://127.0.0.1:30103", addresses.get(0)); + Assertions.assertEquals("http://127.0.0.1:30103", addressManager1.address()); + Assertions.assertEquals("http://127.0.0.1:30103/v4/", addressManager1.getUrlPrefix("http://127.0.0.1:30103")); + } + + @Test + public void formatUrlTest() { + addresses.add("http://127.0.0.1:30103"); + addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus(), "", ""); + Assertions.assertNotNull(addressManager1); + + String address = addressManager1.address(); + Assertions.assertEquals("http://127.0.0.1:30103", address); + String url = addressManager1.formatUrl("/test/", false, address); + Assertions.assertEquals("http://127.0.0.1:30103/v4/project/test/", url); + + url = addressManager1.formatUrl("/test/", true, address); + Assertions.assertEquals("http://127.0.0.1:30103/test/", url); + } + + @Test + public void onRefreshEndpointEvent() { + List addressAZ = new ArrayList<>(); + addressAZ.add("http://127.0.0.3:30100"); + List addressRG = new ArrayList<>(); + addressRG.add("http://127.0.0.4:30100"); + Map> zoneAndRegion = new HashMap<>(); + zoneAndRegion.put("sameZone", addressAZ); + zoneAndRegion.put("sameRegion", addressRG); + addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus(), "", ""); + RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "SERVICECENTER"); + addressManager1.refreshEndpoint(event, "SERVICECENTER"); + + List availableZone = addressManager1.getAvailableZone(); + Assertions.assertEquals("http://127.0.0.3:30100", availableZone.get(0)); + + List availableRegion = addressManager1.getAvailableRegion(); + Assertions.assertEquals("http://127.0.0.4:30100", availableRegion.get(0)); + } +} 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 new file mode 100755 index 00000000000..c486b2bd6ce --- /dev/null +++ b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterClientTest.java @@ -0,0 +1,524 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.service.center.client; + +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; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstanceStatus; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstancesResponse; +import org.apache.servicecomb.service.center.client.model.MicroservicesResponse; +import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceInstanceResponse; +import org.apache.servicecomb.service.center.client.model.RegisteredMicroserviceResponse; +import org.apache.servicecomb.service.center.client.model.SchemaInfo; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.mockito.ArgumentMatchers; + +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 { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + String responseString = "{\n" + + " \"instances\": [\n" + + " {\n" + + " \"instanceId\": \"111111\",\n" + + " \"serviceId\": \"222222\",\n" + + " \"version\": \"1.0\",\n" + + " \"hostName\": \"Test\",\n" + + " \"endpoints\": [\n" + + " \"string\"\n" + + " ],\n" + + " \"status\": \"UP\",\n" + + " \"properties\": {\n" + + " \"additionalProp1\": \"string\",\n" + + " \"additionalProp2\": \"string\",\n" + + " \"additionalProp3\": \"string\"\n" + + " },\n" + + " \"healthCheck\": {\n" + + " \"mode\": \"push\",\n" + + " \"port\": \"0\",\n" + + " \"interval\": \"0\",\n" + + " \"times\": \"0\"\n" + + " },\n" + + " \"dataCenterInfo\": {\n" + + " \"name\": \"string\",\n" + + " \"region\": \"string\",\n" + + " \"availableZone\": \"string\"\n" + + " },\n" + + " \"timestamp\": \"333333\",\n" + + " \"modTimestamp\": \"4444444\"\n" + + " }\n" + + " ]\n" + + "}"; + + httpResponse.setContent(responseString); + + Mockito.when(serviceCenterRawClient.getHttpRequest("/registry/health", null, null)).thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + MicroserviceInstancesResponse serviceCenterInstances = serviceCenterClient.getServiceCenterInstances(); + + Assertions.assertNotNull(serviceCenterInstances); + Assertions.assertEquals(1, serviceCenterInstances.getInstances().size()); + Assertions.assertEquals("111111", serviceCenterInstances.getInstances().get(0).getInstanceId()); + Assertions.assertEquals("222222", serviceCenterInstances.getInstances().get(0).getServiceId()); + } + + @Test + public void TestRegistryService() throws IOException { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + httpResponse.setContent("{\"serviceId\": \"111111\"}"); + + Microservice microservice = new Microservice(); + microservice.setServiceName("Test"); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true); + + Mockito.when(serviceCenterRawClient + .postHttpRequest(ArgumentMatchers.eq("/registry/microservices"), ArgumentMatchers.eq(null), + Mockito.anyString())) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + RegisteredMicroserviceResponse actualResponse = serviceCenterClient.registerMicroservice(microservice); + + Assertions.assertNotNull(actualResponse); + Assertions.assertEquals("111111", actualResponse.getServiceId()); + } + + @Test + public void TestGetServiceMessage() throws IOException { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + String responseString = "{\n" + + " \"service\": {\n" + + " \"serviceId\": \"111111\",\n" + + " \"environment\": \"string\",\n" + + " \"appId\": \"string\",\n" + + " \"serviceName\": \"string\",\n" + + " \"version\": \"string\",\n" + + " \"description\": \"string\",\n" + + " \"level\": \"string\",\n" + + " \"registerBy\": \"string\",\n" + + " \"schemas\": [\n" + + " \"string\"\n" + + " ],\n" + + " \"status\": \"UP\",\n" + + " \"timestamp\": \"string\",\n" + + " \"modTimestamp\": \"string\",\n" + + " \"framework\": {\n" + + " \"name\": \"string\",\n" + + " \"version\": \"string\"\n" + + " },\n" + + " \"paths\": [\n" + + " {\n" + + " \"Path\": \"string\",\n" + + " \"Property\": {\n" + + " \"additionalProp1\": \"string\",\n" + + " \"additionalProp2\": \"string\",\n" + + " \"additionalProp3\": \"string\"\n" + + " }\n" + + " }\n" + + " ],\n" + + " \"properties\": {\n" + + " \"additionalProp1\": \"string\",\n" + + " \"additionalProp2\": \"string\",\n" + + " \"additionalProp3\": \"string\"\n" + + " }\n" + + " }\n" + + "}"; + + httpResponse.setContent(responseString); + + Mockito.when(serviceCenterRawClient.getHttpRequest("/registry/microservices/111111", null, null)) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + Microservice microservices = serviceCenterClient.getMicroserviceByServiceId("111111"); + + Assertions.assertNotNull(microservices); + Assertions.assertEquals("111111", microservices.getServiceId()); + } + + @Test + public void TestGetServiceList() throws IOException { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + + MicroservicesResponse microservicesResponse = new MicroservicesResponse(); + List microserviceList = new ArrayList<>(); + microserviceList.add(new Microservice("Test1")); + microserviceList.add(new Microservice("Test2")); + microserviceList.add(new Microservice("Test3")); + microservicesResponse.setServices(microserviceList); + ObjectMapper mapper = new ObjectMapper(); + httpResponse.setContent(mapper.writeValueAsString(microservicesResponse)); + + Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + MicroservicesResponse actualMicroservicesResponse = serviceCenterClient.getMicroserviceList(); + + Assertions.assertNotNull(actualMicroservicesResponse); + Assertions.assertEquals(3, actualMicroservicesResponse.getServices().size()); + Assertions.assertEquals("Test1", actualMicroservicesResponse.getServices().get(0).getServiceName()); + } + + @Test + public void TestQueryServiceId() throws IOException { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + httpResponse.setContent("{\"serviceId\": \"111111\"}"); + + Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + Microservice microservice = new Microservice("Test111"); + RegisteredMicroserviceResponse actualServiceId = serviceCenterClient.queryServiceId(microservice); + + Assertions.assertNotNull(actualServiceId); + Assertions.assertEquals("111111", actualServiceId.getServiceId()); + } + + @Test + public void TestRegisterServiceInstance() throws IOException { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + httpResponse.setContent("{\"instanceId\": \"111111\"}"); + + MicroserviceInstance instance = new MicroserviceInstance(); + instance.setInstanceId("111111"); + instance.setServiceId("222222"); + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true); + + Mockito.when(serviceCenterRawClient.postHttpRequest(ArgumentMatchers.eq("/registry/microservices/222222/instances"), + ArgumentMatchers.eq(null), + Mockito.anyString())) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + RegisteredMicroserviceInstanceResponse actualResponse = serviceCenterClient.registerMicroserviceInstance(instance); + + Assertions.assertNotNull(actualResponse); + Assertions.assertEquals("111111", actualResponse.getInstanceId()); + } + + @Test + public void TestDeleteServiceInstance() throws IOException { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + + Mockito.when(serviceCenterRawClient.deleteHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + serviceCenterClient.deleteMicroserviceInstance("111", "222"); + } + + @Test + public void TestGetServiceInstanceList() throws IOException { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + String responseString = "{\n" + + " \"instances\": [\n" + + " {\n" + + " \"instanceId\": \"111111\",\n" + + " \"serviceId\": \"222222\",\n" + + " \"version\": \"1.0\",\n" + + " \"hostName\": \"Test\",\n" + + " \"endpoints\": [\n" + + " \"string\"\n" + + " ],\n" + + " \"status\": \"UP\",\n" + + " \"timestamp\": \"333333\",\n" + + " \"modTimestamp\": \"4444444\"\n" + + " }\n" + + " ]\n" + + "}"; + + httpResponse.setContent(responseString); + + Mockito.when(serviceCenterRawClient.getHttpRequest("/registry/microservices/222222/instances", null, null)) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + MicroserviceInstancesResponse serviceCenterInstances = serviceCenterClient + .getMicroserviceInstanceList("222222"); + + Assertions.assertNotNull(serviceCenterInstances); + Assertions.assertEquals(1, serviceCenterInstances.getInstances().size()); + Assertions.assertEquals("111111", serviceCenterInstances.getInstances().get(0).getInstanceId()); + Assertions.assertEquals("222222", serviceCenterInstances.getInstances().get(0).getServiceId()); + } + + @Test + public void TestGetServiceInstanceMessage() throws IOException { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + String responseString = "{\n" + + " \"instance\": {\n" + + " \"instanceId\": \"111\",\n" + + " \"serviceId\": \"222\",\n" + + " \"version\": \"1.0\",\n" + + " \"hostName\": \"Test\",\n" + + " \"endpoints\": [\n" + + " \"string\"\n" + + " ],\n" + + " \"status\": \"UP\",\n" + + " \"properties\": {\n" + + " \"additionalProp1\": \"string\",\n" + + " \"additionalProp2\": \"string\",\n" + + " \"additionalProp3\": \"string\"\n" + + " },\n" + + " \"healthCheck\": {\n" + + " \"mode\": \"push\",\n" + + " \"port\": \"0\",\n" + + " \"interval\": \"0\",\n" + + " \"times\": \"0\"\n" + + " },\n" + + " \"dataCenterInfo\": {\n" + + " \"name\": \"string\",\n" + + " \"region\": \"string\",\n" + + " \"availableZone\": \"string\"\n" + + " },\n" + + " \"timestamp\": \"333333\",\n" + + " \"modTimestamp\": \"4444444\"\n" + + " }\n" + + "}"; + + httpResponse.setContent(responseString); + + Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + MicroserviceInstance responseInstance = serviceCenterClient + .getMicroserviceInstance("111", "222"); + + Assertions.assertNotNull(responseInstance); + Assertions.assertEquals("111", responseInstance.getInstanceId()); + Assertions.assertEquals("Test", responseInstance.getHostName()); + } + + @Test + public void TestSendHeartBeats() throws IOException { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + + HeartbeatsRequest heartbeatsRequest = new HeartbeatsRequest("001", "1001"); + heartbeatsRequest.addInstances(new InstancesRequest("002", "1002")); + ObjectMapper mapper = new ObjectMapper(); + + Mockito + .when(serviceCenterRawClient.putHttpRequest("/registry/microservices/111/instances/222/heartbeat", null, null)) + .thenReturn(httpResponse); + Mockito.when(serviceCenterRawClient + .putHttpRequest(ArgumentMatchers.eq("/registry/heartbeats"), ArgumentMatchers.eq(null), + Mockito.anyString())) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + serviceCenterClient.sendHeartBeats(heartbeatsRequest); + } + + @Test + public void TestUpdateServicesInstanceStatus() throws IOException { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + + Mockito.when(serviceCenterRawClient.putHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + Boolean result = serviceCenterClient + .updateMicroserviceInstanceStatus("111", "222", MicroserviceInstanceStatus.UP); + + Assertions.assertNotNull(result); + Assertions.assertEquals(true, result); + } + + @Test + public void TestGetServiceSchemas() throws IOException { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + String responseString = "{\n" + + " \"schemas\": [\n" + + " {\n" + + " \"schemaId\": \"111111\",\n" + + " \"schema\": \"test\",\n" + + " \"summary\": \"test\"\n" + + " }\n" + + " ]\n" + + "}"; + httpResponse.setContent(responseString); + + Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + List schemaResponse = serviceCenterClient + .getServiceSchemasList("111", false); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = mapper.readTree(mapper.writeValueAsString(schemaResponse)); + + Assertions.assertNotNull(jsonNode); + Assertions.assertEquals("111111", jsonNode.get(0).get("schemaId").textValue()); + Assertions.assertEquals("test", jsonNode.get(0).get("schema").textValue()); + } + + @Test + public void TestGetServiceSchemasContext() throws IOException { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + String responseString = "{\n" + + " \"schema\": \"test context\"\n" + + "}"; + httpResponse.setContent(responseString); + + Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + String schemaContext = serviceCenterClient + .getServiceSchemaContext("111", "222"); + + Assertions.assertNotNull(schemaContext); + Assertions.assertEquals("test context", schemaContext); + } + + @Test + public void TestUpdateServiceSchema() throws IOException { + + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + + Mockito.when(serviceCenterRawClient.putHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + boolean result = serviceCenterClient + .updateServiceSchemaContext("111", new SchemaInfo()); + + Assertions.assertTrue(result); + } + + @Test + public void testUpdateMicroserviceProperties() throws IOException { + ServiceCenterRawClient serviceCenterRawClient = Mockito.mock(ServiceCenterRawClient.class); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setMessage("ok"); + + Mockito.when(serviceCenterRawClient.putHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(httpResponse); + + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); + boolean result = serviceCenterClient + .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 new file mode 100755 index 00000000000..48f3bd914c6 --- /dev/null +++ b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.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.service.center.client; + +import java.io.IOException; +import java.util.Arrays; + +import org.apache.servicecomb.http.client.common.HttpResponse; +import org.apache.servicecomb.http.client.common.HttpTransport; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import com.google.common.eventbus.EventBus; + +/** + * Created by on 2019/10/16. + */ +public class ServiceCenterRawClientTest { + + private static final String PROJECT_NAME = "default"; + + private static final String TENANT_NAME = "default"; + + @Test + 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(), "", ""); + ServiceCenterRawClient client = new ServiceCenterRawClient.Builder() + .setHttpTransport(httpTransport) + .setAddressManager(addressManager) + .setTenantName(TENANT_NAME) + .build(); + + HttpResponse httpResponse = new HttpResponse(); + httpResponse.setStatusCode(200); + httpResponse.setContent("ok"); + + Mockito.when(httpTransport.doRequest(Mockito.any())).thenReturn(httpResponse); + + HttpResponse actualGetResponse = client.getHttpRequest(null, null, null); + HttpResponse actualPostResponse = client.postHttpRequest(null, null, null); + HttpResponse actualPutResponse = client.putHttpRequest(null, null, null); + HttpResponse actualDeleteResponse = client.putHttpRequest(null, null, null); + + Assertions.assertNotNull(actualGetResponse); + Assertions.assertEquals("ok", actualGetResponse.getContent()); + Assertions.assertNotNull(actualPostResponse); + Assertions.assertEquals("ok", actualPostResponse.getContent()); + Assertions.assertNotNull(actualPutResponse); + Assertions.assertEquals("ok", actualPutResponse.getContent()); + Assertions.assertNotNull(actualDeleteResponse); + Assertions.assertEquals("ok", actualDeleteResponse.getContent()); + } +} diff --git a/clients/service-center-client/src/test/resources/tls/client.p12 b/clients/service-center-client/src/test/resources/tls/client.p12 new file mode 100644 index 00000000000..acd63412397 Binary files /dev/null and b/clients/service-center-client/src/test/resources/tls/client.p12 differ diff --git a/clients/service-center-client/src/test/resources/tls/server.jks b/clients/service-center-client/src/test/resources/tls/server.jks new file mode 100644 index 00000000000..f3429ed696e Binary files /dev/null and b/clients/service-center-client/src/test/resources/tls/server.jks differ diff --git a/common/common-access-log/pom.xml b/common/common-access-log/pom.xml new file mode 100644 index 00000000000..e572e6bfd94 --- /dev/null +++ b/common/common-access-log/pom.xml @@ -0,0 +1,54 @@ + + + + + common + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + common-access-log + Java Chassis::Common::CommonAccessLog + + + + org.apache.servicecomb + java-chassis-core + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + common-rest + + + org.apache.servicecomb + transport-rest-client + + + io.vertx + vertx-codegen + test + + + diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogBootListener.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogBootListener.java new file mode 100644 index 00000000000..54becedcbf0 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogBootListener.java @@ -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. + */ + +package org.apache.servicecomb.common.accessLog; + +import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.foundation.common.event.EventManager; + +public class AccessLogBootListener implements BootListener { + + private final AccessLogBootstrap accessLogBootstrap = new AccessLogBootstrap(); + + @Override + public void onAfterRegistry(BootEvent event) { + accessLogBootstrap.start(EventManager.getEventBus()); + } + + @Override + public void onBeforeClose(BootEvent event) { + accessLogBootstrap.shutdown(); + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogBootstrap.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogBootstrap.java new file mode 100644 index 00000000000..56cc12ef0e0 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogBootstrap.java @@ -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. + */ + +package org.apache.servicecomb.common.accessLog; + +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; + +import com.google.common.eventbus.EventBus; + +public class AccessLogBootstrap { + private static final AccessLogConfig config = AccessLogConfig.INSTANCE; + + public void start(EventBus eventBus) { + SPIServiceUtils.getSortedService(AccessLogInitializer.class) + .forEach(initializer -> initializer.init(eventBus, config)); + } + + public void shutdown() { + SPIServiceUtils.getSortedService(AccessLogInitializer.class) + .forEach(AccessLogInitializer::destroy); + } +} 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 new file mode 100644 index 00000000000..e2f204b3cad --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfig.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.apache.servicecomb.foundation.common.LegacyPropertyFactory; + +public class AccessLogConfig { + + public static final String SERVER_BASE = "servicecomb.accesslog."; + + public static final String CLIENT_BASE = "servicecomb.accesslog.request."; + + public static final String SERVER_LOG_ENABLED = SERVER_BASE + "enabled"; + + public static final String SERVER_LOG_PATTERN = SERVER_BASE + "pattern"; + + public static final String CLIENT_LOG_ENABLED = CLIENT_BASE + "enabled"; + + public static final String CLIENT_LOG_PATTERN = CLIENT_BASE + "pattern"; + + public static final String DEFAULT_SERVER_PATTERN = "%h - - %t %r %s %B %D"; + + public static final String DEFAULT_CLIENT_PATTERN = "%h %SCB-transport - - %t %r %s %D"; + + public static final AccessLogConfig INSTANCE = new AccessLogConfig(); + + private boolean serverLogEnabled; + + private boolean clientLogEnabled; + + private String serverLogPattern; + + private String clientLogPattern; + + private AccessLogConfig() { + init(); + } + + 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 isClientLogEnabled() { + return clientLogEnabled; + } + + public String getServerLogPattern() { + return serverLogPattern; + } + + 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/AccessLogInitializer.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogInitializer.java new file mode 100644 index 00000000000..564f0435203 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogInitializer.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.common.accessLog; + +import com.google.common.eventbus.EventBus; + +public interface AccessLogInitializer { + default int getOrder() { + return 0; + } + + void init(EventBus eventBus, AccessLogConfig accessLogConfig); + + default void destroy() {} +} 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 new file mode 100644 index 00000000000..43e5ad9b191 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/client/ClientDefaultInitializer.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.common.accessLog.client; + +import org.apache.servicecomb.common.accessLog.AccessLogConfig; +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; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + +public class ClientDefaultInitializer implements AccessLogInitializer { + private static final Logger LOGGER = LoggerFactory.getLogger("scb-access"); + + private AccessLogGenerator accessLogGenerator; + + @Override + public void init(EventBus eventBus, AccessLogConfig accessLogConfig) { + if (!accessLogConfig.isClientLogEnabled()) { + return; + } + accessLogGenerator = new AccessLogGenerator(accessLogConfig.getClientLogPattern()); + eventBus.register(this); + } + + @Subscribe + @AllowConcurrentEvents + public void onRequestOut(InvocationFinishEvent finishEvent) { + if (InvocationType.CONSUMER.equals(finishEvent.getInvocation().getInvocationType())) { + LOGGER.info(accessLogGenerator.generateClientLog(finishEvent)); + } + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/AccessLogGenerator.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/AccessLogGenerator.java new file mode 100644 index 00000000000..d67247d9257 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/AccessLogGenerator.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.common.accessLog.core; + +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.parser.AccessLogPatternParser; +import org.apache.servicecomb.common.accessLog.core.parser.impl.VertxRestAccessLogPatternParser; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import com.google.common.collect.Iterables; + +import io.vertx.ext.web.RoutingContext; + +/* + * Accept {@link AccessLogParam} and generate access log. + *
+ * Each AccessLogParam for a line of access log. + */ +public class AccessLogGenerator { + /* + * traversal this array to generate access log segment. + */ + private final AccessLogItem[] accessLogItems; + + private final AccessLogPatternParser logPatternParser = new VertxRestAccessLogPatternParser(); + + @SuppressWarnings("unchecked") + public AccessLogGenerator(String rawPattern) { + List> accessLogItemList = logPatternParser.parsePattern(rawPattern); + accessLogItems = Iterables.toArray(accessLogItemList, AccessLogItem.class); + } + + public String generateServerLog(ServerAccessLogEvent accessLogEvent) { + StringBuilder log = new StringBuilder(128); + for (AccessLogItem accessLogItem : getAccessLogItems()) { + accessLogItem.appendServerFormattedItem(accessLogEvent, log); + } + return log.toString(); + } + + public String generateClientLog(InvocationFinishEvent finishEvent) { + StringBuilder log = new StringBuilder(128); + for (AccessLogItem accessLogItem : getAccessLogItems()) { + accessLogItem.appendClientFormattedItem(finishEvent, log); + } + return log.toString(); + } + + @VisibleForTesting + AccessLogItem[] getAccessLogItems() { + return accessLogItems; + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/AccessLogItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/AccessLogItem.java new file mode 100644 index 00000000000..02fffb25a5c --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/AccessLogItem.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.common.accessLog.core.element; + +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +/* + * Access log item represents the items supported in access log and request log printing. + * It generate the segment of log according to {@link AccessLogParam} + */ +public interface AccessLogItem { + default void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + } + + default void appendClientFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) { + } +} 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 new file mode 100644 index 00000000000..9c759ca2cb5 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ConfigurableDatetimeAccessItem.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.element.impl; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import io.vertx.ext.web.RoutingContext; + +/** + * Configurable dateTime element. + */ +public class ConfigurableDatetimeAccessItem implements AccessLogItem { + + public static final String DEFAULT_DATETIME_PATTERN = "EEE, dd MMM yyyy HH:mm:ss zzz"; + + public static final Locale DEFAULT_LOCALE = Locale.US; + + private final ThreadLocal datetimeFormatHolder = new ThreadLocal<>(); + + private String pattern; + + private TimeZone timezone; + + private Locale locale; + + /** + * all configuration is set to default value. + */ + public ConfigurableDatetimeAccessItem() { + this(DEFAULT_DATETIME_PATTERN); + } + + /** + * the configurations not specified will get a default value. + * @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; + if (config.contains("|")) { + configArr = splitConfig(config); + } else { + // if there is no separator "|", regard configuration as pattern. + configArr = new String[3]; + configArr[0] = config; + } + if (3 != configArr.length) { + throw new IllegalArgumentException( + "wrong format of configuration, \"PATTERN|TIMEZONE|LOCALE\" is expected, but actually is \"" + config + "\""); + } + + setConfigurations(configArr); + } + + private String[] splitConfig(String config) { + return config.split("\\|{1}?", -1); + } + + 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]); + } + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + doAppendFormattedItem(accessLogEvent.getMilliStartTime(), builder); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + long milliDuration = finishEvent.getInvocation().getInvocationStageTrace().calcTotal() / 1000_000; + doAppendFormattedItem( + finishEvent.getInvocation().getInvocationStageTrace().getStartInMillis() + milliDuration, builder); + } + + private void doAppendFormattedItem(long milliStartTime, StringBuilder builder) { + SimpleDateFormat dateFormat = getDatetimeFormat(); + builder.append(dateFormat.format(new Date(milliStartTime))); + } + + private SimpleDateFormat getDatetimeFormat() { + SimpleDateFormat dateFormat = datetimeFormatHolder.get(); + if (null == dateFormat) { + dateFormat = new SimpleDateFormat(pattern, locale); + dateFormat.setTimeZone(timezone); + + datetimeFormatHolder.set(dateFormat); + } + + return dateFormat; + } + + public String getPattern() { + return pattern; + } + + public TimeZone getTimezone() { + return timezone; + } + + public Locale getLocale() { + return locale; + } +} 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 new file mode 100644 index 00000000000..e15179acc69 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieAccessItem.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.accessLog.core.element.impl; + +import java.util.Map.Entry; +import java.util.Set; + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.common.rest.RestConst; +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; + +public class CookieAccessItem implements AccessLogItem { + + public static final String RESULT_NOT_FOUND = "-"; + + private final String varName; + + public CookieAccessItem(String varName) { + this.varName = varName; + } + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + Set cookies = accessLogEvent.getRoutingContext().request().cookies(); + if (null == cookies) { + builder.append(RESULT_NOT_FOUND); + return; + } + for (Cookie cookie : cookies) { + if (varName.equals(cookie.getName())) { + builder.append(cookie.getValue()); + return; + } + } + builder.append(RESULT_NOT_FOUND); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation().getHandlerContext() + .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); + if (null == restRequestImpl || null == restRequestImpl.getCookieMap()) { + builder.append(RESULT_NOT_FOUND); + return; + } + for (Entry entry : restRequestImpl.getCookieMap().entrySet()) { + if (entry.getKey().equals(varName)) { + builder.append(entry.getValue()); + return; + } + } + builder.append(RESULT_NOT_FOUND); + } + + public String getVarName() { + return varName; + } +} 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 new file mode 100644 index 00000000000..49ecda73d94 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondAccessItem.java @@ -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. + */ + +package org.apache.servicecomb.common.accessLog.core.element.impl; + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import io.vertx.ext.web.RoutingContext; + +public class DurationMillisecondAccessItem implements AccessLogItem { + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + builder.append(accessLogEvent.getMilliEndTime() - accessLogEvent.getMilliStartTime()); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + 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 new file mode 100644 index 00000000000..68d2ba0d411 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondAccessItem.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.common.accessLog.core.element.impl; + + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import io.vertx.ext.web.RoutingContext; + +public class DurationSecondAccessItem implements AccessLogItem { + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + builder.append((accessLogEvent.getMilliEndTime() - accessLogEvent.getMilliStartTime()) / 1000); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + 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/FirstLineOfRequestAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/FirstLineOfRequestAccessItem.java new file mode 100644 index 00000000000..16f00eeb050 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/FirstLineOfRequestAccessItem.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.common.accessLog.core.element.impl; + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import io.vertx.ext.web.RoutingContext; + +public class FirstLineOfRequestAccessItem implements AccessLogItem { + private static final HttpMethodAccessItem METHOD_ELEMENT = new HttpMethodAccessItem(); + + private static final UrlPathAccessItem URI_PATH_ONLY_ELEMENT = new UrlPathAccessItem(); + + private static final RequestProtocolAccessItem VERSION_OR_PROTOCOL_ELEMENT = new RequestProtocolAccessItem(); + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + builder.append("\""); + METHOD_ELEMENT.appendServerFormattedItem(accessLogEvent, builder); + builder.append(" "); + URI_PATH_ONLY_ELEMENT.appendServerFormattedItem(accessLogEvent, builder); + builder.append(" "); + VERSION_OR_PROTOCOL_ELEMENT.appendServerFormattedItem(accessLogEvent, builder); + builder.append("\""); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + builder.append("\""); + METHOD_ELEMENT.appendClientFormattedItem(finishEvent, builder); + builder.append(" "); + URI_PATH_ONLY_ELEMENT.appendClientFormattedItem(finishEvent, builder); + builder.append(" "); + VERSION_OR_PROTOCOL_ELEMENT.appendClientFormattedItem(finishEvent, builder); + builder.append("\""); + } +} 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 new file mode 100644 index 00000000000..8c8b61583e3 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodAccessItem.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.common.accessLog.core.element.impl; + + +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.RestClientRequest; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import io.vertx.core.http.HttpServerRequest; +import io.vertx.ext.web.RoutingContext; + +/** + * HTTP method + */ +public class HttpMethodAccessItem implements AccessLogItem { + + public static final String EMPTY_RESULT = "-"; + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + HttpServerRequest request = accessLogEvent.getRoutingContext().request(); + if (null == request || null == request.method()) { + builder.append(EMPTY_RESULT); + return; + } + builder.append(request.method().toString()); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + OperationMeta operationMeta = finishEvent.getInvocation().getOperationMeta(); + if (operationMeta != null && !StringUtils.isEmpty(operationMeta.getHttpMethod())) { + builder.append(operationMeta.getHttpMethod()); + return; + } + RestClientRequest restRequestImpl = (RestClientRequest) finishEvent.getInvocation().getHandlerContext() + .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || null == restRequestImpl.getHttpClientRequest().getMethod()) { + builder.append(EMPTY_RESULT); + return; + } + 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 new file mode 100644 index 00000000000..61be5a790a6 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpStatusAccessItem.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.common.accessLog.core.element.impl; + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.swagger.invocation.Response; + +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.RoutingContext; + +public class HttpStatusAccessItem implements AccessLogItem { + public static final String EMPTY_RESULT = "-"; + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + HttpServerResponse response = accessLogEvent.getRoutingContext().response(); + if (null == response) { + builder.append(EMPTY_RESULT); + return; + } + if (response.closed() && !response.ended()) { + builder.append(EMPTY_RESULT); + return; + } + builder.append(response.getStatusCode()); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + Response response = finishEvent.getResponse(); + if (null == response) { + builder.append(EMPTY_RESULT); + return; + } + builder.append(response.getStatusCode()); + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/InvocationContextAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/InvocationContextAccessItem.java new file mode 100644 index 00000000000..d3a206aa803 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/InvocationContextAccessItem.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.common.accessLog.core.element.impl; + +import java.util.Map; + +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.core.Invocation; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + + +import io.vertx.ext.web.RoutingContext; + +public class InvocationContextAccessItem implements AccessLogItem { + + public static final String NOT_FOUND = "-"; + + final String varName; + + public InvocationContextAccessItem(String varName) { + this.varName = varName; + } + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + String invocationContextValue = getValueFromInvocationContext(accessLogEvent); + if (StringUtils.isEmpty(invocationContextValue)) { + builder.append(NOT_FOUND); + return; + } + builder.append(invocationContextValue); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + Invocation invocation = finishEvent.getInvocation(); + if (null == invocation || invocation.getContext() == null + || StringUtils.isEmpty(finishEvent.getInvocation().getContext().get(varName))) { + builder.append(NOT_FOUND); + return; + } + builder.append(finishEvent.getInvocation().getContext().get(varName)); + } + + + protected String getValueFromInvocationContext(ServerAccessLogEvent accessLogEvent) { + Map data = accessLogEvent.getRoutingContext().data(); + if (null == data || null == data.get(RestConst.REST_INVOCATION_CONTEXT)) { + return null; + } + return ((Invocation) data.get(RestConst.REST_INVOCATION_CONTEXT)).getContext(varName); + } + + public String getVarName() { + return varName; + } +} 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 new file mode 100644 index 00000000000..cfa2068b297 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostAccessItem.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.common.accessLog.core.element.impl; + + +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.RestClientRequest; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import io.vertx.core.http.HttpServerRequest; +import io.vertx.ext.web.RoutingContext; + +public class LocalHostAccessItem implements AccessLogItem { + public static final String EMPTY_RESULT = "-"; + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + builder.append(accessLogEvent.getLocalAddress()); + } + + /** + * client do not need localhost + */ + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + RestClientRequest restRequestImpl = (RestClientRequest) finishEvent.getInvocation().getHandlerContext() + .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); + 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.getHttpClientRequest().connection().localAddress().host()); + } + + public static String getLocalAddress(RoutingContext context) { + HttpServerRequest request = context.request(); + if (null == request || null == request.localAddress() || StringUtils.isEmpty(request.localAddress().host())) { + return EMPTY_RESULT; + } + return request.localAddress().host(); + } +} 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 new file mode 100644 index 00000000000..def97c325be --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortAccessItem.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.common.accessLog.core.element.impl; + + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.common.rest.RestConst; +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; + +public class LocalPortAccessItem implements AccessLogItem { + + public static final String EMPTY_RESULT = "-"; + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + HttpServerRequest request = accessLogEvent.getRoutingContext().request(); + if (null == request || null == request.localAddress()) { + builder.append(EMPTY_RESULT); + return; + } + builder.append(request.localAddress().port()); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation() + .getHandlerContext() + .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || null == restRequestImpl.getHttpClientRequest().connection() + || null == restRequestImpl.getHttpClientRequest().connection().localAddress()) { + builder.append(EMPTY_RESULT); + return; + } + 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/PlainTextAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/PlainTextAccessItem.java new file mode 100644 index 00000000000..0f7e130a6b9 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/PlainTextAccessItem.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.common.accessLog.core.element.impl; + + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import io.vertx.ext.web.RoutingContext; + +/** + * Print content as it is. + */ +public class PlainTextAccessItem implements AccessLogItem { + private final String content; + + public PlainTextAccessItem(String content) { + this.content = content; + } + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + builder.append(content); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + builder.append(content); + } +} 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 new file mode 100644 index 00000000000..e2493bd3c09 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringAccessItem.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.common.accessLog.core.element.impl; + +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.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; + +public class QueryStringAccessItem implements AccessLogItem { + public static final String EMPTY_RESULT = "-"; + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + HttpServerRequest request = accessLogEvent.getRoutingContext().request(); + if (null == request || StringUtils.isEmpty(request.query())) { + builder.append(EMPTY_RESULT); + return; + } + builder.append(request.query()); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation() + .getHandlerContext() + .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().query())) { + builder.append(EMPTY_RESULT); + return; + } + builder.append(restRequestImpl.getHttpClientRequest().query()); + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RemoteHostAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RemoteHostAccessItem.java new file mode 100644 index 00000000000..f3b6da6dbd1 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RemoteHostAccessItem.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.common.accessLog.core.element.impl; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; + +import io.vertx.core.http.HttpServerRequest; +import io.vertx.ext.web.RoutingContext; + +public class RemoteHostAccessItem implements AccessLogItem { + + public static final String EMPTY_RESULT = "-"; + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + HttpServerRequest request = accessLogEvent.getRoutingContext().request(); + if (null == request || null == request.remoteAddress() + || StringUtils.isEmpty(request.remoteAddress().host())) { + builder.append(EMPTY_RESULT); + return; + } + builder.append(request.remoteAddress().host()); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) { + Endpoint endpoint = clientLogEvent.getInvocation().getEndpoint(); + if (null == endpoint || null == endpoint.getAddress() + || StringUtils.isEmpty(((URIEndpointObject) endpoint.getAddress()).getHostOrIp())) { + builder.append(EMPTY_RESULT); + return; + } + builder.append(((URIEndpointObject) endpoint.getAddress()).getHostOrIp()); + } +} 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 new file mode 100644 index 00000000000..54099d3b4b1 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderAccessItem.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.accessLog.core.element.impl; + +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.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; + +public class RequestHeaderAccessItem implements AccessLogItem { + public static final String RESULT_NOT_FOUND = "-"; + + private final String varName; + + public RequestHeaderAccessItem(String varName) { + this.varName = varName; + } + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + MultiMap headers = accessLogEvent.getRoutingContext().request().headers(); + if (null == headers || StringUtils.isEmpty(headers.get(varName))) { + builder.append(RESULT_NOT_FOUND); + return; + } + builder.append(headers.get(varName)); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) { + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) clientLogEvent.getInvocation() + .getHandlerContext() + .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); + 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.getHttpClientRequest().headers().get(varName)); + } + + public String getVarName() { + return varName; + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestProtocolAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestProtocolAccessItem.java new file mode 100644 index 00000000000..0556ffa806b --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestProtocolAccessItem.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.common.accessLog.core.element.impl; + + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +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 io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpVersion; +import io.vertx.ext.web.RoutingContext; + +public class RequestProtocolAccessItem implements AccessLogItem { + public static final String EMPTY_RESULT = "-"; + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + HttpServerRequest request = accessLogEvent.getRoutingContext().request(); + if (null == request || null == request.version()) { + builder.append(EMPTY_RESULT); + return; + } + builder.append(getStringVersion(request.version())); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + Invocation invocation = finishEvent.getInvocation(); + if (invocation == null || null == invocation.getEndpoint() || null == invocation.getEndpoint().getAddress() + || !(invocation.getEndpoint().getAddress() instanceof URIEndpointObject) + || !((URIEndpointObject) invocation.getEndpoint().getAddress()).isHttp2Enabled()) { + builder.append("HTTP/1.1"); + return; + } + builder.append("HTTP/2.0"); + } + + private String getStringVersion(HttpVersion version) { + switch (version) { + case HTTP_2: + return "HTTP/2.0"; + case HTTP_1_0: + return "HTTP/1.0"; + case HTTP_1_1: + return "HTTP/1.1"; + default: + return EMPTY_RESULT; + } + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderAccessItem.java new file mode 100644 index 00000000000..e87962f98eb --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderAccessItem.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.common.accessLog.core.element.impl; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.swagger.invocation.Response; + +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.RoutingContext; + +public class ResponseHeaderAccessItem implements AccessLogItem { + + public static final String RESULT_NOT_FOUND = "-"; + + private final String varName; + + public ResponseHeaderAccessItem(String varName) { + this.varName = varName; + } + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + HttpServerResponse response = accessLogEvent.getRoutingContext().response(); + if (null == response || null == response.headers() || StringUtils.isEmpty(response.headers().get(varName))) { + builder.append(RESULT_NOT_FOUND); + return; + } + builder.append(response.headers().get(varName)); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + Response response = finishEvent.getResponse(); + String value = response != null ? response.getHeader(varName) : null; + if (null == value) { + builder.append(RESULT_NOT_FOUND); + return; + } + builder.append(value); + } + + public String getVarName() { + return varName; + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseSizeAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseSizeAccessItem.java new file mode 100644 index 00000000000..bbf094b6940 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseSizeAccessItem.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.common.accessLog.core.element.impl; + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.RoutingContext; + +public class ResponseSizeAccessItem implements AccessLogItem { + // print zeroBytes when bytes is zero + private final String zeroBytes; + + public ResponseSizeAccessItem(String zeroBytesPlaceholder) { + zeroBytes = zeroBytesPlaceholder; + } + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + HttpServerResponse response = accessLogEvent.getRoutingContext().response(); + if (null == response || 0 == response.bytesWritten()) { + builder.append(zeroBytes); + return; + } + builder.append(response.bytesWritten()); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + // client do not know how to calculate is right, maybe Object#toString().length + builder.append(zeroBytes); + } + + public String getZeroBytes() { + return zeroBytes; + } +} 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 new file mode 100644 index 00000000000..e9dfe16d018 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/TraceIdAccessItem.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.common.accessLog.core.element.impl; + +import org.apache.commons.lang3.StringUtils; +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 = CoreConst.TRACE_ID_NAME; + + public TraceIdAccessItem() { + super(TRACE_ID); + } + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + String traceId = getValueFromInvocationContext(accessLogEvent); + if (StringUtils.isEmpty(traceId)) { + traceId = accessLogEvent.getRoutingContext().request().getHeader(TRACE_ID); + } + builder.append(StringUtils.isEmpty(traceId) ? InvocationContextAccessItem.NOT_FOUND : traceId); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + Invocation invocation = finishEvent.getInvocation(); + if (invocation == null || invocation.getContext() == null + || StringUtils.isEmpty(invocation.getContext().get(TRACE_ID))) { + builder.append(InvocationContextAccessItem.NOT_FOUND); + return; + } + builder.append(invocation.getContext().get(TRACE_ID)); + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/TransportAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/TransportAccessItem.java new file mode 100644 index 00000000000..4a3f8da98a1 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/TransportAccessItem.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.common.accessLog.core.element.impl; + + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import io.vertx.ext.web.RoutingContext; + +public class TransportAccessItem implements AccessLogItem { + private static final String EMPTY_STR = "-"; + + /** + * access log only support rest + */ + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + builder.append("rest"); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + String transportName = finishEvent.getInvocation().getConfigTransportName(); + if (!StringUtils.isEmpty(transportName)) { + builder.append(transportName); + return; + } + Endpoint endpoint = finishEvent.getInvocation().getEndpoint(); + if (endpoint == null || StringUtils.isEmpty(endpoint.getEndpoint())) { + builder.append(EMPTY_STR); + return; + } + builder.append(endpoint.getEndpoint().split(":")[0]); + } +} 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 new file mode 100644 index 00000000000..5435a22cfb1 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathAccessItem.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.common.accessLog.core.element.impl; + + +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.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; + +public class UrlPathAccessItem implements AccessLogItem { + + public static final String EMPTY_RESULT = "-"; + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + HttpServerRequest request = accessLogEvent.getRoutingContext().request(); + if (null == request || StringUtils.isEmpty(request.path())) { + builder.append(EMPTY_RESULT); + return; + } + builder.append(request.path()); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + OperationMeta operationMeta = finishEvent.getInvocation().getOperationMeta(); + SchemaMeta schemaMeta = finishEvent.getInvocation().getSchemaMeta(); + if (operationMeta != null && schemaMeta != null && schemaMeta.getSwagger() != null) { + builder.append(operationMeta.getOperationPath()); + return; + } + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation() + .getHandlerContext() + .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().path())) { + builder.append(EMPTY_RESULT); + return; + } + builder.append(restRequestImpl.getHttpClientRequest().path()); + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathWithQueryAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathWithQueryAccessItem.java new file mode 100644 index 00000000000..369b458e618 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathWithQueryAccessItem.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.common.accessLog.core.element.impl; + +import static org.apache.servicecomb.common.rest.RestConst.REST_CLIENT_REQUEST_PATH; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import io.vertx.core.http.HttpServerRequest; +import io.vertx.ext.web.RoutingContext; + +public class UrlPathWithQueryAccessItem implements AccessLogItem { + + public static final String EMPTY_RESULT = "-"; + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + HttpServerRequest request = accessLogEvent.getRoutingContext().request(); + if (null == request || StringUtils.isEmpty(request.uri())) { + builder.append(EMPTY_RESULT); + return; + } + builder.append(request.uri()); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { + Invocation invocation = finishEvent.getInvocation(); + if (null == invocation || null == invocation.getLocalContext(REST_CLIENT_REQUEST_PATH) + || StringUtils.isEmpty(invocation.getLocalContext(REST_CLIENT_REQUEST_PATH).toString())) { + builder.append(EMPTY_RESULT); + return; + } + builder.append(invocation.getLocalContext(REST_CLIENT_REQUEST_PATH).toString()); + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/AccessLogItemCreator.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/AccessLogItemCreator.java new file mode 100644 index 00000000000..9703d588c3c --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/AccessLogItemCreator.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.common.accessLog.core.parser; + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.CookieAccessItem; + +/** + * The {@linkplain AccessLogItemCreator}s are able to instantiate a group of {@linkplain AccessLogItem}. + */ +public interface AccessLogItemCreator { + /** + * Create an instance of {@linkplain AccessLogItem} which is specified by the config. + * @param config + * e.g. For {@linkplain CookieAccessItem CookieItem}, + * the pattern may be "%{varName}C", and it's config is "varName". Some {@linkplain AccessLogItem} with no configurable + * pattern (like "%m") will receive {@code null} as config. + */ + AccessLogItem createItem(String config); +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/AccessLogItemMeta.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/AccessLogItemMeta.java new file mode 100644 index 00000000000..870bd1e9f7c --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/AccessLogItemMeta.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.common.accessLog.core.parser; + + +public class AccessLogItemMeta { + protected String prefix; + + protected String suffix; + + /** + * Used for sorting {@linkplain AccessLogItemMeta}. Default value is 0. + * Smaller one has higher priority. + */ + protected int order; + + protected AccessLogItemCreator accessLogItemCreator; + + public String getPrefix() { + return prefix; + } + + public AccessLogItemMeta setPrefix(String prefix) { + this.prefix = prefix; + return this; + } + + public String getSuffix() { + return suffix; + } + + public AccessLogItemMeta setSuffix(String suffix) { + this.suffix = suffix; + return this; + } + + public int getOrder() { + return order; + } + + public AccessLogItemMeta setOrder(int order) { + this.order = order; + return this; + } + + public AccessLogItemCreator getAccessLogItemCreator() { + return accessLogItemCreator; + } + + public AccessLogItemMeta setAccessLogItemCreator(AccessLogItemCreator accessLogItemCreator) { + this.accessLogItemCreator = accessLogItemCreator; + return this; + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/AccessLogPatternParser.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/AccessLogPatternParser.java new file mode 100644 index 00000000000..0dde4099d0c --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/AccessLogPatternParser.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.common.accessLog.core.parser; + +import java.util.List; + +import org.apache.servicecomb.common.accessLog.core.AccessLogGenerator; +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; + + +/** + * This parser will parse the rawPattern of access log and generate a list of {@link AccessLogItem}, + * which will be used in {@link AccessLogGenerator} to generate + * access log content. + * @param the type of {@linkplain AccessLogItem + * AccessLogParam.contextData}, which usually depends on the transport way. + */ +public interface AccessLogPatternParser { + List> parsePattern(String rawPattern); +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/CompositeVertxRestAccessLogItemMeta.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/CompositeVertxRestAccessLogItemMeta.java new file mode 100644 index 00000000000..6e4c477aa26 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/CompositeVertxRestAccessLogItemMeta.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.common.accessLog.core.parser; + +import java.util.List; + +/** + * Hold a group of {@link VertxRestAccessLogItemMeta} so that user can define + * only one VertxRestAccessLogItemMeta in spi loading file and load a group of meta. + * + * Once the access log loading mechanism finds that a meta is CompositeVertxRestAccessLogItemMeta, + * the meta hold by it will be used in access log while this meta itself will be ignored. + */ +public abstract class CompositeVertxRestAccessLogItemMeta extends VertxRestAccessLogItemMeta { + public abstract List getAccessLogItemMetas(); +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/VertxRestAccessLogItemMeta.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/VertxRestAccessLogItemMeta.java new file mode 100644 index 00000000000..b69b90a9abc --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/VertxRestAccessLogItemMeta.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.accessLog.core.parser; + +import io.vertx.ext.web.RoutingContext; + +/** + * For vertx-rest transport way. + */ +public class VertxRestAccessLogItemMeta extends AccessLogItemMeta { + public VertxRestAccessLogItemMeta() { + } + + public VertxRestAccessLogItemMeta(String prefix, String suffix, + AccessLogItemCreator accessLogItemCreator, int order) { + this.prefix = prefix; + this.suffix = suffix; + this.accessLogItemCreator = accessLogItemCreator; + this.order = order; + } + + public VertxRestAccessLogItemMeta(String prefix, AccessLogItemCreator accessLogItemCreator) { + this(prefix, null, accessLogItemCreator, 0); + } + + public VertxRestAccessLogItemMeta(String prefix, String suffix, + AccessLogItemCreator accessLogItemCreator) { + this(prefix, suffix, accessLogItemCreator, 0); + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/impl/DefaultCompositeVertxRestAccessLogItemMeta.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/impl/DefaultCompositeVertxRestAccessLogItemMeta.java new file mode 100644 index 00000000000..a900c370d51 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/impl/DefaultCompositeVertxRestAccessLogItemMeta.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.common.accessLog.core.parser.impl; + +import java.util.ArrayList; +import java.util.List; + + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.CookieAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.ConfigurableDatetimeAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.DurationMillisecondAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.DurationSecondAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.FirstLineOfRequestAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.HttpMethodAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.HttpStatusAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.InvocationContextAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.LocalHostAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.LocalPortAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.QueryStringAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.RemoteHostAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.RequestHeaderAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.RequestProtocolAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.ResponseHeaderAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.ResponseSizeAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.TraceIdAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.TransportAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.UrlPathAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.UrlPathWithQueryAccessItem; +import org.apache.servicecomb.common.accessLog.core.parser.CompositeVertxRestAccessLogItemMeta; +import org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta; + +import io.vertx.ext.web.RoutingContext; + +public class DefaultCompositeVertxRestAccessLogItemMeta extends CompositeVertxRestAccessLogItemMeta { + private static final List SUPPORTED_META = new ArrayList<>(); + + static { + final AccessLogItem httpMethodItem = new HttpMethodAccessItem(); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%m", config -> httpMethodItem)); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("cs-method", config -> httpMethodItem)); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%s", config -> new HttpStatusAccessItem())); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("sc-status", config -> new HttpStatusAccessItem())); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%T", config -> new DurationSecondAccessItem())); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%D", config -> new DurationMillisecondAccessItem())); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%h", config -> new RemoteHostAccessItem())); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%v", config -> new LocalHostAccessItem())); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%p", config -> new LocalPortAccessItem())); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%B", config -> new ResponseSizeAccessItem("0"))); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%b", config -> new ResponseSizeAccessItem("-"))); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%r", config -> new FirstLineOfRequestAccessItem())); + final AccessLogItem urlPathItem = new UrlPathAccessItem(); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%U", config -> urlPathItem)); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("cs-uri-stem", config -> urlPathItem)); + final AccessLogItem queryStringItem = new QueryStringAccessItem(); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%q", config -> queryStringItem)); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("cs-uri-query", config -> queryStringItem)); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("cs-uri", config -> new UrlPathWithQueryAccessItem())); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%H", config -> new RequestProtocolAccessItem())); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%t", config -> new ConfigurableDatetimeAccessItem())); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%SCB-traceId", config -> new TraceIdAccessItem())); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%SCB-transport", config -> new TransportAccessItem())); + + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}t", ConfigurableDatetimeAccessItem::new)); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}i", RequestHeaderAccessItem::new)); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}o", ResponseHeaderAccessItem::new)); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}C", CookieAccessItem::new)); + SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}SCB-ctx", InvocationContextAccessItem::new)); + } + + @Override + public List getAccessLogItemMetas() { + return SUPPORTED_META; + } +} 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 new file mode 100644 index 00000000000..7a8568b97f7 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/parser/impl/VertxRestAccessLogPatternParser.java @@ -0,0 +1,356 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.parser.impl; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +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; +import org.apache.servicecomb.common.accessLog.core.parser.AccessLogPatternParser; +import org.apache.servicecomb.common.accessLog.core.parser.CompositeVertxRestAccessLogItemMeta; +import org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; + +import io.vertx.ext.web.RoutingContext; + +/** + * The parser is used for rest-over-vertx transport. + */ +public class VertxRestAccessLogPatternParser implements AccessLogPatternParser { + private static final Logger LOGGER = LoggerFactory.getLogger(VertxRestAccessLogPatternParser.class); + + public static final Comparator accessLogItemMetaComparator = (m1, m2) -> { + int result = m1.getOrder() - m2.getOrder(); + if (result != 0) { + return result; + } + + // one of m1 & m2 has suffix, but the other one doesn't have + if (m1.getSuffix() == null ^ m2.getSuffix() == null) { + return m1.getSuffix() == null ? 1 : -1; + } + + if (null != m1.getSuffix()) { + result = comparePlaceholderString(m1.getSuffix(), m2.getSuffix()); + } + + return 0 == result ? + comparePlaceholderString(m1.getPrefix(), m2.getPrefix()) + : result; + }; + + private final List metaList = new ArrayList<>(); + + public VertxRestAccessLogPatternParser() { + List loadedMeta = loadVertxRestLogItemMeta(); + if (null == loadedMeta || loadedMeta.isEmpty()) { + LOGGER.error("cannot load AccessLogItemMeta!"); + throw new IllegalStateException("cannot load AccessLogItemMeta!"); + } + for (VertxRestAccessLogItemMeta meta : loadedMeta) { + if (CompositeVertxRestAccessLogItemMeta.class.isAssignableFrom(meta.getClass())) { + this.metaList.addAll(((CompositeVertxRestAccessLogItemMeta) meta).getAccessLogItemMetas()); + } else { + this.metaList.add(meta); + } + } + sortAccessLogItemMeta(this.metaList); + } + + @VisibleForTesting + List getMetaList() { + return metaList; + } + + @VisibleForTesting + List loadVertxRestLogItemMeta() { + return SPIServiceUtils.getOrLoadSortedService(VertxRestAccessLogItemMeta.class); + } + + /** + * Behavior of this compare: + * 1. comparePlaceholderString("abc","bbc") < 0 + * 2. comparePlaceholderString("abc","ab") < 0 + * 3. comparePlaceholderString("abc","abc") = 0 + */ + public static int comparePlaceholderString(String s1, String s2) { + int result = s1.compareTo(s2); + if (0 == result) { + return result; + } + + // there are two possible cases: + // 1. s1="ab", s2="def" + // 2. s1="ab", s2="abc" + // in the case1 just return the result, but int the case2 the result should be reversed + return result < 0 ? + (s2.startsWith(s1) ? -result : result) + : (s1.startsWith(s2) ? -result : result); + } + + /** + * Sort all of the {@link AccessLogItemMeta}, the meta that is in front of the others has higher priority. + * + * Sort rule(priority decreased): + *
    + *
  1. compare the {@link AccessLogItemMeta#getOrder()}
  2. + *
  3. compare the {@link AccessLogItemMeta#getSuffix()} in lexicographic order, if one's suffix is start with + * the other one's suffix, this one(who's suffix is longer) has higher priority
  4. + *
  5. compare the {@link AccessLogItemMeta#getPrefix()}, compare rule is the same as suffix.
  6. + *
+ * + * e.g. given a list of {@link AccessLogItemMeta} like below: + *
    + *
  1. (%ac{,}bcd)
  2. + *
  3. (%ac{,}bc)
  4. + *
  5. (%ac{,}a)
  6. + *
  7. (%ac,)
  8. + *
  9. (%b,)
  10. + *
  11. (%a)
  12. + *
  13. (%{,}b)
  14. + *
  15. (%{,}bc)
  16. + *
+ * the result is: + *
    + *
  1. (%ac{,}a)
  2. + *
  3. (%ac{,}bcd)
  4. + *
  5. (%ac{,}bc)
  6. + *
  7. (%{,}bc)
  8. + *
  9. (%{,}b)
  10. + *
  11. (%ac,)
  12. + *
  13. (%a)
  14. + *
  15. (%b,)
  16. + *
+ */ + public static void sortAccessLogItemMeta(List accessLogItemMetaList) { + accessLogItemMetaList.sort(accessLogItemMetaComparator); + } + + /** + * @param rawPattern The access log pattern string specified by users. + * @return A list of {@linkplain AccessLogItem} which actually generate the content of access log. + */ + @Override + public List> parsePattern(String rawPattern) { + LOGGER.info("parse the pattern of access log: [{}]", rawPattern); + List locationList = matchAccessLogItem(rawPattern); + locationList = fillInPlainTextLocation(rawPattern, locationList); + + return convertToItemList(rawPattern, locationList); + } + + /** + * Use the {@link #metaList} to match rawPattern. + * Return a list of {@link AccessLogItemLocation}. + * Plain text is ignored. + */ + private List matchAccessLogItem(String rawPattern) { + List locationList = new ArrayList<>(); + int cursor = 0; + while (cursor < rawPattern.length()) { + AccessLogItemLocation candidate = null; + for (VertxRestAccessLogItemMeta meta : metaList) { + if (null != candidate && null == meta.getSuffix()) { + // 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? + + // We've gotten an AccessLogItem with suffix, so there is no need to match those without suffix, + // just break this match loop + cursor = candidate.tail; + break; + } + if (rawPattern.startsWith(meta.getPrefix(), cursor)) { + if (null == meta.getSuffix()) { + // for simple type AccessLogItem, there is no need to try to match the next item. + candidate = new AccessLogItemLocation(cursor, meta); + cursor = candidate.tail; + break; + } + // for configurable type, like %{...}i, more check is needed + // e.g. "%{varName1}o ${varName2}i" should be divided into + // ResponseHeaderItem with varName="varName1" and RequestHeaderItem with varName="varName2" + // INSTEAD OF RequestHeaderItem with varName="varName1}o ${varName2" + int rear = rawPattern.indexOf(meta.getSuffix(), cursor); + if (rear < 0) { + continue; + } + if (null == candidate || rear < candidate.suffixIndex) { + candidate = new AccessLogItemLocation(cursor, rear, meta); + } + // There is a matched item which is in front of this item, so this item is ignored. + } + } + + if (candidate == null) { + ++cursor; + continue; + } + locationList.add(candidate); + } + + return locationList; + } + + /** + * After processing of {@link #matchAccessLogItem(String)}, all of the placeholders of {@link AccessLogItem} have been + * picked out. So the rest part of rawPattern should be treated as plain text. Those parts will be located in this + * method and wrapped as {@link PlainTextAccessItem}. + * @param rawPattern raw pattern string of access log + * @param locationList locations picked out by {@link #matchAccessLogItem(String)} + * @return all of the locations including {@link PlainTextAccessItem}. + */ + private List fillInPlainTextLocation(String rawPattern, + List locationList) { + List resultList = new ArrayList<>(); + if (locationList.isEmpty()) { + resultList.add(createTextPlainItemLocation(0, rawPattern.length())); + return resultList; + } + + Iterator itemLocationIterator = locationList.iterator(); + AccessLogItemLocation previousItemLocation = itemLocationIterator.next(); + if (previousItemLocation.prefixIndex > 0) { + resultList.add(createTextPlainItemLocation(0, previousItemLocation.prefixIndex)); + } + resultList.add(previousItemLocation); + + while (itemLocationIterator.hasNext()) { + AccessLogItemLocation thisItemLocation = itemLocationIterator.next(); + if (previousItemLocation.tail < thisItemLocation.prefixIndex) { + resultList.add(createTextPlainItemLocation(previousItemLocation.tail, thisItemLocation.prefixIndex)); + } + previousItemLocation = thisItemLocation; + resultList.add(previousItemLocation); + } + + if (previousItemLocation.tail < rawPattern.length()) { + resultList.add(createTextPlainItemLocation( + previousItemLocation.tail, + rawPattern.length())); + } + return resultList; + } + + private AccessLogItemLocation createTextPlainItemLocation(int front, int rear) { + return new AccessLogItemLocation(front, rear); + } + + private List> convertToItemList(String rawPattern, + List locationList) { + List> itemList = new ArrayList<>(); + + for (AccessLogItemLocation accessLogItemLocation : locationList) { + VertxRestAccessLogItemMeta accessLogItemMeta = accessLogItemLocation.accessLogItemMeta; + if (null == accessLogItemMeta) { + // a PlainTextItem location + itemList.add(new PlainTextAccessItem(rawPattern.substring( + accessLogItemLocation.prefixIndex, accessLogItemLocation.tail + ))); + continue; + } + + itemList.add(accessLogItemMeta.getAccessLogItemCreator().createItem( + getConfigString(rawPattern, accessLogItemLocation)) + ); + } + + return itemList; + } + + private String getConfigString(String rawPattern, AccessLogItemLocation accessLogItemLocation) { + if (null == accessLogItemLocation.getSuffix()) { + // simple AccessLogItem + return null; + } + + return rawPattern.substring( + accessLogItemLocation.prefixIndex + accessLogItemLocation.getPrefix().length(), + accessLogItemLocation.suffixIndex); + } + + private static class AccessLogItemLocation { + /** + * prefixIndex = rawPattern.indexOf(prefix) + */ + final int prefixIndex; + + /** + * suffixIndex = rawPattern.indexOf(suffix) + */ + final int suffixIndex; + + /** + * tail = suffixIndex + suffix.length() + */ + final int tail; + + VertxRestAccessLogItemMeta accessLogItemMeta; + + /** + * for {@link PlainTextAccessItem} only + */ + AccessLogItemLocation(int prefixIndex, int suffixIndex) { + this.prefixIndex = prefixIndex; + this.suffixIndex = suffixIndex; + this.tail = suffixIndex; + } + + /** + * for configurable type AccessLogItem + */ + AccessLogItemLocation(int prefixIndex, int suffixIndex, VertxRestAccessLogItemMeta accessLogItemMeta) { + this.prefixIndex = prefixIndex; + this.suffixIndex = suffixIndex; + this.tail = suffixIndex + accessLogItemMeta.getSuffix().length(); + this.accessLogItemMeta = accessLogItemMeta; + } + + /** + * for simple type AccessLogItem + */ + AccessLogItemLocation(int prefixIndex, VertxRestAccessLogItemMeta accessLogItemMeta) { + this.prefixIndex = prefixIndex; + this.suffixIndex = prefixIndex + accessLogItemMeta.getPrefix().length(); + this.tail = this.suffixIndex; + this.accessLogItemMeta = accessLogItemMeta; + } + + public String getPrefix() { + if (null == accessLogItemMeta) { + return null; + } + return accessLogItemMeta.getPrefix(); + } + + public String getSuffix() { + if (null == accessLogItemMeta) { + return null; + } + return accessLogItemMeta.getSuffix(); + } + } +} diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/placeholder/AccessLogItemTypeEnum.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/placeholder/AccessLogItemTypeEnum.java new file mode 100644 index 00000000000..5352e37faa9 --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/placeholder/AccessLogItemTypeEnum.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.accessLog.core.placeholder; + +/** + * record what kinds of access log item we support + */ +public enum AccessLogItemTypeEnum { + TEXT_PLAIN, + // %m, cs-method + HTTP_METHOD, + // %s, sc-status + HTTP_STATUS, + // %T + DURATION_IN_SECOND, + // %D + DURATION_IN_MILLISECOND, + // %h + REMOTE_HOSTNAME, + // %v + LOCAL_HOSTNAME, + // %p + LOCAL_PORT, + // %B + RESPONSE_SIZE, + // %b + RESPONSE_SIZE_CLF, + // %r + FIRST_LINE_OF_REQUEST, + // %U, cs-uri-stem + URL_PATH, + // %q, cs-uri-query + QUERY_STRING, + // cs-uri + URL_PATH_WITH_QUERY, + //%H + REQUEST_PROTOCOL, + // %t + DATETIME_DEFAULT, + // %{PATTERN}t, %{PATTERN|TIMEZONE|LOCALE}t + DATETIME_CONFIGURABLE, + // %{VARNAME}i + REQUEST_HEADER, + // %{VARNAME}o + RESPONSE_HEADER, + // %{VARNAME}C + COOKIE, + // %SCB-traceId + SCB_TRACE_ID, + // %{VARNAME}SCB-ctx + SCB_INVOCATION_CONTEXT +} 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 new file mode 100644 index 00000000000..7baf69dd8aa --- /dev/null +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/server/ServerDefaultInitializer.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.accessLog.server; + +import org.apache.servicecomb.common.accessLog.AccessLogConfig; +import org.apache.servicecomb.common.accessLog.AccessLogInitializer; +import org.apache.servicecomb.common.accessLog.core.AccessLogGenerator; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + +public class ServerDefaultInitializer implements AccessLogInitializer { + + private static final Logger LOGGER = LoggerFactory.getLogger("scb-access"); + + private AccessLogGenerator accessLogGenerator; + + @Override + public void init(EventBus eventBus, AccessLogConfig accessLogConfig) { + if (!accessLogConfig.isServerLogEnabled()) { + return; + } + accessLogGenerator = new AccessLogGenerator(accessLogConfig.getServerLogPattern()); + eventBus.register(this); + } + + @Subscribe + @AllowConcurrentEvents + public void onRequestReceived(ServerAccessLogEvent accessLogEvent) { + LOGGER.info(accessLogGenerator.generateServerLog(accessLogEvent)); + } +} diff --git a/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.AccessLogInitializer b/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.AccessLogInitializer new file mode 100644 index 00000000000..159849dab99 --- /dev/null +++ b/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.AccessLogInitializer @@ -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.common.accessLog.client.ClientDefaultInitializer +org.apache.servicecomb.common.accessLog.server.ServerDefaultInitializer 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 new file mode 100644 index 00000000000..f793ca0f575 --- /dev/null +++ b/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta @@ -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.core.parser.impl.DefaultCompositeVertxRestAccessLogItemMeta 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/AccessLogGeneratorTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogGeneratorTest.java new file mode 100644 index 00000000000..e8c7cf38500 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogGeneratorTest.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.mockito.Mockito.when; + +import java.text.SimpleDateFormat; +import java.util.TimeZone; + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.ConfigurableDatetimeAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.HttpMethodAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.PlainTextAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.RemoteHostAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.UserDefinedAccessLogItem; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.ext.web.RoutingContext; + +public class AccessLogGeneratorTest { + + private static final AccessLogGenerator LOG_GENERATOR = new AccessLogGenerator("%m - %t"); + + private static final AccessLogGenerator USER_DEFINED_LOG_GENERATOR = new AccessLogGenerator( + "%h - - %{test-config}user-defined"); + + @Test + public void testConstructor() { + AccessLogItem[] elements = LOG_GENERATOR.getAccessLogItems(); + Assertions.assertEquals(3, elements.length); + Assertions.assertEquals(HttpMethodAccessItem.class, elements[0].getClass()); + Assertions.assertEquals(PlainTextAccessItem.class, elements[1].getClass()); + Assertions.assertEquals(ConfigurableDatetimeAccessItem.class, elements[2].getClass()); + } + + @Test + public void testServerLog() { + RoutingContext context = Mockito.mock(RoutingContext.class); + HttpServerRequest request = Mockito.mock(HttpServerRequest.class); + long startMillisecond = 1416863450581L; + ServerAccessLogEvent serverAccessLogEvent = new ServerAccessLogEvent(); + serverAccessLogEvent.setMilliStartTime(startMillisecond).setRoutingContext(context); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, + ConfigurableDatetimeAccessItem.DEFAULT_LOCALE); + simpleDateFormat.setTimeZone(TimeZone.getDefault()); + + when(context.request()).thenReturn(request); + when(request.method()).thenReturn(HttpMethod.DELETE); + + String log = LOG_GENERATOR.generateServerLog(serverAccessLogEvent); + Assertions.assertEquals("DELETE" + " - " + simpleDateFormat.format(startMillisecond), log); + } + + @Test + public void testClientLog() { + Invocation invocation = Mockito.mock(Invocation.class); + InvocationStageTrace stageTrace = Mockito.mock(InvocationStageTrace.class); + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + long startMillisecond = 1416863450581L; + when(stageTrace.getStartInMillis()).thenReturn(startMillisecond); + when(stageTrace.calcTotal()).thenReturn(0L); + when(invocation.getOperationMeta()).thenReturn(operationMeta); + when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); + + InvocationFinishEvent finishEvent = new InvocationFinishEvent(invocation, null); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, + ConfigurableDatetimeAccessItem.DEFAULT_LOCALE); + simpleDateFormat.setTimeZone(TimeZone.getDefault()); + when(operationMeta.getHttpMethod()).thenReturn(HttpMethod.DELETE.toString()); + String log = LOG_GENERATOR.generateClientLog(finishEvent); + Assertions.assertEquals("DELETE" + " - " + simpleDateFormat.format(startMillisecond), log); + } + + @Test + public void testUserDefinedLogGenerator() { + AccessLogItem[] elements = USER_DEFINED_LOG_GENERATOR.getAccessLogItems(); + Assertions.assertEquals(3, elements.length); + Assertions.assertEquals(RemoteHostAccessItem.class, elements[0].getClass()); + Assertions.assertEquals(PlainTextAccessItem.class, elements[1].getClass()); + Assertions.assertEquals(UserDefinedAccessLogItem.class, elements[2].getClass()); + } +} 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 new file mode 100644 index 00000000000..f1d37e9681f --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieItemTest.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.element.impl; + +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +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; + +public class CookieItemTest { + + public static final String COOKIE_NAME = "cookieName"; + + public static final String COOKIE_VALUE = "cookieVALUE"; + + private static final CookieAccessItem ELEMENT = new CookieAccessItem(COOKIE_NAME); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext mockContext; + + private HttpServerRequest httpServerRequest; + + private Invocation invocation; + + private RestClientRequestParameters restClientRequest; + + @BeforeEach + public void initStrBuilder() { + mockContext = Mockito.mock(RoutingContext.class); + httpServerRequest = Mockito.mock(HttpServerRequest.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); + + accessLogEvent = new ServerAccessLogEvent(); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElement() { + HashSet cookieSet = new HashSet<>(); + CookieImpl cookie = new CookieImpl(COOKIE_NAME, COOKIE_VALUE); + + cookieSet.add(cookie); + Mockito.when(mockContext.request()).thenReturn(httpServerRequest); + Mockito.when(httpServerRequest.cookies()).thenReturn(cookieSet); + accessLogEvent.setRoutingContext(mockContext); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals(COOKIE_VALUE, strBuilder.toString()); + } + + @Test + public void clientFormattedElement() { + Map handlerMap = new HashMap<>(); + handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + + Map cookieMap = new HashMap<>(); + cookieMap.put(COOKIE_NAME, COOKIE_VALUE); + + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerMap); + when(restClientRequest.getCookieMap()).thenReturn(cookieMap); + + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals(COOKIE_VALUE, strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnCookieCountIsZero() { + HashSet cookieSet = new HashSet<>(); + Mockito.when(mockContext.request()).thenReturn(httpServerRequest); + Mockito.when(httpServerRequest.cookies()).thenReturn(cookieSet); + accessLogEvent.setRoutingContext(mockContext); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnCookieCountIsZero() { + Map handlerMap = new HashMap<>(); + handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + Map cookieMap = new HashMap<>(); + + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerMap); + when(restClientRequest.getCookieMap()).thenReturn(cookieMap); + + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + + @Test + public void serverFormattedElementOnCookieSetIsNull() { + Mockito.when(mockContext.request()).thenReturn(httpServerRequest); + Mockito.when(httpServerRequest.cookies()).thenReturn(null); + accessLogEvent.setRoutingContext(mockContext); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnCookieSetIsNull() { + Map handlerMap = new HashMap<>(); + handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerMap); + when(restClientRequest.getCookieMap()).thenReturn(null); + + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + + @Test + public void serverFormattedElementOnNotFound() { + HashSet cookieSet = new HashSet<>(); + CookieImpl cookie = new CookieImpl("anotherCookieName", COOKIE_VALUE); + cookieSet.add(cookie); + Mockito.when(mockContext.request()).thenReturn(httpServerRequest); + Mockito.when(httpServerRequest.cookies()).thenReturn(cookieSet); + accessLogEvent.setRoutingContext(mockContext); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnNotFound() { + Map handlerMap = new HashMap<>(); + handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + + Map cookieMap = new HashMap<>(); + cookieMap.put("anotherCookieValue", COOKIE_VALUE); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerMap); + when(restClientRequest.getCookieMap()).thenReturn(cookieMap); + + ELEMENT.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/DatetimeConfigurableItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DatetimeConfigurableItemTest.java new file mode 100644 index 00000000000..8f55eb0006c --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DatetimeConfigurableItemTest.java @@ -0,0 +1,181 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.element.impl; + +import static org.mockito.Mockito.when; + +import java.text.SimpleDateFormat; +import java.util.Locale; +import java.util.TimeZone; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class DatetimeConfigurableItemTest { + + private static final long START_MILLISECOND = 1416863450581L; + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private Invocation invocation; + + private InvocationStageTrace invocationStageTrace; + + @BeforeEach + public void initStrBuilder() { + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + invocationStageTrace = Mockito.mock(InvocationStageTrace.class); + + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); + when(invocationStageTrace.getStartInMillis()).thenReturn(START_MILLISECOND); + when(invocationStageTrace.calcTotal()).thenReturn(0L); + + accessLogEvent = new ServerAccessLogEvent(); + accessLogEvent.setMilliStartTime(START_MILLISECOND); + strBuilder = new StringBuilder(); + } + + + @Test + public void getFormattedElementOnNoTimezone() { + ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( + "yyyy/MM/dd zzz||zh-CN"); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd zzz", Locale.forLanguageTag("zh-CN")); + simpleDateFormat.setTimeZone(TimeZone.getDefault()); + + element.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnNoTimezone() { + ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( + "yyyy/MM/dd zzz||zh-CN"); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd zzz", Locale.forLanguageTag("zh-CN")); + simpleDateFormat.setTimeZone(TimeZone.getDefault()); + + element.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnNoLocale() { + ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( + "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()); + } + + @Test + public void clientFormattedElementOnNoLocale() { + ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( + "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()); + } + + @Test + public void serverFormattedElementOnNoConfig() { + ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( + "||"); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, + Locale.US); + simpleDateFormat.setTimeZone(TimeZone.getDefault()); + + element.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnNoConfig() { + ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( + "||"); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, + Locale.US); + simpleDateFormat.setTimeZone(TimeZone.getDefault()); + + element.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); + } + + @Test + public void serverConstructorWithNoArg() { + ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem(); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); + simpleDateFormat.setTimeZone(TimeZone.getDefault()); + + element.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("EEE, dd MMM yyyy HH:mm:ss zzz", element.getPattern()); + Assertions.assertEquals(Locale.US, element.getLocale()); + Assertions.assertEquals(TimeZone.getDefault(), element.getTimezone()); + Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); + } + + @Test + public void clientConstructorWithNoArg() { + ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem(); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); + simpleDateFormat.setTimeZone(TimeZone.getDefault()); + + element.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("EEE, dd MMM yyyy HH:mm:ss zzz", element.getPattern()); + Assertions.assertEquals(Locale.US, element.getLocale()); + Assertions.assertEquals(TimeZone.getDefault(), element.getTimezone()); + Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); + } + + @Test + public void serverConstructorWithNoSeparator() { + ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem("yyyy/MM/dd HH:mm:ss zzz"); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss zzz", Locale.US); + simpleDateFormat.setTimeZone(TimeZone.getDefault()); + + element.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("yyyy/MM/dd HH:mm:ss zzz", element.getPattern()); + Assertions.assertEquals(Locale.US, element.getLocale()); + Assertions.assertEquals(TimeZone.getDefault(), element.getTimezone()); + Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); + } + + @Test + public void clientConstructorWithNoSeparator() { + ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem("yyyy/MM/dd HH:mm:ss zzz"); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss zzz", Locale.US); + simpleDateFormat.setTimeZone(TimeZone.getDefault()); + + element.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("yyyy/MM/dd HH:mm:ss zzz", element.getPattern()); + Assertions.assertEquals(Locale.US, element.getLocale()); + Assertions.assertEquals(TimeZone.getDefault(), element.getTimezone()); + Assertions.assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString()); + } +} 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 new file mode 100644 index 00000000000..1fdd3b4e0d0 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondItemTest.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.accessLog.core.element.impl; + +import static org.mockito.Mockito.when; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class DurationMillisecondItemTest { + + public static final DurationMillisecondAccessItem ELEMENT = new DurationMillisecondAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private Invocation invocation; + + private InvocationStageTrace invocationStageTrace; + + @BeforeEach + public void initStrBuilder() { + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + invocationStageTrace = Mockito.mock(InvocationStageTrace.class); + + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); + when(invocationStageTrace.calcTotal()).thenReturn(1000_000L); + + accessLogEvent = new ServerAccessLogEvent(); + accessLogEvent.setMilliStartTime(1L); + accessLogEvent.setMilliEndTime(2L); + strBuilder = new StringBuilder(); + } + + @Test + public void testAppendFormattedElement() { + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("1", strBuilder.toString()); + + strBuilder = new StringBuilder(); + 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/DurationSecondItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondItemTest.java new file mode 100644 index 00000000000..9cbe7f28da5 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondItemTest.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.common.accessLog.core.element.impl; + +import static org.mockito.Mockito.when; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class DurationSecondItemTest { + + public static final DurationSecondAccessItem ELEMENT = new DurationSecondAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private Invocation invocation; + + private InvocationStageTrace invocationStageTrace; + + @BeforeEach + public void initStrBuilder() { + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + invocationStageTrace = Mockito.mock(InvocationStageTrace.class); + + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); + when(invocationStageTrace.calcTotal()).thenReturn(1L); + + accessLogEvent = new ServerAccessLogEvent(); + accessLogEvent.setMilliStartTime(1L); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElementOn999ms() { + accessLogEvent.setMilliEndTime(1000L); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("0", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOn999ms() { + when(invocationStageTrace.calcTotal()).thenReturn(0L); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("0", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOn1000ms() { + accessLogEvent.setMilliEndTime(1001L); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("1", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOn1000ms() { + when(invocationStageTrace.calcTotal()).thenReturn(1000_000_000L); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("1", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOn1001ms() { + accessLogEvent.setMilliEndTime(1002L); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("1", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOn1001ms() { + 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 new file mode 100644 index 00000000000..54e1fccef33 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/FirstLineOfRequestItemTest.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.element.impl; + +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +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; +import org.mockito.Mockito; + +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpVersion; +import io.vertx.ext.web.RoutingContext; + +public class FirstLineOfRequestItemTest { + + public static final FirstLineOfRequestAccessItem ELEMENT = new FirstLineOfRequestAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext mockContext; + + private Invocation invocation; + + private RestClientRequestParameters restClientRequest; + + private HttpClientRequest clientRequest; + + private Endpoint endpoint; + + private URIEndpointObject urlEndpoint; + + @BeforeEach + public void initStrBuilder() { + mockContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); + clientRequest = Mockito.mock(HttpClientRequest.class); + endpoint = Mockito.mock(Endpoint.class); + urlEndpoint = Mockito.mock(URIEndpointObject.class); + Map handlerMap = new HashMap<>(); + handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerMap); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(invocation.getEndpoint()).thenReturn(endpoint); + when(endpoint.getAddress()).thenReturn(urlEndpoint); + accessLogEvent = new ServerAccessLogEvent(); + accessLogEvent.setRoutingContext(mockContext); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElement() { + HttpServerRequest request = Mockito.mock(HttpServerRequest.class); + String uri = "/test/uri"; + + when(mockContext.request()).thenReturn(request); + when(request.method()).thenReturn(HttpMethod.DELETE); + when(request.path()).thenReturn(uri); + when(request.version()).thenReturn(HttpVersion.HTTP_1_1); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("\"DELETE " + uri + " HTTP/1.1\"", strBuilder.toString()); + } + + @Test + public void clientFormattedElement() { + String uri = "/test/uri"; + when(clientRequest.getMethod()).thenReturn(HttpMethod.DELETE); + when(clientRequest.path()).thenReturn(uri); + when(urlEndpoint.isHttp2Enabled()).thenReturn(true); + + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("\"DELETE " + uri + " HTTP/2.0\"", strBuilder.toString()); + } +} 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 new file mode 100644 index 00000000000..1dbeba59e9a --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodItemTest.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.element.impl; + +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +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; +import org.mockito.Mockito; + +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.ext.web.RoutingContext; + +public class HttpMethodItemTest { + private static final HttpMethodAccessItem ITEM = new HttpMethodAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private Invocation invocation; + + private RestClientRequestParameters restClientRequest; + + private HttpClientRequest clientRequest; + + private Endpoint endpoint; + + private URIEndpointObject urlEndpoint; + + @BeforeEach + public void initStrBuilder() { + routingContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); + clientRequest = Mockito.mock(HttpClientRequest.class); + endpoint = Mockito.mock(Endpoint.class); + urlEndpoint = Mockito.mock(URIEndpointObject.class); + Map handlerMap = new HashMap<>(); + handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerMap); + when(invocation.getEndpoint()).thenReturn(endpoint); + when(endpoint.getAddress()).thenReturn(urlEndpoint); + accessLogEvent = new ServerAccessLogEvent(); + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElement() { + HttpServerRequest request = Mockito.mock(HttpServerRequest.class); + Mockito.when(routingContext.request()).thenReturn(request); + Mockito.when(request.method()).thenReturn(HttpMethod.DELETE); + accessLogEvent.setRoutingContext(routingContext); + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("DELETE", strBuilder.toString()); + } + + @Test + public void clientFormattedElement() { + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(clientRequest.getMethod()).thenReturn(HttpMethod.DELETE); + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("DELETE", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnRequestIsNull() { + accessLogEvent.setRoutingContext(routingContext); + Mockito.when(routingContext.request()).thenReturn(null); + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnRequestIsNull() { + when(restClientRequest.getHttpClientRequest()).thenReturn(null); + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + + @Test + public void serverFormattedElementOnMethodIsNull() { + HttpServerRequest request = Mockito.mock(HttpServerRequest.class); + accessLogEvent.setRoutingContext(routingContext); + + Mockito.when(routingContext.request()).thenReturn(request); + Mockito.when(request.method()).thenReturn(null); + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnMethodIsNull() { + when(clientRequest.getMethod()).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/HttpStatusItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpStatusItemTest.java new file mode 100644 index 00000000000..749a38416d8 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpStatusItemTest.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.common.accessLog.core.element.impl; + +import static org.mockito.Mockito.when; + +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.swagger.invocation.Response; +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.HttpServerResponse; +import io.vertx.ext.web.RoutingContext; + +public class HttpStatusItemTest { + + private static final HttpStatusAccessItem STATUS_ELEMENT = new HttpStatusAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private Response response; + + private HttpServerResponse serverResponse; + + @BeforeEach + public void initStrBuilder() { + routingContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + response = Mockito.mock(Response.class); + serverResponse = Mockito.mock(HttpServerResponse.class); + + accessLogEvent = new ServerAccessLogEvent(); + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElement() { + int statusCode = 200; + when(routingContext.response()).thenReturn(serverResponse); + when(serverResponse.getStatusCode()).thenReturn(statusCode); + + STATUS_ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("200", strBuilder.toString()); + } + + @Test + public void clientFormattedElement() { + int statusCode = 200; + when(finishEvent.getResponse()).thenReturn(response); + when(response.getStatusCode()).thenReturn(statusCode); + + STATUS_ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("200", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnResponseIsNull() { + Mockito.when(routingContext.response()).thenReturn(null); + STATUS_ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + + Mockito.when(routingContext.response()).thenReturn(serverResponse); + Mockito.when(serverResponse.closed()).thenReturn(true); + Mockito.when(serverResponse.ended()).thenReturn(false); + + strBuilder = new StringBuilder(); + STATUS_ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnResponseIsNull() { + when(finishEvent.getResponse()).thenReturn(null); + STATUS_ELEMENT.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/InvocationContextItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/InvocationContextItemTest.java new file mode 100644 index 00000000000..644ac99de8f --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/InvocationContextItemTest.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.element.impl; + +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import io.vertx.ext.web.RoutingContext; + +public class InvocationContextItemTest { + + public static final String INVOCATION_CONTEXT_KEY = "testKey"; + + public static final String INVOCATION_CONTEXT_VALUE = "testValue"; + + private static final InvocationContextAccessItem ITEM = new InvocationContextAccessItem(INVOCATION_CONTEXT_KEY); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private Invocation invocation; + + @BeforeEach + public void initStrBuilder() { + accessLogEvent = new ServerAccessLogEvent(); + routingContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + } + + @Test + public void serverGetFormattedItem() { + Map routingContextData = new HashMap<>(); + when(routingContext.data()).thenReturn(routingContextData); + routingContextData.put(RestConst.REST_INVOCATION_CONTEXT, invocation); + when(invocation.getContext(INVOCATION_CONTEXT_KEY)).thenReturn(INVOCATION_CONTEXT_VALUE); + + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(INVOCATION_CONTEXT_VALUE)); + } + + @Test + public void clientGetFormattedItem() { + Map context = new HashMap<>(); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getContext()).thenReturn(context); + context.put(INVOCATION_CONTEXT_KEY, INVOCATION_CONTEXT_VALUE); + + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(INVOCATION_CONTEXT_VALUE)); + } + + @Test + public void serverGetFormattedItemOnInvocationContextValueNotFound() { + Map routingContextData = new HashMap<>(); + Invocation invocation = Mockito.mock(Invocation.class); + when(routingContext.data()).thenReturn(routingContextData); + routingContextData.put(RestConst.REST_INVOCATION_CONTEXT, invocation); + when(invocation.getContext(INVOCATION_CONTEXT_KEY)).thenReturn(null); + + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); + } + + @Test + public void clientGetFormattedItemOnInvocationContextValueNotFound() { + Map context = new HashMap<>(); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getContext()).thenReturn(context); + context.put(INVOCATION_CONTEXT_KEY, null); + + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); + } + + @Test + public void serverGetFormattedItemOnInvocationNotFound() { + Map routingContextData = new HashMap<>(); + when(routingContext.data()).thenReturn(routingContextData); + + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); + } + + @Test + public void clientGetFormattedItemOnInvocationContextNotFound() { + Map context = new HashMap<>(); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getContext()).thenReturn(context); + + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); + } + + @Test + public void testGetFormattedItemOnRoutingContextDataNotFound() { + when(routingContext.data()).thenReturn(null); + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); + } + + @Test + public void clientGetFormattedItemOnRoutingContextDataNotFound() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getContext()).thenReturn(null); + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); + } + + @Test + public void clientGetFormattedItemOnInvocationNotFound() { + when(finishEvent.getInvocation()).thenReturn(null); + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextAccessItem.NOT_FOUND)); + } +} 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 new file mode 100644 index 00000000000..d1669f0316c --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostItemTest.java @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.accessLog.core.element.impl; + +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +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.HttpClientRequest; +import io.vertx.core.http.HttpConnection; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.net.SocketAddress; +import io.vertx.ext.web.RoutingContext; + +public class LocalHostItemTest { + public static final LocalHostAccessItem ELEMENT = new LocalHostAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private HttpServerRequest serverRequest; + + private SocketAddress socketAddress; + + private Invocation invocation; + + private RestClientRequestParameters restClientRequest; + + private HttpClientRequest clientRequest; + + private HttpConnection connection; + + @BeforeEach + public void initStrBuilder() { + accessLogEvent = new ServerAccessLogEvent(); + routingContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + serverRequest = Mockito.mock(HttpServerRequest.class); + socketAddress = Mockito.mock(SocketAddress.class); + invocation = Mockito.mock(Invocation.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); + clientRequest = Mockito.mock(HttpClientRequest.class); + connection = Mockito.mock(HttpConnection.class); + Map handlerMap = new HashMap<>(); + handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerMap); + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + } + + @Test + public void clientFormattedItem() { + String localAddress = "192.168.0.1"; + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(clientRequest.connection()).thenReturn(connection); + when(connection.localAddress()).thenReturn(socketAddress); + when(socketAddress.host()).thenReturn(localAddress); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals(localAddress, strBuilder.toString()); + } + + @Test + public void serverFormattedItem() { + String localAddress = "192.168.0.1"; + accessLogEvent.setLocalAddress(localAddress); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals(localAddress, strBuilder.toString()); + } + + @Test + public void getLocalAddress() { + String localHost = "testHost"; + Mockito.when(routingContext.request()).thenReturn(serverRequest); + Mockito.when(serverRequest.localAddress()).thenReturn(socketAddress); + Mockito.when(socketAddress.host()).thenReturn(localHost); + + String result = LocalHostAccessItem.getLocalAddress(routingContext); + Assertions.assertEquals(localHost, result); + } + + @Test + public void serverLocalAddressOnRequestIsNull() { + Mockito.when(routingContext.request()).thenReturn(null); + String result = LocalHostAccessItem.getLocalAddress(routingContext); + Assertions.assertEquals("-", result); + } + + @Test + public void clientLocalAddressOnRequestIsNull() { + when(restClientRequest.getHttpClientRequest()).thenReturn(null); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverLocalAddressOnLocalAddressIsNull() { + Mockito.when(routingContext.request()).thenReturn(serverRequest); + Mockito.when(serverRequest.localAddress()).thenReturn(null); + String result = LocalHostAccessItem.getLocalAddress(routingContext); + Assertions.assertEquals("-", result); + } + + @Test + public void clientLocalAddressOnLocalAddressIsNull() { + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(clientRequest.connection()).thenReturn(connection); + when(connection.localAddress()).thenReturn(null); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverLocalAddressOnHostIsNull() { + Mockito.when(routingContext.request()).thenReturn(serverRequest); + Mockito.when(serverRequest.localAddress()).thenReturn(socketAddress); + Mockito.when(socketAddress.host()).thenReturn(null); + + String result = LocalHostAccessItem.getLocalAddress(routingContext); + Assertions.assertEquals("-", result); + } + + @Test + public void clientLocalAddressOnHostIsNull() { + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(clientRequest.connection()).thenReturn(connection); + when(connection.localAddress()).thenReturn(socketAddress); + when(socketAddress.host()).thenReturn(null); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverLocalAddressIsEmpty() { + String localHost = ""; + Mockito.when(routingContext.request()).thenReturn(serverRequest); + Mockito.when(serverRequest.localAddress()).thenReturn(socketAddress); + Mockito.when(socketAddress.host()).thenReturn(localHost); + + String result = LocalHostAccessItem.getLocalAddress(routingContext); + Assertions.assertEquals("-", result); + } + + @Test + public void clientLocalAddressIsEmpty() { + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(clientRequest.connection()).thenReturn(connection); + when(connection.localAddress()).thenReturn(socketAddress); + when(socketAddress.host()).thenReturn(""); + ELEMENT.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/LocalPortItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortItemTest.java new file mode 100644 index 00000000000..f77c51fbe74 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortItemTest.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.common.accessLog.core.element.impl; + +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +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.HttpClientRequest; +import io.vertx.core.http.HttpConnection; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.net.SocketAddress; +import io.vertx.ext.web.RoutingContext; + +public class LocalPortItemTest { + public static final LocalPortAccessItem ELEMENT = new LocalPortAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private HttpServerRequest serverRequest; + + private SocketAddress socketAddress; + + private Invocation invocation; + + private RestClientRequestParameters restClientRequest; + + private HttpClientRequest clientRequest; + + private HttpConnection connection; + + @BeforeEach + public void initStrBuilder() { + accessLogEvent = new ServerAccessLogEvent(); + routingContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + serverRequest = Mockito.mock(HttpServerRequest.class); + socketAddress = Mockito.mock(SocketAddress.class); + invocation = Mockito.mock(Invocation.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); + clientRequest = Mockito.mock(HttpClientRequest.class); + connection = Mockito.mock(HttpConnection.class); + Map handlerMap = new HashMap<>(); + handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerMap); + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElement() { + Mockito.when(routingContext.request()).thenReturn(serverRequest); + Mockito.when(serverRequest.localAddress()).thenReturn(socketAddress); + Mockito.when(socketAddress.port()).thenReturn(8080); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("8080", strBuilder.toString()); + } + + @Test + public void clientFormattedElement() { + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(clientRequest.connection()).thenReturn(connection); + when(connection.localAddress()).thenReturn(socketAddress); + when(socketAddress.port()).thenReturn(8080); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("8080", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnRequestIsNull() { + Mockito.when(routingContext.request()).thenReturn(null); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnRequestIsNull() { + when(restClientRequest.getHttpClientRequest()).thenReturn(null); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnLocalAddressIsNull() { + Mockito.when(routingContext.request()).thenReturn(serverRequest); + Mockito.when(serverRequest.localAddress()).thenReturn(null); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnLocalAddressIsNull() { + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(clientRequest.connection()).thenReturn(connection); + when(connection.localAddress()).thenReturn(null); + ELEMENT.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/PlainTextItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/PlainTextItemTest.java new file mode 100644 index 00000000000..b49b7562669 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/PlainTextItemTest.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.common.accessLog.core.element.impl; + +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class PlainTextItemTest { + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private StringBuilder strBuilder; + + @BeforeEach + public void initStrBuilder() { + accessLogEvent = new ServerAccessLogEvent(); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElement() { + PlainTextAccessItem element = new PlainTextAccessItem("contentTest"); + element.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("contentTest", strBuilder.toString()); + } + + @Test + public void clientFormattedElement() { + PlainTextAccessItem element = new PlainTextAccessItem("contentTest"); + element.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("contentTest", strBuilder.toString()); + } +} 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 new file mode 100644 index 00000000000..ef472623d43 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringItemTest.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.element.impl; + +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +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.HttpClientRequest; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.ext.web.RoutingContext; + +public class QueryStringItemTest { + private static final QueryStringAccessItem ITEM = new QueryStringAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private Invocation invocation; + + private RestClientRequestParameters restClientRequest; + + private HttpServerRequest serverRequest; + + private HttpClientRequest clientRequest; + + @BeforeEach + public void initStrBuilder() { + routingContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + serverRequest = Mockito.mock(HttpServerRequest.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); + clientRequest = Mockito.mock(HttpClientRequest.class); + + accessLogEvent = new ServerAccessLogEvent(); + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElement() { + String query = "?status=up"; + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.query()).thenReturn(query); + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals(query, strBuilder.toString()); + } + + @Test + public void clientFormattedElement() { + String query = "?status=up"; + Map handlerContext = new HashMap<>(); + handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerContext); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(clientRequest.query()).thenReturn(query); + + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals(query, strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnRequestIsNull() { + when(routingContext.request()).thenReturn(null); + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnRequestIsNull() { + Map handlerContext = new HashMap<>(); + handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerContext); + when(restClientRequest.getHttpClientRequest()).thenReturn(null); + + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnQueryIsNull() { + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.query()).thenReturn(null); + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnQueryIsNull() { + Map handlerContext = new HashMap<>(); + handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerContext); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(clientRequest.query()).thenReturn(null); + + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnQueryIsEmpty() { + String query = ""; + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.query()).thenReturn(query); + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnQueryIsEmpty() { + String query = ""; + Map handlerContext = new HashMap<>(); + handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerContext); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(clientRequest.query()).thenReturn(query); + + 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/RemoteHostItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RemoteHostItemTest.java new file mode 100644 index 00000000000..096e40f805a --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RemoteHostItemTest.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.common.accessLog.core.element.impl; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +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 io.vertx.core.http.HttpServerRequest; +import io.vertx.core.net.SocketAddress; +import io.vertx.ext.web.RoutingContext; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class RemoteHostItemTest { + public static final RemoteHostAccessItem ELEMENT = new RemoteHostAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private Invocation invocation; + + private HttpServerRequest serverRequest; + + private Endpoint endpoint; + + private URIEndpointObject uriEndpointObject; + + private SocketAddress socketAddress; + + @BeforeEach + public void initStrBuilder() { + routingContext = mock(RoutingContext.class); + finishEvent = mock(InvocationFinishEvent.class); + invocation = mock(Invocation.class); + serverRequest = mock(HttpServerRequest.class); + endpoint = mock(Endpoint.class); + uriEndpointObject = mock(URIEndpointObject.class); + socketAddress = mock(SocketAddress.class); + accessLogEvent = new ServerAccessLogEvent(); + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElement() { + String remoteHost = "remoteHost"; + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.remoteAddress()).thenReturn(socketAddress); + when(socketAddress.host()).thenReturn(remoteHost); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals(remoteHost, strBuilder.toString()); + } + + @Test + public void clientFormattedElement() { + String remoteHost = "remoteHost"; + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getEndpoint()).thenReturn(endpoint); + when(endpoint.getAddress()).thenReturn(uriEndpointObject); + when(uriEndpointObject.getHostOrIp()).thenReturn(remoteHost); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals(remoteHost, strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnRequestIsNull() { + when(routingContext.request()).thenReturn(null); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnRequestIsNull() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getEndpoint()).thenReturn(null); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnRemoteAddressIsNull() { + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.remoteAddress()).thenReturn(null); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnRemoteAddressIsNull() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getEndpoint()).thenReturn(endpoint); + when(endpoint.getAddress()).thenReturn(null); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnHostIsNull() { + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.remoteAddress()).thenReturn(socketAddress); + when(socketAddress.host()).thenReturn(null); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnHostIsNull() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getEndpoint()).thenReturn(endpoint); + when(endpoint.getAddress()).thenReturn(uriEndpointObject); + when(uriEndpointObject.getHostOrIp()).thenReturn(null); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnHostIsEmpty() { + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.remoteAddress()).thenReturn(socketAddress); + when(socketAddress.host()).thenReturn(""); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnHostIsEmpty() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getEndpoint()).thenReturn(endpoint); + when(endpoint.getAddress()).thenReturn(uriEndpointObject); + when(uriEndpointObject.getHostOrIp()).thenReturn(""); + ELEMENT.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/RequestHeaderItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderItemTest.java new file mode 100644 index 00000000000..f2ae7004251 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderItemTest.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.common.accessLog.core.element.impl; + +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +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.MultiMap; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.impl.headers.HeadersMultiMap; +import io.vertx.ext.web.RoutingContext; + +public class RequestHeaderItemTest { + private static final String VAR_NAME = "varName"; + + private static final RequestHeaderAccessItem ELEMENT = new RequestHeaderAccessItem(VAR_NAME); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private Invocation invocation; + + private RestClientRequestParameters restClientRequest; + + private HttpServerRequest serverRequest; + + private HttpClientRequest clientRequest; + + private MultiMap headers; + + @BeforeEach + public void initStrBuilder() { + routingContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + serverRequest = Mockito.mock(HttpServerRequest.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); + clientRequest = Mockito.mock(HttpClientRequest.class); + headers = Mockito.mock(MultiMap.class); + + accessLogEvent = new ServerAccessLogEvent(); + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElement() { + HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); + String testValue = "testValue"; + headers.add(VAR_NAME, testValue); + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.headers()).thenReturn(headers); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals(testValue, strBuilder.toString()); + Assertions.assertEquals(ELEMENT.getVarName(), VAR_NAME); + } + + @Test + public void clientFormattedElement() { + Map handlerContext = new HashMap<>(); + String testValue = "testValue"; + + handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerContext); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(clientRequest.headers()).thenReturn(headers); + when(headers.get(VAR_NAME)).thenReturn(testValue); + + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals(testValue, strBuilder.toString()); + } + + @Test + public void serverFormattedElementIfHeaderIsNull() { + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.headers()).thenReturn(null); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementIfHeaderIsNull() { + Map handlerContext = new HashMap<>(); + handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerContext); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(clientRequest.headers()).thenReturn(null); + + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverFormattedElementIfNotFound() { + HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); + String testValue = "testValue"; + headers.add("anotherKey", testValue); + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.headers()).thenReturn(headers); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } +} diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestProtocolItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestProtocolItemTest.java new file mode 100644 index 00000000000..020df94aeb2 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestProtocolItemTest.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.accessLog.core.element.impl; + +import static org.mockito.Mockito.when; + +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.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.HttpServerRequest; +import io.vertx.core.http.HttpVersion; +import io.vertx.ext.web.RoutingContext; + +public class RequestProtocolItemTest { + private static final RequestProtocolAccessItem ITEM = new RequestProtocolAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private Invocation invocation; + + private HttpServerRequest serverRequest; + + private Endpoint endpoint; + + private URIEndpointObject urlEndpoint; + + @BeforeEach + public void initStrBuilder() { + routingContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + serverRequest = Mockito.mock(HttpServerRequest.class); + urlEndpoint = Mockito.mock(URIEndpointObject.class); + endpoint = Mockito.mock(Endpoint.class); + + accessLogEvent = new ServerAccessLogEvent(); + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElement() { + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.version()).thenReturn(HttpVersion.HTTP_1_1); + + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("HTTP/1.1", strBuilder.toString()); + + strBuilder = new StringBuilder(); + when(serverRequest.version()).thenReturn(HttpVersion.HTTP_1_0); + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("HTTP/1.0", strBuilder.toString()); + + strBuilder = new StringBuilder(); + when(serverRequest.version()).thenReturn(HttpVersion.HTTP_2); + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("HTTP/2.0", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnRequestIsNull() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getEndpoint()).thenReturn(endpoint); + when(endpoint.getAddress()).thenReturn(urlEndpoint); + when(urlEndpoint.isHttp2Enabled()).thenReturn(true); + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("HTTP/2.0", strBuilder.toString()); + + strBuilder = new StringBuilder(); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getEndpoint()).thenReturn(endpoint); + when(endpoint.getAddress()).thenReturn(urlEndpoint); + when(urlEndpoint.isHttp2Enabled()).thenReturn(false); + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("HTTP/1.1", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnVersionIsNull() { + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.version()).thenReturn(null); + + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnVersionIsNull() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getEndpoint()).thenReturn(endpoint); + when(endpoint.getAddress()).thenReturn(null); + + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("HTTP/1.1", strBuilder.toString()); + } +} 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 new file mode 100644 index 00000000000..a8708172945 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderItemTest.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.common.accessLog.core.element.impl; + +import static org.mockito.Mockito.when; + +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.swagger.invocation.Response; +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.MultiMap; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.http.impl.headers.HeadersMultiMap; +import io.vertx.ext.web.RoutingContext; + +public class ResponseHeaderItemTest { + + private static final String VAR_NAME = "varName"; + + private static final ResponseHeaderAccessItem ELEMENT = new ResponseHeaderAccessItem(VAR_NAME); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private HttpServerResponse serverResponse; + + private Response response; + + @BeforeEach + public void initStrBuilder() { + routingContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + serverResponse = Mockito.mock(HttpServerResponse.class); + response = Mockito.mock(Response.class); + + accessLogEvent = new ServerAccessLogEvent(); + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElement() { + HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); + String headerValue = "headerValue"; + headers.add(VAR_NAME, headerValue); + when(routingContext.response()).thenReturn(serverResponse); + when(serverResponse.headers()).thenReturn(headers); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals(headerValue, strBuilder.toString()); + Assertions.assertEquals(ELEMENT.getVarName(), VAR_NAME); + } + + @Test + public void clientFormattedElement() { + String headerValue = "headerValue"; + + response = Response.ok(null) + .setHeader(VAR_NAME, headerValue); + when(finishEvent.getResponse()).thenReturn(response); + + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals(headerValue, strBuilder.toString()); + Assertions.assertEquals(ELEMENT.getVarName(), VAR_NAME); + } + + @Test + public void serverFormattedElementOnHeadersIsNull() { + when(routingContext.response()).thenReturn(serverResponse); + when(serverResponse.headers()).thenReturn(null); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnHeadersIsNull() { + when(finishEvent.getResponse()).thenReturn(response); + when(response.getHeaders()).thenReturn(null); + + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnResponseIsNull() { + when(routingContext.response()).thenReturn(null); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnResponseIsNull() { + when(finishEvent.getResponse()).thenReturn(null); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnNotFound() { + HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); + String headerValue = "headerValue"; + headers.add("anotherHeader", headerValue); + when(routingContext.response()).thenReturn(serverResponse); + when(serverResponse.headers()).thenReturn(headers); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnNotFound() { + String headerValue = "headerValue"; + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + headers.set("anotherHeader", headerValue); + when(finishEvent.getResponse()).thenReturn(response); + when(response.getHeaders()).thenReturn(headers); + + ELEMENT.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/ResponseSizeItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseSizeItemTest.java new file mode 100644 index 00000000000..e5f6e6ae17f --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseSizeItemTest.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.element.impl; + +import static org.mockito.Mockito.when; + +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +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.HttpServerResponse; +import io.vertx.ext.web.RoutingContext; + +public class ResponseSizeItemTest { + private static final ResponseSizeAccessItem ELEMENT = new ResponseSizeAccessItem("0"); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private HttpServerResponse serverResponse; + + @BeforeEach + public void initStrBuilder() { + routingContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + serverResponse = Mockito.mock(HttpServerResponse.class); + + accessLogEvent = new ServerAccessLogEvent(); + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElement() { + long bytesWritten = 16L; + when(routingContext.response()).thenReturn(serverResponse); + when(serverResponse.bytesWritten()).thenReturn(bytesWritten); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals(String.valueOf(bytesWritten), strBuilder.toString()); + } + + @Test + public void clientFormattedElement() { + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("0", strBuilder.toString()); + } + + @Test + public void getFormattedElementOnResponseIsNull() { + when(routingContext.response()).thenReturn(null); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("0", strBuilder.toString()); + } + + @Test + public void getFormattedElementOnBytesWrittenIsZero() { + long bytesWritten = 0L; + when(routingContext.response()).thenReturn(serverResponse); + when(serverResponse.bytesWritten()).thenReturn(bytesWritten); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("0", strBuilder.toString()); + } +} 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 new file mode 100644 index 00000000000..b21dc7dfbaf --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/TraceIdItemTest.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.common.accessLog.core.element.impl; + +import static org.hamcrest.core.Is.is; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +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; +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import io.vertx.core.http.HttpServerRequest; +import io.vertx.ext.web.RoutingContext; + +public class TraceIdItemTest { + private static final TraceIdAccessItem ELEMENT = new TraceIdAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private HttpServerRequest serverRequest; + + private Invocation invocation; + + private final Map clientContext = new HashMap<>(); + + @BeforeEach + public void initStrBuilder() { + accessLogEvent = new ServerAccessLogEvent(); + routingContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + serverRequest = Mockito.mock(HttpServerRequest.class); + + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + clientContext.clear(); + } + + @Test + public void serverGetFormattedElementFromInvocationContext() { + Map data = new HashMap<>(); + String traceIdTest = "traceIdTest"; + when(invocation.getContext(CoreConst.TRACE_ID_NAME)).thenReturn(traceIdTest); + when(routingContext.data()).thenReturn(data); + data.put(RestConst.REST_INVOCATION_CONTEXT, invocation); + + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), is(traceIdTest)); + } + + @Test + public void clientGetFormattedElementFromInvocationContext() { + String traceIdTest = "traceIdTest"; + clientContext.put(CoreConst.TRACE_ID_NAME, traceIdTest); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getContext()).thenReturn(clientContext); + + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), is(traceIdTest)); + } + + @Test + public void serverGetFormattedElementFromRequestHeader() { + Map data = new HashMap<>(); + String traceIdTest = "traceIdTest"; + when(invocation.getContext(CoreConst.TRACE_ID_NAME)).thenReturn(null); + when(routingContext.data()).thenReturn(data); + data.put(RestConst.REST_INVOCATION_CONTEXT, invocation); + + when(serverRequest.getHeader(CoreConst.TRACE_ID_NAME)).thenReturn(traceIdTest); + when(routingContext.request()).thenReturn(serverRequest); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), is(traceIdTest)); + } + + @Test + public void serverGetFormattedElementOnTraceIdNotFound() { + Map data = new HashMap<>(); + when(invocation.getContext(CoreConst.TRACE_ID_NAME)).thenReturn(""); + when(routingContext.data()).thenReturn(data); + data.put(RestConst.REST_INVOCATION_CONTEXT, invocation); + + 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(CoreConst.TRACE_ID_NAME)).thenReturn(null); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), is("-")); + } + + @Test + public void clientGetFormattedElementOnTraceIdNotFound() { + clientContext.put(CoreConst.TRACE_ID_NAME, null); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getContext()).thenReturn(clientContext); + + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), is("-")); + } + + @Test + public void serverGetFormattedElementOnInvocationContextIsNull() { + when(routingContext.data()).thenReturn(null); + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.getHeader(CoreConst.TRACE_ID_NAME)).thenReturn(null); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), is("-")); + } + + @Test + public void clientGetFormattedElementOnInvocationContextIsNull() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getContext()).thenReturn(null); + + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), is("-")); + } + + @Test + public void serverGetFormattedElementOnDataIsNull() { + when(serverRequest.getHeader(CoreConst.TRACE_ID_NAME)).thenReturn(null); + when(routingContext.request()).thenReturn(serverRequest); + when(routingContext.data()).thenReturn(null); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + MatcherAssert.assertThat(strBuilder.toString(), is("-")); + } +} diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/TransportItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/TransportItemTest.java new file mode 100644 index 00000000000..02a111b94c0 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/TransportItemTest.java @@ -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. + */ + +package org.apache.servicecomb.common.accessLog.core.element.impl; + +import static org.mockito.Mockito.when; + +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.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TransportItemTest { + private static final TransportAccessItem ITEM = new TransportAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private Invocation invocation; + + private Endpoint endpoint; + + @BeforeEach + public void initStrBuilder() { + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + endpoint = Mockito.mock(Endpoint.class); + accessLogEvent = new ServerAccessLogEvent(); + strBuilder = new StringBuilder(); + } + + @Test + public void clientFormattedElement() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getConfigTransportName()).thenReturn("rest"); + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("rest", strBuilder.toString()); + + strBuilder = new StringBuilder(); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getEndpoint()).thenReturn(endpoint); + when(endpoint.getEndpoint()).thenReturn("rest:xxx:30100"); + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("rest", strBuilder.toString()); + } + + @Test + public void serverFormattedElement() { + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("rest", strBuilder.toString()); + } + + @Test + public void clientConfigTransportNameIsNull() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getConfigTransportName()).thenReturn(null); + + strBuilder = new StringBuilder(); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getEndpoint()).thenReturn(endpoint); + when(endpoint.getEndpoint()).thenReturn("rest:xxx:30100"); + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("rest", strBuilder.toString()); + } + + @Test + public void clientConfigTransportNameIsEmpty() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getConfigTransportName()).thenReturn(""); + strBuilder = new StringBuilder(); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getEndpoint()).thenReturn(endpoint); + when(endpoint.getEndpoint()).thenReturn("rest:xxx:30100"); + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("rest", strBuilder.toString()); + } + + @Test + public void clientALLIsEmpty() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getConfigTransportName()).thenReturn(null); + strBuilder = new StringBuilder(); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getEndpoint()).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/UrlPathItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathItemTest.java new file mode 100644 index 00000000000..d1601609a28 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathItemTest.java @@ -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. + */ + +package org.apache.servicecomb.common.accessLog.core.element.impl; + +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +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.v3.oas.models.OpenAPI; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.ext.web.RoutingContext; + +public class UrlPathItemTest { + private static final UrlPathAccessItem ITEM = new UrlPathAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private Invocation invocation; + + private HttpServerRequest serverRequest; + + private OperationMeta operationMeta; + + private SchemaMeta schemaMeta; + + private OpenAPI swagger; + + private RestClientRequestParameters restClientRequest; + + private HttpClientRequest clientRequest; + + @BeforeEach + public void initStrBuilder() { + accessLogEvent = new ServerAccessLogEvent(); + routingContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + serverRequest = Mockito.mock(HttpServerRequest.class); + operationMeta = Mockito.mock(OperationMeta.class); + schemaMeta = Mockito.mock(SchemaMeta.class); + swagger = Mockito.mock(OpenAPI.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); + clientRequest = Mockito.mock(HttpClientRequest.class); + + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + } + + @Test + public void clientFormattedElement() { + String uri = "/test"; + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getOperationMeta()).thenReturn(operationMeta); + when(invocation.getSchemaMeta()).thenReturn(schemaMeta); + when(schemaMeta.getSwagger()).thenReturn(swagger); + when(operationMeta.getOperationPath()).thenReturn("/test"); + + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals(uri, strBuilder.toString()); + + strBuilder = new StringBuilder(); + Map handlerContext = new HashMap<>(); + handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + when(invocation.getOperationMeta()).thenReturn(null); + when(invocation.getSchemaMeta()).thenReturn(null); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerContext); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); + when(clientRequest.path()).thenReturn(uri); + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals(uri, strBuilder.toString()); + } + + @Test + public void serverFormattedElement() { + String uri = "/test"; + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.path()).thenReturn(uri); + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals(uri, strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnRequestIsNull() { + when(routingContext.request()).thenReturn(null); + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnRequestIsNull() { + Map handlerContext = new HashMap<>(); + handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerContext); + when(restClientRequest.getHttpClientRequest()).thenReturn(null); + ITEM.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnMethodIsNull() { + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.path()).thenReturn(null); + ITEM.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnMethodIsNull() { + Map handlerContext = new HashMap<>(); + handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getHandlerContext()).thenReturn(handlerContext); + 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/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathWithQueryItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathWithQueryItemTest.java new file mode 100644 index 00000000000..f0bebc1f3d7 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathWithQueryItemTest.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.common.accessLog.core.element.impl; + +import static org.mockito.Mockito.when; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +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.HttpServerRequest; +import io.vertx.ext.web.RoutingContext; + +public class UrlPathWithQueryItemTest { + + public static final UrlPathWithQueryAccessItem ELEMENT = new UrlPathWithQueryAccessItem(); + + private StringBuilder strBuilder; + + private InvocationFinishEvent finishEvent; + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + private Invocation invocation; + + private HttpServerRequest serverRequest; + + @BeforeEach + public void initStrBuilder() { + accessLogEvent = new ServerAccessLogEvent(); + routingContext = Mockito.mock(RoutingContext.class); + finishEvent = Mockito.mock(InvocationFinishEvent.class); + invocation = Mockito.mock(Invocation.class); + serverRequest = Mockito.mock(HttpServerRequest.class); + + accessLogEvent.setRoutingContext(routingContext); + strBuilder = new StringBuilder(); + } + + @Test + public void serverFormattedElement() { + String uri = "uriTest"; + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.uri()).thenReturn(uri); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals(uri, strBuilder.toString()); + } + + @Test + public void clientFormattedElement() { + String uri = "uriTest"; + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH)).thenReturn(uri); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals(uri, strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnRequestIsNull() { + when(routingContext.request()).thenReturn(null); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnRequestIsNull() { + when(finishEvent.getInvocation()).thenReturn(null); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnUriIsNull() { + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.uri()).thenReturn(null); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnUriIsNull() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH)).thenReturn(null); + ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void serverFormattedElementOnUriIsEmpty() { + when(routingContext.request()).thenReturn(serverRequest); + when(serverRequest.uri()).thenReturn(""); + ELEMENT.appendServerFormattedItem(accessLogEvent, strBuilder); + Assertions.assertEquals("-", strBuilder.toString()); + } + + @Test + public void clientFormattedElementOnUriIsEmpty() { + when(finishEvent.getInvocation()).thenReturn(invocation); + when(invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH)).thenReturn(""); + ELEMENT.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/UserDefinedAccessLogItem.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UserDefinedAccessLogItem.java new file mode 100644 index 00000000000..0cee6247efe --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UserDefinedAccessLogItem.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.common.accessLog.core.element.impl; + + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import io.vertx.ext.web.RoutingContext; + +/** + * For access log extension test + */ +public class UserDefinedAccessLogItem implements AccessLogItem { + private final String config; + + public UserDefinedAccessLogItem(String config) { + this.config = config; + } + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + builder.append("user-defined-") + .append(config); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) { + builder.append("user-defined-") + .append(config); + } +} diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UserDefinedAccessLogItemLowPriority.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UserDefinedAccessLogItemLowPriority.java new file mode 100644 index 00000000000..a4ed8fd6460 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UserDefinedAccessLogItemLowPriority.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.common.accessLog.core.element.impl; + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; + +import io.vertx.ext.web.RoutingContext; + +/** + * For access log extension test, will be overridden by {@link RemoteHostAccessItem}("%h"), + * and takes no effect. + */ +public class UserDefinedAccessLogItemLowPriority implements AccessLogItem { + + @Override + public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) { + builder.append("OverriddenItem"); + } + + @Override + public void appendClientFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) { + builder.append("OverriddenItem"); + } +} diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/parser/impl/TestCompositeExtendedAccessLogItemMeta.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/parser/impl/TestCompositeExtendedAccessLogItemMeta.java new file mode 100644 index 00000000000..5a39657bd9f --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/parser/impl/TestCompositeExtendedAccessLogItemMeta.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.common.accessLog.core.parser.impl; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.common.accessLog.core.element.impl.UserDefinedAccessLogItem; +import org.apache.servicecomb.common.accessLog.core.parser.CompositeVertxRestAccessLogItemMeta; +import org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta; + + +public class TestCompositeExtendedAccessLogItemMeta extends CompositeVertxRestAccessLogItemMeta { + private static final List META_LIST = new ArrayList<>(); + + static { + META_LIST.add(new VertxRestAccessLogItemMeta("%{", "}user-defined", UserDefinedAccessLogItem::new)); + } + + @Override + public List getAccessLogItemMetas() { + return META_LIST; + } +} diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/parser/impl/TestSingleExtendedAccessLogItemMeta.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/parser/impl/TestSingleExtendedAccessLogItemMeta.java new file mode 100644 index 00000000000..7d881557a14 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/parser/impl/TestSingleExtendedAccessLogItemMeta.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.core.parser.impl; + + +import org.apache.servicecomb.common.accessLog.core.element.impl.UserDefinedAccessLogItemLowPriority; +import org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta; + +public class TestSingleExtendedAccessLogItemMeta extends VertxRestAccessLogItemMeta { + public TestSingleExtendedAccessLogItemMeta() { + super("%h", null, config -> new UserDefinedAccessLogItemLowPriority(), 1); + } +} diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/parser/impl/VertxRestAccessLogPatternParserTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/parser/impl/VertxRestAccessLogPatternParserTest.java new file mode 100644 index 00000000000..d870fc2b225 --- /dev/null +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/parser/impl/VertxRestAccessLogPatternParserTest.java @@ -0,0 +1,362 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.parser.impl; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + +import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.CookieAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.ConfigurableDatetimeAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.DurationMillisecondAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.DurationSecondAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.FirstLineOfRequestAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.HttpMethodAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.HttpStatusAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.InvocationContextAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.LocalHostAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.LocalPortAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.QueryStringAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.RemoteHostAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.RequestHeaderAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.RequestProtocolAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.ResponseHeaderAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.ResponseSizeAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.TraceIdAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.TransportAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.UrlPathAccessItem; +import org.apache.servicecomb.common.accessLog.core.element.impl.UrlPathWithQueryAccessItem; +import org.apache.servicecomb.common.accessLog.core.parser.CompositeVertxRestAccessLogItemMeta; +import org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +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.ext.web.RoutingContext; + +public class VertxRestAccessLogPatternParserTest { + private static final String ROW_PATTERN = "[cs-method] %m %s%T%D%h%v%p%B%b%r%U%q" + + "cs-uri-stemcs-uri-querycs-uri%H%t%{yyyy MM dd HH:mm:ss zzz}t" + + "%{yyyy MM dd HH:mm:ss|GMT+0|en-US}t" + + "%{incoming-header}i" + + "%{outgoing-header}o" + + "%{cookie}C" + + "%SCB-traceId" + + "%{ctx}SCB-ctx" + + "%SCB-transport"; + + private static final VertxRestAccessLogPatternParser logPatternParser = new VertxRestAccessLogPatternParser(); + + private ServerAccessLogEvent accessLogEvent; + + private RoutingContext routingContext; + + + @BeforeEach + public void initStrBuilder() { + routingContext = Mockito.mock(RoutingContext.class); + accessLogEvent = new ServerAccessLogEvent(); + accessLogEvent.setRoutingContext(routingContext); + } + + @Test + public void testParsePatternFullTest() { + List> result = logPatternParser.parsePattern(ROW_PATTERN); + Assertions.assertEquals(29, result.size()); + StringBuilder builder = new StringBuilder(); + result.get(0).appendServerFormattedItem(accessLogEvent, builder); + Assertions.assertEquals("[", builder.toString()); + Assertions.assertEquals(HttpMethodAccessItem.class, result.get(1).getClass()); + builder = new StringBuilder(); + result.get(2).appendServerFormattedItem(accessLogEvent, builder); + Assertions.assertEquals("] ", builder.toString()); + Assertions.assertEquals(HttpMethodAccessItem.class, result.get(3).getClass()); + builder = new StringBuilder(); + result.get(4).appendServerFormattedItem(accessLogEvent, builder); + Assertions.assertEquals(" ", builder.toString()); + Assertions.assertEquals(HttpStatusAccessItem.class, result.get(5).getClass()); + Assertions.assertEquals(DurationSecondAccessItem.class, result.get(6).getClass()); + Assertions.assertEquals(DurationMillisecondAccessItem.class, result.get(7).getClass()); + Assertions.assertEquals(RemoteHostAccessItem.class, result.get(8).getClass()); + Assertions.assertEquals(LocalHostAccessItem.class, result.get(9).getClass()); + Assertions.assertEquals(LocalPortAccessItem.class, result.get(10).getClass()); + Assertions.assertEquals(ResponseSizeAccessItem.class, result.get(11).getClass()); + Assertions.assertEquals("0", ((ResponseSizeAccessItem) result.get(11)).getZeroBytes()); + Assertions.assertEquals(ResponseSizeAccessItem.class, result.get(12).getClass()); + Assertions.assertEquals("-", ((ResponseSizeAccessItem) result.get(12)).getZeroBytes()); + Assertions.assertEquals(FirstLineOfRequestAccessItem.class, result.get(13).getClass()); + Assertions.assertEquals(UrlPathAccessItem.class, result.get(14).getClass()); + Assertions.assertEquals(QueryStringAccessItem.class, result.get(15).getClass()); + Assertions.assertEquals(UrlPathAccessItem.class, result.get(16).getClass()); + Assertions.assertEquals(QueryStringAccessItem.class, result.get(17).getClass()); + Assertions.assertEquals(UrlPathWithQueryAccessItem.class, result.get(18).getClass()); + Assertions.assertEquals(RequestProtocolAccessItem.class, result.get(19).getClass()); + Assertions.assertEquals(ConfigurableDatetimeAccessItem.class, result.get(20).getClass()); + Assertions.assertEquals(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, + ((ConfigurableDatetimeAccessItem) result.get(20)).getPattern()); + Assertions.assertEquals(ConfigurableDatetimeAccessItem.DEFAULT_LOCALE, ((ConfigurableDatetimeAccessItem) result.get(20)).getLocale()); + Assertions.assertEquals(TimeZone.getDefault(), ((ConfigurableDatetimeAccessItem) result.get(20)).getTimezone()); + Assertions.assertEquals("yyyy MM dd HH:mm:ss zzz", ((ConfigurableDatetimeAccessItem) result.get(21)).getPattern()); + Assertions.assertEquals(ConfigurableDatetimeAccessItem.DEFAULT_LOCALE, ((ConfigurableDatetimeAccessItem) result.get(21)).getLocale()); + Assertions.assertEquals(TimeZone.getDefault(), ((ConfigurableDatetimeAccessItem) result.get(21)).getTimezone()); + Assertions.assertEquals("yyyy MM dd HH:mm:ss", ((ConfigurableDatetimeAccessItem) result.get(22)).getPattern()); + Assertions.assertEquals(Locale.forLanguageTag("en-US"), ((ConfigurableDatetimeAccessItem) result.get(22)).getLocale()); + Assertions.assertEquals(TimeZone.getTimeZone("GMT+0"), ((ConfigurableDatetimeAccessItem) result.get(22)).getTimezone()); + Assertions.assertEquals(RequestHeaderAccessItem.class, result.get(23).getClass()); + Assertions.assertEquals("incoming-header", ((RequestHeaderAccessItem) result.get(23)).getVarName()); + Assertions.assertEquals(ResponseHeaderAccessItem.class, result.get(24).getClass()); + Assertions.assertEquals("outgoing-header", ((ResponseHeaderAccessItem) result.get(24)).getVarName()); + Assertions.assertEquals(CookieAccessItem.class, result.get(25).getClass()); + Assertions.assertEquals("cookie", ((CookieAccessItem) result.get(25)).getVarName()); + Assertions.assertEquals(TraceIdAccessItem.class, result.get(26).getClass()); + Assertions.assertEquals(InvocationContextAccessItem.class, result.get(27).getClass()); + Assertions.assertEquals("ctx", ((InvocationContextAccessItem) result.get(27)).getVarName()); + Assertions.assertEquals(TransportAccessItem.class, result.get(28).getClass()); + } + + @Test + public void testParsePattern() { + String pattern = " %m cs-uri-stem %{response-header}o "; + List> result = logPatternParser.parsePattern(pattern); + Assertions.assertEquals(7, result.size()); + StringBuilder stringBuilder = new StringBuilder(); + result.get(0).appendServerFormattedItem(accessLogEvent, stringBuilder); + Assertions.assertEquals(" ", stringBuilder.toString()); + Assertions.assertEquals(HttpMethodAccessItem.class, result.get(1).getClass()); + stringBuilder = new StringBuilder(); + result.get(2).appendServerFormattedItem(accessLogEvent, stringBuilder); + Assertions.assertEquals(" ", stringBuilder.toString()); + Assertions.assertEquals(UrlPathAccessItem.class, result.get(3).getClass()); + stringBuilder = new StringBuilder(); + result.get(4).appendServerFormattedItem(accessLogEvent, stringBuilder); + Assertions.assertEquals(" ", stringBuilder.toString()); + Assertions.assertEquals(ResponseHeaderAccessItem.class, result.get(5).getClass()); + Assertions.assertEquals("response-header", ((ResponseHeaderAccessItem) result.get(5)).getVarName()); + stringBuilder = new StringBuilder(); + result.get(6).appendServerFormattedItem(accessLogEvent, stringBuilder); + Assertions.assertEquals(" ", stringBuilder.toString()); + } + + @Test + public void testParsePatternWithNoBlank() { + String pattern = "%mcs-uri-stem%{response-header}o"; + List> result = logPatternParser.parsePattern(pattern); + Assertions.assertEquals(3, result.size()); + + Assertions.assertEquals(HttpMethodAccessItem.class, result.get(0).getClass()); + Assertions.assertEquals(UrlPathAccessItem.class, result.get(1).getClass()); + Assertions.assertEquals(ResponseHeaderAccessItem.class, result.get(2).getClass()); + Assertions.assertEquals("response-header", ((ResponseHeaderAccessItem) result.get(2)).getVarName()); + } + + @Test + public void testParsePatternComplex() { + String pattern = "%m cs-uri-stem %{response-header}o abc cs-uri-query %s%{request} header}i plain cs-uri"; + List> result = logPatternParser.parsePattern(pattern); + Assertions.assertEquals(12, result.size()); + + Assertions.assertEquals(HttpMethodAccessItem.class, result.get(0).getClass()); + StringBuilder stringBuilder = new StringBuilder(); + result.get(1).appendServerFormattedItem(accessLogEvent, stringBuilder); + Assertions.assertEquals(" ", stringBuilder.toString()); + Assertions.assertEquals(UrlPathAccessItem.class, result.get(2).getClass()); + stringBuilder = new StringBuilder(); + result.get(3).appendServerFormattedItem(accessLogEvent, stringBuilder); + Assertions.assertEquals(" ", stringBuilder.toString()); + Assertions.assertEquals(ResponseHeaderAccessItem.class, result.get(4).getClass()); + Assertions.assertEquals("response-header", ((ResponseHeaderAccessItem) result.get(4)).getVarName()); + stringBuilder = new StringBuilder(); + result.get(5).appendServerFormattedItem(accessLogEvent, stringBuilder); + Assertions.assertEquals(" abc ", stringBuilder.toString()); + Assertions.assertEquals(QueryStringAccessItem.class, result.get(6).getClass()); + stringBuilder = new StringBuilder(); + result.get(7).appendServerFormattedItem(accessLogEvent, stringBuilder); + Assertions.assertEquals(" ", stringBuilder.toString()); + Assertions.assertEquals(HttpStatusAccessItem.class, result.get(8).getClass()); + Assertions.assertEquals(RequestHeaderAccessItem.class, result.get(9).getClass()); + Assertions.assertEquals("request} header", ((RequestHeaderAccessItem) result.get(9)).getVarName()); + stringBuilder = new StringBuilder(); + result.get(10).appendServerFormattedItem(accessLogEvent, stringBuilder); + Assertions.assertEquals(" plain ", stringBuilder.toString()); + Assertions.assertEquals(UrlPathWithQueryAccessItem.class, result.get(11).getClass()); + } + + final Comparator comparator = VertxRestAccessLogPatternParser.accessLogItemMetaComparator; + + /** + * one factor test + */ + @Test + public void testCompareMetaSimple() { + Assertions.assertTrue( + comparator.compare( + new VertxRestAccessLogItemMeta(null, null, null, 0), + new VertxRestAccessLogItemMeta(null, null, null, 1) + ) < 0 + ); + Assertions.assertTrue( + comparator.compare( + new VertxRestAccessLogItemMeta(null, "}abc", null, 0), + new VertxRestAccessLogItemMeta(null, null, null, 0) + ) < 0 + ); + Assertions.assertTrue( + comparator.compare( + new VertxRestAccessLogItemMeta(null, "}abc", null, 0), + new VertxRestAccessLogItemMeta(null, "}de", null, 0) + ) < 0 + ); + Assertions.assertTrue( + comparator.compare( + new VertxRestAccessLogItemMeta(null, "}abc", null, 0), + new VertxRestAccessLogItemMeta(null, "}ab", null, 0) + ) < 0 + ); + Assertions.assertTrue( + comparator.compare( + new VertxRestAccessLogItemMeta("%abc", null, null, 0), + new VertxRestAccessLogItemMeta("%de", null, null, 0) + ) < 0 + ); + Assertions.assertTrue( + comparator.compare( + new VertxRestAccessLogItemMeta("%abc", null, null, 0), + new VertxRestAccessLogItemMeta("%ab", null, null, 0) + ) < 0 + ); + Assertions.assertEquals(0, comparator.compare( + new VertxRestAccessLogItemMeta("%abc", null, null, 0), + new VertxRestAccessLogItemMeta("%abc", null, null, 0) + )); + } + + /** + * multiple factors test + */ + @Test + public void testCompareMetaComplex() { + Assertions.assertTrue( + comparator.compare( + new VertxRestAccessLogItemMeta("%bcd", "}ab", null, 0), + new VertxRestAccessLogItemMeta("%abc", "}abc", null, 0) + ) > 0 + ); + Assertions.assertTrue( + comparator.compare( + new VertxRestAccessLogItemMeta("%abc", null, null, 0), + new VertxRestAccessLogItemMeta("%bcd", "}ab", null, 0) + ) > 0 + ); + Assertions.assertTrue( + comparator.compare( + new VertxRestAccessLogItemMeta("%bcd", "}abc", null, 0), + new VertxRestAccessLogItemMeta("%abc", "}abc", null, 0) + ) > 0 + ); + Assertions.assertTrue( + comparator.compare( + new VertxRestAccessLogItemMeta("%abc", "}abc", null, 1), + new VertxRestAccessLogItemMeta("%ab", "}ab", null, 0) + ) > 0 + ); + } + + @Test + public void testComparePlaceholderString() { + Assertions.assertTrue( + VertxRestAccessLogPatternParser.comparePlaceholderString("abc", "bbc") < 0 + ); + Assertions.assertTrue( + VertxRestAccessLogPatternParser.comparePlaceholderString("abc", "ab") < 0 + ); + Assertions.assertEquals(0, VertxRestAccessLogPatternParser.comparePlaceholderString("abc", "abc")); + Assertions.assertTrue( + VertxRestAccessLogPatternParser.comparePlaceholderString("bbc", "abc") > 0 + ); + Assertions.assertTrue( + VertxRestAccessLogPatternParser.comparePlaceholderString("ab", "abc") > 0 + ); + } + + @Test + public void testExtendedVertxRestAccessLogItemCreator() { + final List metaList0 = new ArrayList<>(); + metaList0.add(new VertxRestAccessLogItemMeta("%{", "}abc", null)); + metaList0.add(new VertxRestAccessLogItemMeta("%{", "}a", null)); + metaList0.add(new VertxRestAccessLogItemMeta("%_", null, null, -1)); + + final List metaList1 = new ArrayList<>(); + metaList0.add(new VertxRestAccessLogItemMeta("%a", "}abc", null)); + metaList0.add(new VertxRestAccessLogItemMeta("%0", "}abc", null, 1)); + metaList0.add(new VertxRestAccessLogItemMeta("%m", null, null)); + + CompositeVertxRestAccessLogItemMeta compositeMeta0 = new CompositeVertxRestAccessLogItemMeta() { + @Override + public List getAccessLogItemMetas() { + return metaList0; + } + }; + CompositeVertxRestAccessLogItemMeta compositeMeta1 = new CompositeVertxRestAccessLogItemMeta() { + @Override + public List getAccessLogItemMetas() { + return metaList1; + } + }; + + List metaList = new ArrayList<>(1); + metaList.add(compositeMeta0); + metaList.add(compositeMeta1); + metaList.add(new VertxRestAccessLogItemMeta("%{", null, null)); + + VertxRestAccessLogPatternParser parser = Mockito.mock(VertxRestAccessLogPatternParser.class); + Mockito.when(parser.getMetaList()).thenAnswer(invocation -> { + List resultMetaList = new ArrayList<>(); + for (VertxRestAccessLogItemMeta meta : metaList) { + if (CompositeVertxRestAccessLogItemMeta.class.isAssignableFrom(meta.getClass())) { + resultMetaList.addAll(((CompositeVertxRestAccessLogItemMeta) meta).getAccessLogItemMetas()); + } else { + resultMetaList.add(meta); + } + } + VertxRestAccessLogPatternParser.sortAccessLogItemMeta(resultMetaList); + return resultMetaList; + }); + + List accessLogItemMetaList = parser.getMetaList(); + + Assertions.assertEquals(7, accessLogItemMetaList.size()); + Assertions.assertEquals("%_", accessLogItemMetaList.get(0).getPrefix()); + Assertions.assertEquals("%a", accessLogItemMetaList.get(1).getPrefix()); + Assertions.assertEquals("}abc", accessLogItemMetaList.get(1).getSuffix()); + Assertions.assertEquals("%{", accessLogItemMetaList.get(2).getPrefix()); + Assertions.assertEquals("}abc", accessLogItemMetaList.get(2).getSuffix()); + Assertions.assertEquals("%{", accessLogItemMetaList.get(3).getPrefix()); + Assertions.assertEquals("}a", accessLogItemMetaList.get(3).getSuffix()); + Assertions.assertEquals("%m", accessLogItemMetaList.get(4).getPrefix()); + Assertions.assertNull(accessLogItemMetaList.get(4).getSuffix()); + Assertions.assertEquals("%{", accessLogItemMetaList.get(5).getPrefix()); + Assertions.assertNull(accessLogItemMetaList.get(5).getSuffix()); + Assertions.assertEquals("%0", accessLogItemMetaList.get(6).getPrefix()); + Assertions.assertEquals("}abc", accessLogItemMetaList.get(6).getSuffix()); + } +} 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 new file mode 100644 index 00000000000..74f9ee4cb24 --- /dev/null +++ b/common/common-access-log/src/test/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta @@ -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.common.accessLog.core.parser.impl.TestCompositeExtendedAccessLogItemMeta +org.apache.servicecomb.common.accessLog.core.parser.impl.TestSingleExtendedAccessLogItemMeta diff --git a/common/common-javassist/pom.xml b/common/common-javassist/pom.xml deleted file mode 100644 index b82958330bd..00000000000 --- a/common/common-javassist/pom.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - 4.0.0 - - io.servicecomb - common - 0.3.0-SNAPSHOT - - common-javassist - - - org.javassist - javassist - - - io.servicecomb - foundation-common - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - - diff --git a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/ClassConfig.java b/common/common-javassist/src/main/java/io/servicecomb/common/javassist/ClassConfig.java deleted file mode 100644 index 046222e41cb..00000000000 --- a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/ClassConfig.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.javassist; - -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -public class ClassConfig { - - private String className; - - private boolean intf; - - private List intfList = new ArrayList<>(); - - private List fieldList = new ArrayList<>(); - - private List methodList = new ArrayList<>(); - - public boolean isIntf() { - return intf; - } - - public void setIntf(boolean intf) { - this.intf = intf; - } - - public String getClassName() { - return className; - } - - public void setClassName(String className) { - this.className = className; - } - - public void addInterface(Class intf) { - addInterface(intf.getName()); - } - - public void addInterface(String intf) { - intfList.add(intf); - } - - public List getIntfList() { - return intfList; - } - - public List getFieldList() { - return fieldList; - } - - public FieldConfig addField(String name, Type genericType) { - return addField(name, TypeFactory.defaultInstance().constructType(genericType)); - } - - public FieldConfig addField(String name, JavaType javaType) { - FieldConfig field = new FieldConfig(); - field.setName(name); - field.setType(javaType); - - fieldList.add(field); - - return field; - } - - public void addMethod(MethodConfig methodConfig) { - methodConfig.init(); - methodList.add(methodConfig); - } - - public void addMethod(String source) { - addMethod(source, null); - } - - public void addMethod(String source, String genericSignature) { - MethodConfig methodConfig = new MethodConfig(); - methodConfig.setSource(source); - methodConfig.setGenericSignature(genericSignature); - addMethod(methodConfig); - } - - public List getMethodList() { - return methodList; - } -} diff --git a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/FieldConfig.java b/common/common-javassist/src/main/java/io/servicecomb/common/javassist/FieldConfig.java deleted file mode 100644 index 4b3c6e9502b..00000000000 --- a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/FieldConfig.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.javassist; - -import org.springframework.util.ClassUtils; - -import com.fasterxml.jackson.databind.JavaType; - -public class FieldConfig { - private String name; - - // javassist的成员不支持int这样的类型,必须是Integer才行 - private Class rawType; - - private JavaType type; - - private boolean genGetter; - - private boolean genSetter; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Class getRawType() { - return rawType; - } - - public JavaType getType() { - return type; - } - - public void setType(JavaType type) { - this.rawType = ClassUtils.resolvePrimitiveIfNecessary(type.getRawClass()); - this.type = type; - } - - public boolean isGenGetter() { - return genGetter; - } - - public void setGenGetter(boolean genGetter) { - this.genGetter = genGetter; - } - - public boolean isGenSetter() { - return genSetter; - } - - public void setGenSetter(boolean genSetter) { - this.genSetter = genSetter; - } - - public String getGenericSignature() { - if (type.hasGenericTypes()) { - return type.getGenericSignature(); - } - - return null; - } -} diff --git a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/JavassistUtils.java b/common/common-javassist/src/main/java/io/servicecomb/common/javassist/JavassistUtils.java deleted file mode 100644 index 90c5bd5cc55..00000000000 --- a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/JavassistUtils.java +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.javassist; - -import static java.util.Locale.ENGLISH; - -import java.lang.reflect.Modifier; -import java.util.Arrays; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.ClassUtils; - -import com.fasterxml.jackson.databind.JavaType; - -import javassist.CannotCompileException; -import javassist.ClassPool; -import javassist.CtClass; -import javassist.CtConstructor; -import javassist.CtField; -import javassist.CtMethod; -import javassist.LoaderClassPath; -import javassist.NotFoundException; - -public final class JavassistUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(JavassistUtils.class); - - private static final ClassPool POOL = ClassPool.getDefault(); - - // servlet场景下,不添加classpath,则默认是java.lang.Object对应人classpath,找不到web-inf/lib中的jar - private static final Map CLASSLOADER_SET = new IdentityHashMap<>(); - - private static final Object LOCK = new Object(); - - private JavassistUtils() { - } - - private static void appendThreadClassPath() { - synchronized (LOCK) { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - if (CLASSLOADER_SET.containsKey(classLoader)) { - return; - } - - POOL.appendClassPath(new LoaderClassPath(classLoader)); - CLASSLOADER_SET.put(classLoader, classLoader); - } - } - - @SuppressWarnings("rawtypes") - public static Class createEnum(String clsName, String... values) { - return createEnum(null, clsName, Arrays.asList(values)); - } - - @SuppressWarnings("rawtypes") - public static Class createEnum(String clsName, List values) { - return createEnum(null, clsName, values); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public static Class createEnum(ClassLoader classLoader, String clsName, List values) { - if (values == null || values.size() == 0) { - throw new Error("values is not allowed empty."); - } - - if (classLoader == null) { - classLoader = Thread.currentThread().getContextClassLoader(); - } - - CtClass ctClass = POOL.makeClass(clsName); - ctClass.setModifiers(ctClass.getModifiers() | javassist.Modifier.ENUM); - - try { - ctClass.setSuperclass(POOL.get(Enum.class.getName())); - - addEnumConstructor(ctClass); - addEnumValuesMethod(ctClass, values); - - return ctClass.toClass(classLoader, null); - } catch (Throwable e) { - throw new Error(e); - } - } - - private static void addEnumConstructor(CtClass ctClass) throws Exception { - String src = "super($1, $2);"; - CtConstructor ctConstructor = new CtConstructor( - POOL.get(new String[] {String.class.getName(), int.class.getName()}), ctClass); - ctConstructor.setBody(src); - - ctClass.addConstructor(ctConstructor); - } - - private static void addEnumValuesMethod(CtClass ctClass, List values) throws CannotCompileException { - StringBuilder sb = new StringBuilder(); - sb.append("public static Enum[] values(){return new Enum[]{"); - for (int idx = 0; idx < values.size(); idx++) { - String value = values.get(idx); - - String line = String.format("new %s(\"%s\", %d),", ctClass.getName(), value, idx); - sb.append(line); - } - sb.setLength(sb.length() - 1); - sb.append("};}"); - - CtMethod valuesMethod = CtMethod.make(sb.toString(), ctClass); - ctClass.addMethod(valuesMethod); - } - - public static Class createClass(ClassConfig config) { - return createClass(null, config); - } - - public static Class createClass(ClassLoader classLoader, ClassConfig config) { - if (classLoader == null) { - classLoader = Thread.currentThread().getContextClassLoader(); - } - - appendThreadClassPath(); - - CtClass ctClass = POOL.getOrNull(config.getClassName()); - if (ctClass == null) { - if (config.isIntf()) { - ctClass = POOL.makeInterface(config.getClassName()); - } else { - - ctClass = POOL.makeClass(config.getClassName()); - } - } - - try { - for (String intfName : config.getIntfList()) { - ctClass.addInterface(POOL.get(intfName)); - } - - for (FieldConfig fieldConfig : config.getFieldList()) { - CtField field = createCtField(POOL, ctClass, fieldConfig); - ctClass.addField(field); - - if (fieldConfig.isGenGetter()) { - addFieldGetter(config, fieldConfig); - } - - if (fieldConfig.isGenSetter()) { - addFieldSetter(config, fieldConfig); - } - } - - for (MethodConfig methodConfig : config.getMethodList()) { - CtMethod ctMethod = CtMethod.make(methodConfig.getSource(), ctClass); - if (methodConfig.getGenericSignature() != null) { - ctMethod.setGenericSignature(methodConfig.getGenericSignature()); - } - ctClass.addMethod(ctMethod); - } - - return ctClass.toClass(classLoader, null); - } catch (Throwable e) { - throw new Error(e); - } - } - - public static String capitalize(String name) { - if (name == null || name.length() == 0) { - return name; - } - return name.substring(0, 1).toUpperCase(ENGLISH) + name.substring(1); - } - - private static void addFieldGetter(ClassConfig config, FieldConfig fieldConfig) { - MethodConfig methodConfig = new MethodConfig(); - - Class cls = fieldConfig.getRawType(); - String prefix = "get"; - if (cls.equals(Boolean.class) || cls.equals(boolean.class)) { - prefix = "is"; - } - methodConfig.setName(prefix + capitalize(fieldConfig.getName())); - methodConfig.setResult(fieldConfig.getType()); - methodConfig.setBodySource("return " + fieldConfig.getName() + ";"); - - config.addMethod(methodConfig); - } - - private static void addFieldSetter(ClassConfig config, FieldConfig fieldConfig) { - MethodConfig methodConfig = new MethodConfig(); - methodConfig.setName("set" + capitalize(fieldConfig.getName())); - methodConfig.addParameter(fieldConfig.getName(), fieldConfig.getType()); - methodConfig.setBodySource(" this." + fieldConfig.getName() + " = " + fieldConfig.getName() + ";"); - - config.addMethod(methodConfig); - } - - public static void genMultiWrapperInterface(ClassConfig config) { - try { - config.addInterface(MultiWrapper.class); - - config.addMethod(genReadFieldsMethodSource(config.getFieldList())); - config.addMethod(genWriteFieldsMethodSource(config.getFieldList())); - } catch (Exception e) { - String msg = String.format("failed to genMultiWrapperInterface, name=%s", config.getClassName()); - LOGGER.error(msg, e); - - throw new Error(msg, e); - } - } - - public static void genSingleWrapperInterface(ClassConfig config) { - try { - config.addInterface(SingleWrapper.class); - - config.addMethod(genReadFieldMethodSource(config.getFieldList())); - config.addMethod(genWriteFieldMethodSource(config.getFieldList())); - } catch (Exception e) { - String msg = String.format("failed to genSingleWrapperMethod, name=%s", config.getClassName()); - LOGGER.error(msg, e); - - throw new Error(msg, e); - } - } - - private static String genReadFieldsMethodSource(List fieldList) throws Exception { - StringBuilder sb = new StringBuilder(); - sb.append("public Object[] readFields(){"); - sb.append(String.format("Object values[] = new Object[%d];", fieldList.size())); - - for (int idx = 0; idx < fieldList.size(); idx++) { - String fieldName = fieldList.get(idx).getName(); - String code = String.format(" values[%d] = %s;", - idx, - fieldName); - - sb.append(code); - } - sb.append("return values;"); - sb.append("}"); - - return sb.toString(); - } - - private static String genWriteFieldsMethodSource(List fieldList) throws Exception { - StringBuilder sb = new StringBuilder(); - sb.append("public void writeFields(Object[] values){"); - for (int idx = 0; idx < fieldList.size(); idx++) { - FieldConfig fieldConfig = fieldList.get(idx); - - String fieldName = fieldConfig.getName(); - Class type = fieldConfig.getRawType(); - String code = String.format(" %s = (%s)values[%d];", - fieldName, - type.getTypeName(), - idx); - - sb.append(code); - } - sb.append("}"); - - return sb.toString(); - } - - private static String genReadFieldMethodSource(List fieldList) throws Exception { - StringBuilder sb = new StringBuilder(); - sb.append("public Object readField(){"); - - String fieldName = "null"; - if (!fieldList.isEmpty()) { - fieldName = fieldList.get(0).getName(); - } - - sb.append(String.format(" return %s;", fieldName)); - sb.append("}"); - - return sb.toString(); - } - - private static String genWriteFieldMethodSource(List fieldList) throws Exception { - StringBuilder sb = new StringBuilder(); - sb.append("public void writeField(Object value){"); - - if (!fieldList.isEmpty()) { - FieldConfig fieldConfig = fieldList.get(0); - sb.append( - String.format(" %s=(%s)value;", - fieldConfig.getName(), - fieldConfig.getRawType().getTypeName())); - } - - sb.append("}"); - - return sb.toString(); - } - - private static CtField createCtField(ClassPool pool, CtClass ctClass, FieldConfig field) throws Exception { - Class fieldType = field.getRawType(); - - CtField ctField = new CtField(pool.getCtClass(fieldType.getName()), field.getName(), ctClass); - if (field.getGenericSignature() != null) { - ctField.setGenericSignature(field.getGenericSignature()); - } - ctField.setModifiers(Modifier.PUBLIC); - return ctField; - } - - public static String getNameForGenerateCode(JavaType javaType) { - if (byte[].class.equals(javaType.getRawClass())) { - return "byte[]"; - } - - if (!javaType.isArrayType()) { - Class rawClass = ClassUtils.resolvePrimitiveIfNecessary(javaType.getRawClass()); - return rawClass.getTypeName(); - } - - return javaType.getContentType().getRawClass().getName() + "[]"; - } - - // for test - public static void detach(String clsName) { - try { - POOL.getCtClass(clsName).detach(); - } catch (NotFoundException e) { - // do nothing. - } - } -} diff --git a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/MethodConfig.java b/common/common-javassist/src/main/java/io/servicecomb/common/javassist/MethodConfig.java deleted file mode 100644 index 138c6b31bdd..00000000000 --- a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/MethodConfig.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.javassist; - -import java.util.ArrayList; -import java.util.List; - -import com.fasterxml.jackson.databind.JavaType; - -public class MethodConfig { - private String name; - - private JavaType result; - - private List parameterList = new ArrayList<>(); - - // 不包括前后的{} - private String bodySource; - - // 根据上面的信息,生成下面两个字段 - // 包括method声明和body - // 如果是接口,则只是method声明 - private String source; - - // 泛型声明,如果method参数及应答中没有泛型类型,则此字段应该为null - private String genericSignature; - - public String getSource() { - return source; - } - - public void setSource(String source) { - this.source = source; - } - - public String getGenericSignature() { - return genericSignature; - } - - public void setGenericSignature(String genericSignature) { - this.genericSignature = genericSignature; - } - - public void setName(String name) { - this.name = name; - } - - public void setResult(JavaType result) { - this.result = result; - } - - public void addParameter(String name, JavaType type) { - ParameterConfig parameterConfig = new ParameterConfig(); - parameterConfig.setName(name); - parameterConfig.setType(type); - parameterList.add(parameterConfig); - } - - public void setBodySource(String bodySource) { - this.bodySource = bodySource; - } - - void init() { - if (source != null) { - return; - } - - StringBuilder sbMethod = new StringBuilder(); - StringBuilder sbMethodGenericSignature = new StringBuilder(); - - sbMethod.append("public "); - sbMethod.append(result == null ? "void" : JavassistUtils.getNameForGenerateCode(result)); - sbMethod.append(" ") - .append(name) - .append("("); - sbMethodGenericSignature.append("("); - - boolean hasGenericSignature = result == null ? false : result.hasGenericTypes(); - for (ParameterConfig parameter : parameterList) { - hasGenericSignature = hasGenericSignature || parameter.getType().hasGenericTypes(); - - String paramTypeName = JavassistUtils.getNameForGenerateCode(parameter.getType()); - String code = String.format("%s %s,", paramTypeName, parameter.getName()); - sbMethod.append(code); - sbMethodGenericSignature.append(parameter.getType().getGenericSignature()); - } - - if (!parameterList.isEmpty()) { - sbMethod.setLength(sbMethod.length() - 1); - } - sbMethod.append(")"); - sbMethodGenericSignature.append(")"); - sbMethodGenericSignature.append(result == null ? "V" : result.getGenericSignature()); - - if (bodySource != null) { - sbMethod.append("{").append(bodySource).append("}"); - } else { - sbMethod.append(";"); - } - - source = sbMethod.toString(); - if (hasGenericSignature) { - genericSignature = sbMethodGenericSignature.toString(); - } - } -} diff --git a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/MultiWrapper.java b/common/common-javassist/src/main/java/io/servicecomb/common/javassist/MultiWrapper.java deleted file mode 100644 index 420f58cb698..00000000000 --- a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/MultiWrapper.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.javassist; - -public interface MultiWrapper { - void writeFields(Object[] values); - - Object[] readFields(); -} diff --git a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/ParameterConfig.java b/common/common-javassist/src/main/java/io/servicecomb/common/javassist/ParameterConfig.java deleted file mode 100644 index fd354500cb4..00000000000 --- a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/ParameterConfig.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.common.javassist; - -import com.fasterxml.jackson.databind.JavaType; - -public class ParameterConfig { - private String name; - - private JavaType type; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public JavaType getType() { - return type; - } - - public void setType(JavaType type) { - this.type = type; - } -} diff --git a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/SingleWrapper.java b/common/common-javassist/src/main/java/io/servicecomb/common/javassist/SingleWrapper.java deleted file mode 100644 index 2ee1e27220b..00000000000 --- a/common/common-javassist/src/main/java/io/servicecomb/common/javassist/SingleWrapper.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.javassist; - -public interface SingleWrapper { - void writeField(Object value); - - Object readField(); -} diff --git a/common/common-javassist/src/test/java/io/servicecomb/common/javassist/TestJavassistUtils.java b/common/common-javassist/src/test/java/io/servicecomb/common/javassist/TestJavassistUtils.java deleted file mode 100644 index c28c0f0dd5c..00000000000 --- a/common/common-javassist/src/test/java/io/servicecomb/common/javassist/TestJavassistUtils.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.javassist; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.junit.Assert; -import org.junit.Test; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.foundation.common.utils.ReflectUtils; - -public class TestJavassistUtils { - @Test - public void testField() throws Exception { - ClassConfig classConfig = new ClassConfig(); - classConfig.setClassName("cse.ut.testField"); - - FieldConfig fieldConfig = classConfig.addField("intField", int.class); - fieldConfig.setGenGetter(true); - fieldConfig.setGenSetter(true); - - fieldConfig = classConfig.addField("intArrayField", int[].class); - fieldConfig.setGenGetter(true); - fieldConfig.setGenSetter(true); - - fieldConfig = classConfig.addField("listStringField", - TypeFactory.defaultInstance().constructCollectionType(List.class, String.class)); - fieldConfig.setGenGetter(true); - fieldConfig.setGenSetter(true); - - Class cls = JavassistUtils.createClass(classConfig); - - // intField - Field field = cls.getField("intField"); - Assert.assertEquals(Integer.class, field.getType()); - - Method method = cls.getMethod("getIntField"); - Assert.assertEquals(Integer.class, method.getReturnType()); - method = cls.getMethod("setIntField", Integer.class); - - // intArrayField - field = cls.getField("intArrayField"); - Assert.assertEquals(int[].class, field.getType()); - - method = cls.getMethod("getIntArrayField"); - Assert.assertEquals(int[].class, method.getReturnType()); - method = cls.getMethod("setIntArrayField", int[].class); - - // listStringField - field = cls.getField("listStringField"); - Assert.assertEquals("java.util.List", field.getGenericType().getTypeName()); - - method = cls.getMethod("getListStringField"); - Assert.assertEquals("java.util.List", method.getGenericReturnType().getTypeName()); - method = cls.getMethod("setListStringField", List.class); - } - - @Test - public void testAddParameter() { - ClassConfig classConfig = new ClassConfig(); - classConfig.setIntf(true); - String intfName = "cse.ut.TestAddParameter"; - classConfig.setClassName(intfName); - - // List method(Map map, Set set) - MethodConfig methodConfig = new MethodConfig(); - methodConfig.setName("method"); - methodConfig.setResult(TypeFactory.defaultInstance().constructCollectionType(List.class, String.class)); - methodConfig.addParameter("map", - TypeFactory.defaultInstance().constructMapType(Map.class, String.class, String.class)); - methodConfig.addParameter("set", - TypeFactory.defaultInstance().constructCollectionType(Set.class, String.class)); - classConfig.addMethod(methodConfig); - - Class intf = JavassistUtils.createClass(classConfig); - - Assert.assertEquals(intfName, intf.getName()); - Method method = ReflectUtils.findMethod(intf, "method"); - Assert.assertEquals("method", method.getName()); - Assert.assertEquals("java.util.List", method.getGenericReturnType().getTypeName()); - - Type[] types = method.getGenericParameterTypes(); - Assert.assertEquals("java.util.Map", types[0].getTypeName()); - Assert.assertEquals("java.util.Set", types[1].getTypeName()); - } - - @Test - public void testInterface() throws Exception { - ClassConfig classConfig = new ClassConfig(); - classConfig.setIntf(true); - String intfName = "cse.ut.TestInterface"; - classConfig.setClassName(intfName); - - String source = "java.util.List method(java.util.Map map, java.util.Set set);"; - String genericSignature = - "(Ljava/util/Map;Ljava/util/Set;)Ljava/util/List;"; - classConfig.addMethod(source, genericSignature); - - Class intf = JavassistUtils.createClass(classConfig); - - Assert.assertEquals(intfName, intf.getName()); - Method method = ReflectUtils.findMethod(intf, "method"); - Assert.assertEquals("method", method.getName()); - Assert.assertEquals("java.util.List", method.getGenericReturnType().getTypeName()); - - Type[] types = method.getGenericParameterTypes(); - Assert.assertEquals("java.util.Map", types[0].getTypeName()); - Assert.assertEquals("java.util.Set", types[1].getTypeName()); - } - - @Test - public void singleWrapperInt() throws Exception { - ClassConfig classConfig = new ClassConfig(); - classConfig.setClassName("cse.ut.single.IntWrapper"); - classConfig.addField("intField", TypeFactory.defaultInstance().constructType(int.class)); - - JavassistUtils.genSingleWrapperInterface(classConfig); - - Class wrapperClass = JavassistUtils.createClass(classConfig); - - SingleWrapper instance = (SingleWrapper) wrapperClass.newInstance(); - instance.writeField(100); - int intFieldValue = (int) instance.readField(); - Assert.assertEquals(100, intFieldValue); - } - - @Test - public void multiWrapper() throws Exception { - ClassConfig classConfig = new ClassConfig(); - classConfig.setClassName("cse.ut.multi.Wrapper"); - classConfig.addField("intField", (Type) int.class); - classConfig.addField("strField", String.class); - - JavassistUtils.genMultiWrapperInterface(classConfig); - - Class wrapperClass = JavassistUtils.createClass(classConfig); - - MultiWrapper instance = (MultiWrapper) wrapperClass.newInstance(); - instance.writeFields(new Object[] {100, "test"}); - Object[] fieldValues = (Object[]) instance.readFields(); - Assert.assertEquals(100, fieldValues[0]); - Assert.assertEquals("test", fieldValues[1]); - } - - @Test - public void testEnum() throws Exception { - @SuppressWarnings("rawtypes") - Class cls = JavassistUtils.createEnum("cse.ut.EnumAbc", "a", "b"); - Method method = cls.getMethod("values"); - Enum[] values = (Enum[]) method.invoke(null); - - Assert.assertEquals("cse.ut.EnumAbc", cls.getName()); - Assert.assertEquals(2, values.length); - Assert.assertEquals("a", values[0].name()); - Assert.assertEquals(0, values[0].ordinal()); - Assert.assertEquals("b", values[1].name()); - Assert.assertEquals(1, values[1].ordinal()); - } - - @Test - public void testGetNameForGenerateCode() { - JavaType jt = TypeFactory.defaultInstance().constructType(byte[].class); - String name = JavassistUtils.getNameForGenerateCode(jt); - Assert.assertEquals("byte[]", name); - - jt = TypeFactory.defaultInstance().constructType(Byte[].class); - name = JavassistUtils.getNameForGenerateCode(jt); - Assert.assertEquals("java.lang.Byte[]", name); - - jt = TypeFactory.defaultInstance().constructType(Object[].class); - name = JavassistUtils.getNameForGenerateCode(jt); - Assert.assertEquals("java.lang.Object[]", name); - } -} diff --git a/common/common-javassist/src/test/resources/log4j.properties b/common/common-javassist/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/common/common-javassist/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/common/common-protobuf/pom.xml b/common/common-protobuf/pom.xml index 3528dd88227..5b7ac5fcf7c 100644 --- a/common/common-protobuf/pom.xml +++ b/common/common-protobuf/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - common - 0.3.0-SNAPSHOT - - common-protobuf - - - io.servicecomb - java-chassis-core - - - io.protostuff - protostuff-core - - - io.protostuff - protostuff-runtime - - - - com.fasterxml.jackson.dataformat - jackson-dataformat-protobuf - - - io.servicecomb - common-javassist - - - com.google.protobuf - protobuf-java - 3.3.0 - test - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - - \ No newline at end of file + 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 + common + 3.4.0-SNAPSHOT + + common-protobuf + Java Chassis::Common::Protobuf + + + + org.apache.servicecomb + java-chassis-core + + + + org.apache.servicecomb + foundation-protobuf + + + + com.google.protobuf + protobuf-java + test + + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.servicecomb + swagger-generator-springmvc + test + + + org.apache.servicecomb + foundation-test-scaffolding + + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + ${protobuf-maven-plugin.version} + + com.google.protobuf:protoc:${protoc3-maven-plugin.version}:exe:${os.detected.classifier} + true + grpc-java + io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc-java-plugin.version}:exe:${os.detected.classifier} + + + + + generate-test-sources + + test-compile + + + + + + + diff --git a/common/common-protobuf/src/main/java/io/protostuff/MapSchemaUtils.java b/common/common-protobuf/src/main/java/io/protostuff/MapSchemaUtils.java deleted file mode 100644 index 94ab077ee06..00000000000 --- a/common/common-protobuf/src/main/java/io/protostuff/MapSchemaUtils.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.protostuff; - -import java.util.Map; - -import io.protostuff.MapSchema.MapWrapper; - -public final class MapSchemaUtils { - private MapSchemaUtils() { - } - - public static MapWrapper createMapWrapper(Map map) { - return new MapWrapper<>(map); - } -} diff --git a/common/common-protobuf/src/main/java/io/protostuff/runtime/ProtobufCompatibleUtils.java b/common/common-protobuf/src/main/java/io/protostuff/runtime/ProtobufCompatibleUtils.java deleted file mode 100644 index 81ed4406d2d..00000000000 --- a/common/common-protobuf/src/main/java/io/protostuff/runtime/ProtobufCompatibleUtils.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.protostuff.runtime; - -import java.io.IOException; -import java.util.Map; - -import io.protostuff.Input; -import io.protostuff.Output; -import io.protostuff.Pipe; -import io.protostuff.WireFormat.FieldType; -import io.servicecomb.foundation.common.utils.ReflectUtils; - -/** - * protostuff实现protobuf逻辑时,关于map的编码与protobuf不兼容 - * 这里修改map的编码逻辑 - */ -public final class ProtobufCompatibleUtils { - private static boolean inited = false; - - private ProtobufCompatibleUtils() { - } - - public static void init() { - if (inited) { - return; - } - - replaceRuntimeFieldFactoryMap(); - - inited = true; - } - - protected static void replaceRuntimeFieldFactoryMap() { - RuntimeFieldFactory> org = RuntimeMapFieldFactory.MAP; - RuntimeFieldFactory> map = new RuntimeFieldFactory>( - RuntimeFieldFactory.ID_MAP) { - - @Override - public FieldType getFieldType() { - return org.getFieldType(); - } - - @Override - public Map readFrom(Input input) throws IOException { - return org.readFrom(input); - } - - @Override - public void writeTo(Output output, int number, Map value, - boolean repeated) throws IOException { - org.writeTo(output, number, value, repeated); - } - - @Override - public void transfer(Pipe pipe, Input input, Output output, int number, - boolean repeated) throws IOException { - org.transfer(pipe, input, output, number, repeated); - } - - @Override - public Class typeClass() { - return org.typeClass(); - } - - @Override - public Field create(int number, String name, java.lang.reflect.Field field, IdStrategy strategy) { - @SuppressWarnings("unchecked") - RuntimeMapField runtimeMapField = - (RuntimeMapField) org.create(number, name, field, strategy); - - return new RuntimeMapFieldProtobuf(runtimeMapField, field); - } - }; - - ReflectUtils.setField(RuntimeMapFieldFactory.class, null, "MAP", map); - } -} diff --git a/common/common-protobuf/src/main/java/io/protostuff/runtime/ProtobufFeature.java b/common/common-protobuf/src/main/java/io/protostuff/runtime/ProtobufFeature.java deleted file mode 100644 index 260bbe7f99d..00000000000 --- a/common/common-protobuf/src/main/java/io/protostuff/runtime/ProtobufFeature.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.protostuff.runtime; - -public class ProtobufFeature { - // 历史版本中的protoStuff实现的protobuf的map编码与标准的protobuf不兼容 - // 为保持highway的兼容,旧的不兼容编码也要保留 - // 所以这里默认为false - // 只有LoginRequest/LoginResponse同时为true时,才使用标准protobuf编码 - private boolean useProtobufMapCodec = false; - - public boolean isUseProtobufMapCodec() { - return useProtobufMapCodec; - } - - public void setUseProtobufMapCodec(boolean useProtobufMapCodec) { - this.useProtobufMapCodec = useProtobufMapCodec; - } -} diff --git a/common/common-protobuf/src/main/java/io/protostuff/runtime/ProtobufFeatureUtils.java b/common/common-protobuf/src/main/java/io/protostuff/runtime/ProtobufFeatureUtils.java deleted file mode 100644 index 2c2fddaf26d..00000000000 --- a/common/common-protobuf/src/main/java/io/protostuff/runtime/ProtobufFeatureUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.protostuff.runtime; - -public final class ProtobufFeatureUtils { - private static ThreadLocal local = new ThreadLocal<>(); - - private ProtobufFeatureUtils() { - } - - static boolean isUseProtobufMapCodec() { - ProtobufFeature protobufFeature = local.get(); - return protobufFeature != null && protobufFeature.isUseProtobufMapCodec(); - } - - public static ProtobufFeature getProtobufFeature() { - return local.get(); - } - - public static void setProtobufFeature(ProtobufFeature protobufFeature) { - local.set(protobufFeature); - } - - public static void removeProtobufFeature() { - local.remove(); - } -} diff --git a/common/common-protobuf/src/main/java/io/protostuff/runtime/RuntimeMapFieldProtobuf.java b/common/common-protobuf/src/main/java/io/protostuff/runtime/RuntimeMapFieldProtobuf.java deleted file mode 100644 index e097a014e97..00000000000 --- a/common/common-protobuf/src/main/java/io/protostuff/runtime/RuntimeMapFieldProtobuf.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.protostuff.runtime; - -import java.io.IOException; -import java.util.Map; -import java.util.Map.Entry; - -import org.springframework.util.ReflectionUtils; - -import io.protostuff.ByteArrayInput; -import io.protostuff.ByteBufferInput; -import io.protostuff.Input; -import io.protostuff.MapSchema; -import io.protostuff.MapSchema.MapWrapper; -import io.protostuff.MapSchemaUtils; -import io.protostuff.Output; -import io.protostuff.Pipe; -import io.protostuff.ProtostuffException; -import io.protostuff.Schema; - -public class RuntimeMapFieldProtobuf extends RuntimeMapField { - private static java.lang.reflect.Field entrySchemaField = - ReflectionUtils.findField(MapSchema.class, "entrySchema"); - - static { - entrySchemaField.setAccessible(true); - } - - private RuntimeMapField runtimeMapField; - - private java.lang.reflect.Field field; - - private Schema> entrySchema; - - @SuppressWarnings("unchecked") - public RuntimeMapFieldProtobuf(RuntimeMapField runtimeMapField, - java.lang.reflect.Field field) { - super(runtimeMapField.type, runtimeMapField.number, runtimeMapField.name, null, - runtimeMapField.schema.messageFactory); - - entrySchema = (Schema>) ReflectionUtils.getField(entrySchemaField, - runtimeMapField.schema); - - this.runtimeMapField = runtimeMapField; - this.field = field; - } - - @SuppressWarnings("unchecked") - @Override - protected void mergeFrom(Input input, T message) throws IOException { - if (!ProtobufFeatureUtils.isUseProtobufMapCodec()) { - runtimeMapField.mergeFrom(input, message); - return; - } - - Map value = null; - try { - value = (Map) field.get(message); - if (value == null) { - value = schema.newMessage(); - field.set(message, value); - } - } catch (Exception e) { - throw new ProtostuffException( - "Failed to get or set map field " + field.getDeclaringClass().getName() + ":" + field.getName(), - e); - } - - MapWrapper mapWrapper = MapSchemaUtils.createMapWrapper(value); - if (ByteArrayInput.class.isInstance(input)) { - ((ByteArrayInput) input).readRawVarint32(); - } else if (ByteBufferInput.class.isInstance(input)) { - ((ByteBufferInput) input).readRawVarint32(); - } else { - throw new Error("not handler " + input.getClass().getName()); - } - - int keyNumber = input.readFieldNumber(schema); - if (keyNumber != 1) { - throw new ProtostuffException( - "The map was incorrectly serialized, expect key number 1, but be " + keyNumber); - } - Object key = kFrom(input, null); - - int valueNumber = input.readFieldNumber(schema); - if (valueNumber != 2) { - throw new ProtostuffException( - "The map was incorrectly serialized, expect value number 2, but be " + valueNumber); - } - vPutFrom(input, mapWrapper, key); - } - - @Override - @SuppressWarnings("unchecked") - protected void writeTo(Output output, T message) throws IOException { - if (!ProtobufFeatureUtils.isUseProtobufMapCodec()) { - runtimeMapField.writeTo(output, message); - return; - } - - final Map existing; - try { - existing = (Map) field.get(message); - } catch (Exception e) { - throw new RuntimeException(e); - } - - if (existing != null) { - for (Entry entry : existing.entrySet()) { - output.writeObject(number, entry, entrySchema, true); - } - } - } - - @Override - protected void transfer(Pipe pipe, Input input, Output output, - boolean repeated) throws IOException { - runtimeMapField.transfer(pipe, input, output, repeated); - } - - @Override - protected Object kFrom(Input input, - MapWrapper wrapper) throws IOException { - return runtimeMapField.kFrom(input, wrapper); - } - - @Override - protected void kTo(Output output, int fieldNumber, Object key, - boolean repeated) throws IOException { - runtimeMapField.kTo(output, fieldNumber, key, repeated); - } - - @Override - protected void kTransfer(Pipe pipe, Input input, Output output, - int number, boolean repeated) throws IOException { - runtimeMapField.kTransfer(pipe, input, output, number, repeated); - } - - @Override - protected void vPutFrom(Input input, - MapWrapper wrapper, Object key) throws IOException { - runtimeMapField.vPutFrom(input, wrapper, key); - } - - @Override - protected void vTo(Output output, int fieldNumber, Object val, - boolean repeated) throws IOException { - runtimeMapField.vTo(output, fieldNumber, val, repeated); - } - - @Override - protected void vTransfer(Pipe pipe, Input input, Output output, - int number, boolean repeated) throws IOException { - runtimeMapField.vTransfer(pipe, input, output, number, repeated); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/AbstractCodec.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/AbstractCodec.java deleted file mode 100644 index 029c66e79e8..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/AbstractCodec.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.codec; - -import java.lang.reflect.Type; - -import com.fasterxml.jackson.databind.ObjectReader; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -public abstract class AbstractCodec { - protected ObjectWriter writer; - - protected ObjectReader reader; - - public ObjectWriter getWriter() { - return writer; - } - - public ObjectReader getReader() { - return reader; - } - - public abstract void init(ProtobufSchema schema, Type... types); -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/AbstractFieldCodec.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/AbstractFieldCodec.java deleted file mode 100644 index 20607a410a3..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/AbstractFieldCodec.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.codec; - -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.type.TypeFactory; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufField; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -import io.servicecomb.codec.protobuf.jackson.CseObjectReader; - -public class AbstractFieldCodec extends AbstractCodec { - public static class ReaderHelpData { - // 在reader返回的Object[]中的下标 - private int index; - - private JsonDeserializer deser; - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - public JsonDeserializer getDeser() { - return deser; - } - - public void setDeser(JsonDeserializer deser) { - this.deser = deser; - } - } - - // key为field name - protected Map readerHelpDataMap = new HashMap<>(); - - @Override - public void init(ProtobufSchema schema, Type... types) { - initFieldMap(schema, types); - } - - private void initFieldMap(ProtobufSchema schema, Type[] types) { - Iterator fieldIter = schema.getRootType().fields().iterator(); - for (int idx = 0; idx < schema.getRootType().getFieldCount(); idx++) { - JavaType type = TypeFactory.defaultInstance().constructType(types[idx]); - ProtobufField field = fieldIter.next(); - - ReaderHelpData helpData = new ReaderHelpData(); - helpData.index = idx; - helpData.deser = ((CseObjectReader) reader).findDeserializer(type); - - readerHelpDataMap.put(field.name, helpData); - } - } - - public ReaderHelpData findInfo(String name) { - return readerHelpDataMap.get(name); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/ParamFieldCodec.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/ParamFieldCodec.java deleted file mode 100644 index 36ecc0ea99b..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/ParamFieldCodec.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.codec; - -import java.lang.reflect.Type; - -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -import io.servicecomb.codec.protobuf.definition.ProtobufManager; -import io.servicecomb.codec.protobuf.jackson.CseObjectReader; -import io.servicecomb.codec.protobuf.jackson.CseObjectWriter; -import io.servicecomb.codec.protobuf.jackson.ParamDeserializer; -import io.servicecomb.codec.protobuf.jackson.ParamSerializer; - -public class ParamFieldCodec extends AbstractFieldCodec { - @Override - public void init(ProtobufSchema schema, Type... types) { - writer = new CseObjectWriter(ProtobufManager.getWriter(), schema, new ParamSerializer()); - reader = - new CseObjectReader(ProtobufManager.getReader(), schema, new ParamDeserializer(readerHelpDataMap)); - - super.init(schema, types); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/ResultFieldCodec.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/ResultFieldCodec.java deleted file mode 100644 index 43a22d54808..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/ResultFieldCodec.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.codec; - -import java.lang.reflect.Type; - -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -import io.servicecomb.codec.protobuf.definition.ProtobufManager; -import io.servicecomb.codec.protobuf.jackson.CseObjectReader; -import io.servicecomb.codec.protobuf.jackson.CseObjectWriter; -import io.servicecomb.codec.protobuf.jackson.ResultDeserializer; -import io.servicecomb.codec.protobuf.jackson.ResultSerializer; - -public class ResultFieldCodec extends AbstractFieldCodec { - @Override - public void init(ProtobufSchema schema, Type... types) { - writer = new CseObjectWriter(ProtobufManager.getWriter(), schema, new ResultSerializer()); - reader = - new CseObjectReader(ProtobufManager.getReader(), schema, new ResultDeserializer(readerHelpDataMap)); - - super.init(schema, types); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/StandardParamCodec.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/StandardParamCodec.java deleted file mode 100644 index fd547383dd8..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/StandardParamCodec.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.codec; - -import java.lang.reflect.Type; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -import io.servicecomb.codec.protobuf.definition.ProtobufManager; -import io.servicecomb.codec.protobuf.jackson.StandardObjectReader; -import io.servicecomb.codec.protobuf.jackson.StandardObjectWriter; - -public class StandardParamCodec extends AbstractCodec { - @Override - public void init(ProtobufSchema schema, Type... types) { - writer = ProtobufManager.getMapper().writer(schema); - reader = ProtobufManager.getMapper().reader(schema); - - // 需要考虑没参数的场景 - if (types.length == 1) { - JavaType javaType = TypeFactory.defaultInstance().constructType(types[0]); - writer = new StandardObjectWriter(writer.forType(javaType)); - reader = new StandardObjectReader(reader.forType(javaType)); - } - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/StandardResultCodec.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/StandardResultCodec.java deleted file mode 100644 index 03ebac1ca9a..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/codec/StandardResultCodec.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.codec; - -import java.lang.reflect.Type; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -import io.servicecomb.codec.protobuf.definition.ProtobufManager; - -public class StandardResultCodec extends AbstractCodec { - @Override - public void init(ProtobufSchema schema, Type... types) { - writer = ProtobufManager.getMapper().writer(schema); - reader = ProtobufManager.getMapper().reader(schema); - - // 需要考虑void场景 - if (types.length == 1) { - JavaType javaType = TypeFactory.defaultInstance().constructType(types[0]); - writer = writer.forType(javaType); - reader = reader.forType(javaType); - } - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/definition/OperationProtobuf.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/definition/OperationProtobuf.java deleted file mode 100644 index 3ed18c72be3..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/definition/OperationProtobuf.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.definition; - -import java.lang.reflect.Method; - -import javax.ws.rs.core.Response.Status.Family; - -import io.servicecomb.codec.protobuf.utils.ProtobufSchemaUtils; -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.swagger.invocation.response.ResponseMeta; - -public class OperationProtobuf { - private OperationMeta operationMeta; - - private WrapSchema requestSchema; - - private WrapSchema responseSchema; - - public OperationProtobuf(OperationMeta operationMeta) - throws Exception { - this.operationMeta = operationMeta; - - requestSchema = ProtobufSchemaUtils.getOrCreateArgsSchema(operationMeta); - - Method method = operationMeta.getMethod(); - responseSchema = ProtobufSchemaUtils.getOrCreateSchema(method.getGenericReturnType()); - } - - public OperationMeta getOperationMeta() { - return operationMeta; - } - - public WrapSchema getRequestSchema() { - return requestSchema; - } - - public WrapSchema getResponseSchema() { - return responseSchema; - } - - public WrapSchema findResponseSchema(int statusCode) { - if (Family.SUCCESSFUL.equals(Family.familyOf(statusCode))) { - return responseSchema; - } - - ResponseMeta responseMeta = operationMeta.findResponseMeta(statusCode); - return ProtobufSchemaUtils.getOrCreateSchema(responseMeta.getJavaType()); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/definition/ProtobufManager.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/definition/ProtobufManager.java deleted file mode 100644 index 4db9ba24a7d..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/definition/ProtobufManager.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.definition; - -import com.fasterxml.jackson.databind.ObjectReader; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.dataformat.protobuf.ProtobufMapper; - -import io.servicecomb.core.definition.OperationMeta; - -public final class ProtobufManager { - private static ProtobufManager instance = new ProtobufManager(); - - private static ProtobufMapper mapper = new ProtobufMapper(); - - private static ObjectWriter writer = mapper.writer(); - - private static ObjectReader reader = mapper.reader(); - - public static final String EXT_ID = "protobuf"; - - private static final Object LOCK = new Object(); - - static { - // 支持在idl中定义empty message - mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); - } - - private ProtobufManager() { - } - - public static OperationProtobuf getOrCreateOperation(OperationMeta operationMeta) throws Exception { - OperationProtobuf operationProtobuf = operationMeta.getExtData(ProtobufManager.EXT_ID); - if (operationProtobuf == null) { - synchronized (LOCK) { - operationProtobuf = operationMeta.getExtData(ProtobufManager.EXT_ID); - if (operationProtobuf == null) { - operationProtobuf = new OperationProtobuf(operationMeta); - operationMeta.putExtData(EXT_ID, operationProtobuf); - } - } - } - - return operationProtobuf; - } - - public static ProtobufManager getInstance() { - return instance; - } - - public static ProtobufMapper getMapper() { - return mapper; - } - - public static ObjectWriter getWriter() { - return writer; - } - - public static ObjectReader getReader() { - return reader; - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/AbstractDeserializer.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/AbstractDeserializer.java deleted file mode 100644 index 38800a2f0e7..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/AbstractDeserializer.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import java.io.IOException; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; - -import io.servicecomb.codec.protobuf.codec.AbstractFieldCodec.ReaderHelpData; - -public abstract class AbstractDeserializer extends JsonDeserializer { - protected Map readerHelpDataMap; - - public AbstractDeserializer(Map readerHelpDataMap) { - this.readerHelpDataMap = readerHelpDataMap; - } - - @Override - public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { - Object result = createResult(); - for (String fieldName = p.nextFieldName(); fieldName != null; fieldName = p.nextFieldName()) { - // p实际是ProtobufParser,其内部是可以直接取到proto field的,理论上可以根据id来索引 - // 可是field默认没暴露出来,所以,直接用name索引了 - ReaderHelpData helpData = readerHelpDataMap.get(fieldName); - if (helpData == null) { - continue; - } - - JsonToken t = p.nextToken(); - // Note: must handle null explicitly here; value deserializers won't - Object value = null; - if (t == JsonToken.VALUE_NULL) { - value = helpData.getDeser().getNullValue(ctxt); - } else { - value = helpData.getDeser().deserialize(p, ctxt); - } - - result = updateResult(result, value, helpData); - } - - return result; - } - - protected abstract Object createResult(); - - protected abstract Object updateResult(Object result, Object value, ReaderHelpData helpData); -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/CseObjectReader.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/CseObjectReader.java deleted file mode 100644 index d0d229ea4ec..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/CseObjectReader.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import com.fasterxml.jackson.core.FormatSchema; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.ObjectReader; - -public class CseObjectReader extends ObjectReader { - private static final long serialVersionUID = -4154834940923475928L; - - public CseObjectReader(ObjectReader base, FormatSchema schema, JsonDeserializer rootDeser) { - super(base, base.getConfig(), null, rootDeser, null, schema, null, null); - } - - public JsonDeserializer findDeserializer(JavaType valueType) { - return _prefetchRootDeserializer(valueType); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/CseObjectWriter.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/CseObjectWriter.java deleted file mode 100644 index 24beaf9f1bc..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/CseObjectWriter.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import java.lang.reflect.Constructor; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.core.FormatSchema; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectWriter; - -@SuppressWarnings("unchecked") -public class CseObjectWriter extends ObjectWriter { - - private static final Logger LOGGER = LoggerFactory.getLogger(CseObjectWriter.class); - - private static final long serialVersionUID = -6435897284942268001L; - - private static Constructor prefetchConstructor; - - static { - prefetchConstructor = (Constructor) Prefetch.class.getDeclaredConstructors()[0]; - prefetchConstructor.setAccessible(true); - } - - private static Prefetch createPrefetch(JsonSerializer valueSerializer) { - try { - return prefetchConstructor.newInstance(null, valueSerializer, null); - } catch (Exception e) { - LOGGER.error("create prefetch error:", e); - } - return null; - } - - public CseObjectWriter(ObjectWriter base, FormatSchema schema, JsonSerializer valueSerializer) { - - super(base, base.getConfig(), new GeneratorSettings(null, schema, null, null), - createPrefetch(valueSerializer)); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ParamDeserializer.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ParamDeserializer.java deleted file mode 100644 index 372685902ef..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ParamDeserializer.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import java.util.Map; - -import io.servicecomb.codec.protobuf.codec.AbstractFieldCodec.ReaderHelpData; - -public class ParamDeserializer extends AbstractDeserializer { - - public ParamDeserializer(Map readerHelpDataMap) { - super(readerHelpDataMap); - } - - @Override - protected Object createResult() { - return new Object[readerHelpDataMap.size()]; - } - - @Override - protected Object updateResult(Object result, Object value, ReaderHelpData helpData) { - ((Object[]) result)[helpData.getIndex()] = value; - return result; - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ParamSerializer.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ParamSerializer.java deleted file mode 100644 index 6a240225674..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ParamSerializer.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import java.io.IOException; -import java.util.Iterator; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.dataformat.protobuf.ProtobufGenerator; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufField; - -public class ParamSerializer extends JsonSerializer { - @Override - public void serialize(Object value, JsonGenerator gen, - SerializerProvider serializers) throws IOException, JsonProcessingException { - gen.writeStartObject(); - - ProtobufGenerator protobufGenerator = (ProtobufGenerator) gen; - Iterator iter = protobufGenerator.getSchema().getRootType().fields().iterator(); - Object[] values = (Object[]) value; - for (int idx = 0; idx < values.length; idx++) { - gen.writeObjectField(iter.next().name, values[idx]); - } - - gen.writeEndObject(); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ResultDeserializer.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ResultDeserializer.java deleted file mode 100644 index 44f8541bfb5..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ResultDeserializer.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import java.util.Map; - -import io.servicecomb.codec.protobuf.codec.AbstractFieldCodec.ReaderHelpData; - -public class ResultDeserializer extends AbstractDeserializer { - - public ResultDeserializer(Map readerHelpDataMap) { - super(readerHelpDataMap); - } - - @Override - protected Object createResult() { - return null; - } - - @Override - protected Object updateResult(Object result, Object value, ReaderHelpData helpData) { - return value; - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ResultSerializer.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ResultSerializer.java deleted file mode 100644 index ce683ef9170..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/ResultSerializer.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.dataformat.protobuf.ProtobufGenerator; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufField; - -public class ResultSerializer extends JsonSerializer { - @Override - public void serialize(Object value, JsonGenerator gen, - SerializerProvider serializers) throws IOException, JsonProcessingException { - gen.writeStartObject(); - - ProtobufGenerator protobufGenerator = (ProtobufGenerator) gen; - ProtobufField field = protobufGenerator.getSchema().getRootType().firstField(); - gen.writeObjectField(field.name, value); - - gen.writeEndObject(); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/StandardObjectReader.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/StandardObjectReader.java deleted file mode 100644 index ab1aab66469..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/StandardObjectReader.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import java.io.IOException; -import java.io.InputStream; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectReader; - -public class StandardObjectReader extends ObjectReader { - private static final long serialVersionUID = -8162644250351645123L; - - public StandardObjectReader(ObjectReader base) { - super(base, base.getConfig()); - } - - @SuppressWarnings("unchecked") - @Override - public T readValue(InputStream src) throws IOException, JsonProcessingException { - T result = super.readValue(src); - return (T) new Object[] {result}; - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/StandardObjectWriter.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/StandardObjectWriter.java deleted file mode 100644 index 0b205186c61..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/jackson/StandardObjectWriter.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import java.io.IOException; -import java.io.OutputStream; - -import com.fasterxml.jackson.core.JsonGenerationException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectWriter; - -public class StandardObjectWriter extends ObjectWriter { - private static final long serialVersionUID = -8162644250351645123L; - - public StandardObjectWriter(ObjectWriter base) { - super(base, base.getConfig()); - } - - @Override - public void writeValue(OutputStream out, - Object value) throws IOException, JsonGenerationException, JsonMappingException { - Object[] values = (Object[]) value; - super.writeValue(out, values[0]); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/ProtobufSchemaUtils.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/ProtobufSchemaUtils.java deleted file mode 100644 index 0d8bcf8a25f..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/ProtobufSchemaUtils.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.utils; - -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.lang.reflect.Type; -import java.util.Collection; -import java.util.Date; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.util.ClassUtils; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.protostuff.Schema; -import io.protostuff.runtime.ProtobufCompatibleUtils; -import io.protostuff.runtime.RuntimeSchema; -import io.servicecomb.codec.protobuf.utils.schema.WrapSchemaFactory; -import io.servicecomb.common.javassist.JavassistUtils; -import io.servicecomb.core.definition.OperationMeta; - -public final class ProtobufSchemaUtils { - private static volatile Map schemaCache = new ConcurrentHashMap<>(); - - static { - ProtobufCompatibleUtils.init(); - } - - private interface SchemaCreator { - WrapSchema create() throws Exception; - } - - private ProtobufSchemaUtils() { - } - - private static WrapSchema getOrCreateSchema(String className, SchemaCreator creator) { - WrapSchema schema = schemaCache.get(className); - if (schema != null) { - return schema; - } - - synchronized (ProtobufSchemaUtils.class) { - schema = schemaCache.get(className); - if (schema != null) { - return schema; - } - - try { - schema = creator.create(); - } catch (Exception e) { - throw new Error(e); - } - schemaCache.put(className, schema); - return schema; - } - } - - private static boolean isArgsNeedWrap(Method method) { - if (method.getParameterCount() != 1) { - return true; - } - - // 单参数时,需要根据实际情况判断 - return isNeedWrap(method.getParameterTypes()[0]); - } - - private static boolean isNeedWrap(Class cls) { - // protobuf不支持原子类型、enum、string、数组、collection等等作为msg,只有Object类型才可以 - return ClassUtils.isPrimitiveOrWrapper(cls) || cls.isArray() || cls.isEnum() - || String.class.isAssignableFrom(cls) - || Collection.class.isAssignableFrom(cls) - || Map.class.isAssignableFrom(cls) - || Date.class.isAssignableFrom(cls); - } - - // 为了支持method args的场景,全部实现ProtobufMessageWrapper接口,有的场景有点浪费,不过无关紧要 - private static WrapSchema createWrapSchema(WrapClassConfig config) throws Exception { - Class cls = JavassistUtils.createClass(config); - Schema schema = RuntimeSchema.createFrom(cls); - return WrapSchemaFactory.createSchema(schema, config.getType()); - } - - // 适用于将单个类型包装的场景 - // 比如return - public static WrapSchema getOrCreateSchema(Type type) { - JavaType javaType = TypeFactory.defaultInstance().constructType(type); - // List -> java.util.List - // List> -> java.util.List> - String key = javaType.toCanonical(); - return getOrCreateSchema(key, () -> { - if (!isNeedWrap(javaType.getRawClass())) { - // 可以直接使用 - Schema schema = RuntimeSchema.createFrom(javaType.getRawClass()); - return WrapSchemaFactory.createSchema(schema, WrapType.NOT_WRAP); - } - - // 需要包装 - WrapClassConfig config = new WrapClassConfig(); - config.setType(WrapType.NORMAL_WRAP); - - config.setClassName("gen.wrap.protobuf." + key.replaceAll("[<>]", "_").replace("[", "array_")); - if (!Void.TYPE.isAssignableFrom(javaType.getRawClass())) { - config.addField("field0", javaType); - } - - JavassistUtils.genSingleWrapperInterface(config); - - return createWrapSchema(config); - }); - } - - public static WrapSchema getOrCreateArgsSchema(OperationMeta operationMeta) { - Method method = operationMeta.getMethod(); - String type = "gen." + method.getDeclaringClass().getName() + "." + method.getName() + ".Args"; - - return getOrCreateSchema(type, () -> { - if (!isArgsNeedWrap(method)) { - // 可以直接使用 - Class cls = (Class) method.getParameterTypes()[0]; - Schema schema = RuntimeSchema.createFrom(cls); - return WrapSchemaFactory.createSchema(schema, WrapType.ARGS_NOT_WRAP); - } - - // 需要包装 - WrapClassConfig config = new WrapClassConfig(); - config.setType(WrapType.ARGS_WRAP); - config.setClassName(type); - - Parameter[] params = method.getParameters(); - for (int idx = 0; idx < params.length; idx++) { - Parameter param = params[idx]; - String paramName = operationMeta.getParamName(idx); - - config.addField(paramName, param.getParameterizedType()); - } - - JavassistUtils.genMultiWrapperInterface(config); - - return createWrapSchema(config); - }); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/WrapClassConfig.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/WrapClassConfig.java deleted file mode 100644 index 6853992c463..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/WrapClassConfig.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.utils; - -import io.servicecomb.common.javassist.ClassConfig; - -public class WrapClassConfig extends ClassConfig { - private WrapType type; - - public WrapType getType() { - return type; - } - - public void setType(WrapType type) { - this.type = type; - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/WrapSchema.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/WrapSchema.java deleted file mode 100644 index 40baea7d26a..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/WrapSchema.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.utils; - -import java.nio.ByteBuffer; - -import io.protostuff.ByteBufferInput; -import io.protostuff.Input; -import io.protostuff.Output; -import io.protostuff.runtime.ProtobufFeature; -import io.protostuff.runtime.ProtobufFeatureUtils; -import io.vertx.core.buffer.Buffer; - -public interface WrapSchema { - @SuppressWarnings("unchecked") - default T readObject(Buffer buffer, ProtobufFeature protobufFeature) throws Exception { - if (buffer == null || buffer.length() == 0) { - // void以及函数入参为null的场景 - // 空串时,protobuf至少为编码为1字节 - return (T) readFromEmpty(); - } - - ByteBuffer nioBuffer = buffer.getByteBuf().nioBuffer(); - Input input = new ByteBufferInput(nioBuffer, false); - - ProtobufFeatureUtils.setProtobufFeature(protobufFeature); - try { - return (T) readObject(input); - } finally { - ProtobufFeatureUtils.removeProtobufFeature(); - } - } - - default void writeObject(Output output, Object value, ProtobufFeature protobufFeature) throws Exception { - ProtobufFeatureUtils.setProtobufFeature(protobufFeature); - try { - writeObject(output, value); - } finally { - ProtobufFeatureUtils.removeProtobufFeature(); - } - } - - Object readFromEmpty(); - - Object readObject(Input input) throws Exception; - - void writeObject(Output output, Object value) throws Exception; -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/WrapType.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/WrapType.java deleted file mode 100644 index 9e8b5b76f7b..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/WrapType.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.utils; - -public enum WrapType { - NOT_WRAP, - NORMAL_WRAP, - ARGS_NOT_WRAP, - ARGS_WRAP -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/AbstractWrapSchema.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/AbstractWrapSchema.java deleted file mode 100644 index 19f9186e64f..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/AbstractWrapSchema.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.utils.schema; - -import io.protostuff.Schema; -import io.servicecomb.codec.protobuf.utils.WrapSchema; - -public abstract class AbstractWrapSchema implements WrapSchema { - protected Schema schema; -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/ArgsNotWrapSchema.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/ArgsNotWrapSchema.java deleted file mode 100644 index 937036b3bf9..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/ArgsNotWrapSchema.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.utils.schema; - -import java.io.IOException; - -import io.protostuff.Input; -import io.protostuff.Output; -import io.protostuff.Schema; - -public class ArgsNotWrapSchema extends AbstractWrapSchema { - - @SuppressWarnings("unchecked") - public ArgsNotWrapSchema(Schema schema) { - this.schema = (Schema) schema; - } - - @Override - public Object readFromEmpty() { - return new Object[] {null}; - } - - public Object readObject(Input input) throws IOException { - Object readValue = schema.newMessage(); - schema.mergeFrom(input, readValue); - - return new Object[] {readValue}; - } - - public void writeObject(Output output, Object value) throws IOException { - Object writeValue = ((Object[]) value)[0]; - if (writeValue == null) { - return; - } - - schema.writeTo(output, writeValue); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/ArgsWrapSchema.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/ArgsWrapSchema.java deleted file mode 100644 index e93cd74c91c..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/ArgsWrapSchema.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.utils.schema; - -import java.io.IOException; - -import io.protostuff.Input; -import io.protostuff.Output; -import io.protostuff.Schema; -import io.servicecomb.common.javassist.MultiWrapper; - -public class ArgsWrapSchema extends AbstractWrapSchema { - - @SuppressWarnings("unchecked") - public ArgsWrapSchema(Schema schema) { - this.schema = (Schema) schema; - } - - @Override - public Object readFromEmpty() { - MultiWrapper wrapper = (MultiWrapper) schema.newMessage(); - return wrapper.readFields(); - } - - public Object readObject(Input input) throws IOException { - MultiWrapper wrapper = (MultiWrapper) schema.newMessage(); - schema.mergeFrom(input, wrapper); - - return wrapper.readFields(); - } - - public void writeObject(Output output, Object value) throws IOException { - MultiWrapper wrapper = (MultiWrapper) schema.newMessage(); - wrapper.writeFields((Object[]) value); - - schema.writeTo(output, wrapper); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/NormalWrapSchema.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/NormalWrapSchema.java deleted file mode 100644 index b0da5cc8028..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/NormalWrapSchema.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.utils.schema; - -import java.io.IOException; - -import io.protostuff.Input; -import io.protostuff.Output; -import io.protostuff.Schema; -import io.servicecomb.common.javassist.SingleWrapper; - -public class NormalWrapSchema extends AbstractWrapSchema { - - @SuppressWarnings("unchecked") - public NormalWrapSchema(Schema schema) { - this.schema = (Schema) schema; - } - - @Override - public Object readFromEmpty() { - SingleWrapper wrapper = (SingleWrapper) schema.newMessage(); - return wrapper.readField(); - } - - public Object readObject(Input input) throws IOException { - SingleWrapper wrapper = (SingleWrapper) schema.newMessage(); - schema.mergeFrom(input, wrapper); - - return wrapper.readField(); - } - - public void writeObject(Output output, Object value) throws IOException { - if (value == null) { - return; - } - - SingleWrapper wrapper = (SingleWrapper) schema.newMessage(); - wrapper.writeField(value); - - schema.writeTo(output, wrapper); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/NotWrapSchema.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/NotWrapSchema.java deleted file mode 100644 index df1b6a0ced0..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/NotWrapSchema.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.utils.schema; - -import java.io.IOException; - -import io.protostuff.Input; -import io.protostuff.Output; -import io.protostuff.Schema; - -public class NotWrapSchema extends AbstractWrapSchema { - - @SuppressWarnings("unchecked") - public NotWrapSchema(Schema schema) { - this.schema = (Schema) schema; - } - - @Override - public Object readFromEmpty() { - return null; - } - - public Object readObject(Input input) throws IOException { - Object value = schema.newMessage(); - schema.mergeFrom(input, value); - - return value; - } - - public void writeObject(Output output, Object value) throws IOException { - if (value == null) { - return; - } - - schema.writeTo(output, value); - } -} diff --git a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/WrapSchemaFactory.java b/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/WrapSchemaFactory.java deleted file mode 100644 index 7056e68f14a..00000000000 --- a/common/common-protobuf/src/main/java/io/servicecomb/codec/protobuf/utils/schema/WrapSchemaFactory.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.utils.schema; - -import io.protostuff.Schema; -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.servicecomb.codec.protobuf.utils.WrapType; - -public final class WrapSchemaFactory { - public static WrapSchema createSchema(Schema schema, WrapType type) { - switch (type) { - case NOT_WRAP: - return new NotWrapSchema(schema); - case NORMAL_WRAP: - return new NormalWrapSchema(schema); - case ARGS_NOT_WRAP: - return new ArgsNotWrapSchema(schema); - case ARGS_WRAP: - return new ArgsWrapSchema(schema); - default: - throw new Error("impossible"); - } - } - - private WrapSchemaFactory() { - } -} diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/HighwayJsonUtils.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/HighwayJsonUtils.java new file mode 100644 index 00000000000..bb9925308a6 --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/HighwayJsonUtils.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.codec.protobuf.definition; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Date; + +import org.apache.servicecomb.foundation.common.utils.RestObjectMapper; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * Add a separate converter for highway. Now highway compile types depends on swagger information, do not use any consumer information, + * This will cause any invocation will convert swagger raw types to consumer types. This will lose performance. + * + * In later improvements, need add consumer information to OperationProtobuf. + * + * Why not using RestObjectMapper? Because the reason above, highway use java.util.Date for date-time, and will serialize to a String + * that can not convert to LocalDateTime. (This is a jackson change to format java.util.Date to `2017-07-21T16:32:28.320+0000` + * but jackson can only convert `2017-07-21T16:32:28.320Z` to LocalDateTime + */ +public class HighwayJsonUtils { + + public static final ObjectMapper OBJ_MAPPER = new RestObjectMapper(); + + + private HighwayJsonUtils() { + + } + + @SuppressWarnings("unchecked") + public static T convertValue(Object fromValue, JavaType toValueType) { + if (fromValue == null) { + return null; + } + + if (TypeFactory.defaultInstance().constructType(LocalDateTime.class).equals(toValueType)) { + // jackson do not have a proper converter for this. + if (fromValue instanceof Date) { + return (T) LocalDateTime.ofInstant(Instant.ofEpochMilli(((Date) fromValue).getTime()), ZoneOffset.UTC); + } + } + return OBJ_MAPPER.convertValue(fromValue, toValueType); + } +} 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 new file mode 100644 index 00000000000..6c12da844de --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/OperationProtobuf.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.definition; + +import java.lang.reflect.Type; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.servicecomb.codec.protobuf.utils.ScopedProtobufSchemaManager; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; + +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; + +public final class OperationProtobuf { + private RequestRootSerializer requestRootSerializer; + + private RequestRootDeserializer requestRootDeserializer; + + private ResponseRootSerializer responseRootSerializer; + + private ResponseRootDeserializer responseRootDeserializer; + + private ResponseRootSerializer anyResponseRootSerializer; + + private ResponseRootDeserializer anyResponseRootDeserializer; + + public OperationProtobuf(ScopedProtobufSchemaManager scopedProtobufSchemaManager, Invocation invocation) { + initRequestCodec(scopedProtobufSchemaManager, invocation); + initResponseCodec(scopedProtobufSchemaManager, invocation); + } + + public RequestRootSerializer getRequestRootSerializer() { + return this.requestRootSerializer; + } + + public RequestRootDeserializer getRequestRootDeserializer() { + return this.requestRootDeserializer; + } + + public ResponseRootSerializer findResponseRootSerializer(int statusCode) { + if (Family.SUCCESSFUL.equals(Family.familyOf(statusCode))) { + return this.responseRootSerializer; + } + return anyResponseRootSerializer; + } + + public ResponseRootDeserializer findResponseRootDeserializer(int statusCode) { + if (Family.SUCCESSFUL.equals(Family.familyOf(statusCode))) { + return this.responseRootDeserializer; + } + return anyResponseRootDeserializer; + } + + private void initProducerRequestCodec(Invocation invocation, Message requestMessage, ProtoMapper mapper) { + Map swaggerParameterTypes = invocation.getOperationMeta().getSwaggerProducerOperation() + .getSwaggerParameterTypes(); + if (ProtoUtils.isWrapArguments(requestMessage)) { + requestRootDeserializer = new RequestRootDeserializer<>( + mapper.createRootDeserializer(requestMessage, swaggerParameterTypes), true, null); + } else { + if (swaggerParameterTypes.isEmpty()) { + requestRootDeserializer = new RequestRootDeserializer<>( + mapper.createRootDeserializer(requestMessage, Object.class), false, null); + } else if (swaggerParameterTypes.size() == 1) { + Entry entry = swaggerParameterTypes.entrySet().iterator().next(); + requestRootDeserializer = new RequestRootDeserializer<>(mapper.createRootDeserializer(requestMessage, + entry.getValue()), false, entry.getKey()); + } else { + throw new IllegalStateException( + "unexpected operation definition " + invocation.getOperationMeta().getMicroserviceQualifiedName()); + } + } + } + + private void initConsumerRequestCodec(Invocation invocation, Message requestMessage, ProtoMapper mapper) { + if (ProtoUtils.isWrapArguments(requestMessage)) { + requestRootSerializer = new RequestRootSerializer( + mapper.createRootSerializer(requestMessage, Object.class), true, false); + } else { + if (invocation.getOperationMeta().parameterCount() == 0) { + requestRootSerializer = new RequestRootSerializer(mapper.createRootSerializer(requestMessage, Object.class), + false, false); + } else if (invocation.getOperationMeta().parameterCount() == 1) { + requestRootSerializer = new RequestRootSerializer(mapper.createRootSerializer(requestMessage, + Object.class), false, true); + } else { + throw new IllegalStateException( + "unexpected operation definition " + invocation.getOperationMeta().getMicroserviceQualifiedName()); + } + } + } + + private void initRequestCodec(ScopedProtobufSchemaManager scopedProtobufSchemaManager, Invocation invocation) { + ProtoMapper mapper = scopedProtobufSchemaManager.getOrCreateProtoMapper(invocation.getSchemaMeta()); + Message requestMessage = mapper.getRequestMessage(invocation.getOperationMeta().getOperationId()); + + if (invocation.isProducer()) { + initProducerRequestCodec(invocation, requestMessage, mapper); + } else { + initConsumerRequestCodec(invocation, requestMessage, mapper); + } + } + + private void initProviderResponseCode(Message responseMessage, ProtoMapper mapper, + JavaType responseType) { + if (ProtoUtils.isWrapProperty(responseMessage)) { + responseRootSerializer = new ResponseRootSerializer( + mapper.createRootSerializer(responseMessage, responseType), true, false); + } else { + if (ProtoUtils.isEmptyMessage(responseMessage)) { + responseRootSerializer = new ResponseRootSerializer(mapper.createRootSerializer(responseMessage, + Object.class), false, false); + } else { + responseRootSerializer = new ResponseRootSerializer(mapper.createRootSerializer(responseMessage, + responseType), false, true); + } + } + } + + private void initConsumerResponseCode(Message responseMessage, ProtoMapper mapper, + JavaType responseType) { + if (ProtoUtils.isWrapProperty(responseMessage)) { + responseRootSerializer = new ResponseRootSerializer( + mapper.createRootSerializer(responseMessage, responseType), true, false); + responseRootDeserializer = new ResponseRootDeserializer<>( + mapper.createRootDeserializer(responseMessage, responseType), false); + } else { + if (ProtoUtils.isEmptyMessage(responseMessage)) { + responseRootSerializer = new ResponseRootSerializer(mapper.createRootSerializer(responseMessage, + Object.class), false, false); + responseRootDeserializer = new ResponseRootDeserializer<>( + mapper.createRootDeserializer(responseMessage, Object.class), true); + } else { + responseRootSerializer = new ResponseRootSerializer(mapper.createRootSerializer(responseMessage, + responseType), false, false); + responseRootDeserializer = new ResponseRootDeserializer<>( + mapper.createRootDeserializer(responseMessage, responseType), false); + } + } + } + + private void initResponseCodec(ScopedProtobufSchemaManager scopedProtobufSchemaManager, Invocation invocation) { + ProtoMapper mapper = scopedProtobufSchemaManager.getOrCreateProtoMapper(invocation.getSchemaMeta()); + Message responseMessage = mapper.getResponseMessage(invocation.getOperationMeta().getOperationId()); + + JavaType responseType = invocation.findResponseType(Status.OK.getStatusCode()); + if (invocation.isProducer()) { + initProviderResponseCode(responseMessage, mapper, responseType); + } else { + initConsumerResponseCode(responseMessage, mapper, responseType); + } + anyResponseRootSerializer = new ResponseRootSerializer(mapper.createRootSerializer(ProtoConst.ANY, + Object.class), false, true); + anyResponseRootDeserializer = new ResponseRootDeserializer<>( + mapper.createRootDeserializer(ProtoConst.ANY, Object.class), false); + } +} 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 new file mode 100644 index 00000000000..09ccb9d70cd --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ProtobufManager.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.codec.protobuf.definition; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import jakarta.ws.rs.core.Response.Status; + +import org.apache.servicecomb.codec.protobuf.utils.ScopedProtobufSchemaManager; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.swagger.invocation.InvocationType; + +import com.fasterxml.jackson.databind.JavaType; +import com.google.common.annotations.VisibleForTesting; + +public final class ProtobufManager { + public static final String EXT_ID = "protobuf"; + + private static final Object LOCK = new Object(); + + static class RuntimeCacheKey { + final InvocationType invocationType; + + final String uniqueOperationId; + + // Using response type as the cache key. + // May consider request type as well, but now not implemented + final JavaType responseType; + + public RuntimeCacheKey(InvocationType invocationType, String operationId, JavaType responseType) { + this.invocationType = invocationType; + this.uniqueOperationId = operationId; + this.responseType = responseType; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + RuntimeCacheKey that = (RuntimeCacheKey) o; + + if (invocationType != that.invocationType) { + return false; + } + if (!uniqueOperationId.equals(that.uniqueOperationId)) { + return false; + } + return Objects.equals(responseType, that.responseType); + } + + @Override + public int hashCode() { + int result = invocationType.hashCode(); + result = 31 * result + uniqueOperationId.hashCode(); + result = 31 * result + (responseType != null ? responseType.hashCode() : 0); + return result; + } + } + + private static final Map RUNTIME_CACHE = new HashMap<>(); + + public static OperationProtobuf getOrCreateOperation(Invocation invocation) { + RuntimeCacheKey cacheKey = new RuntimeCacheKey(invocation.getInvocationType(), + invocation.getOperationMeta().getMicroserviceQualifiedName(), + invocation.findResponseType(Status.OK.getStatusCode())); + OperationProtobuf operationProtobuf = RUNTIME_CACHE.get(cacheKey); + if (operationProtobuf == null) { + synchronized (LOCK) { + MicroserviceMeta microserviceMeta = invocation.getMicroserviceMeta(); + ScopedProtobufSchemaManager scopedProtobufSchemaManager = microserviceMeta.getExtData(EXT_ID); + if (scopedProtobufSchemaManager == null) { + scopedProtobufSchemaManager = new ScopedProtobufSchemaManager(); + microserviceMeta.putExtData(EXT_ID, scopedProtobufSchemaManager); + } + + operationProtobuf = RUNTIME_CACHE.get(cacheKey); + if (operationProtobuf == null) { + operationProtobuf = new OperationProtobuf(scopedProtobufSchemaManager, invocation); + RUNTIME_CACHE.put(cacheKey, operationProtobuf); + } + } + } + + return operationProtobuf; + } + + @VisibleForTesting + public static void clear() { + RUNTIME_CACHE.clear(); + } +} diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootDeserializer.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootDeserializer.java new file mode 100644 index 00000000000..ef4e75befdb --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootDeserializer.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.codec.protobuf.definition; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; + +public class RequestRootDeserializer { + private final boolean wrapArgument; + + private final String parameterName; + + private final RootDeserializer rootDeserializer; + + public RequestRootDeserializer(RootDeserializer rootDeserializer, boolean wrapArgument, String parameterName) { + this.rootDeserializer = rootDeserializer; + this.wrapArgument = wrapArgument; + this.parameterName = parameterName; + } + + @SuppressWarnings("unchecked") + public Map deserialize(byte[] bytes) throws IOException { + if (!wrapArgument) { + Map result = new HashMap<>(1); + Object obj = rootDeserializer.deserialize(bytes); + if (obj instanceof PropertyWrapper) { + obj = ((PropertyWrapper) obj).getValue(); + } + if (parameterName == null) { + return result; + } + result.put(parameterName, obj); + return result; + } else { + return (Map) rootDeserializer.deserialize(bytes); + } + } +} 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 new file mode 100644 index 00000000000..ac5763d9b57 --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootSerializer.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.definition; + +import java.io.IOException; +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.RootSerializer; + +import io.vertx.core.json.JsonObject; + +public class RequestRootSerializer { + private final RootSerializer rootSerializer; + + private final boolean noTypesInfo; + + private final boolean isWrap; + + public RequestRootSerializer(RootSerializer serializer, boolean isWrap, boolean noTypesInfo) { + this.rootSerializer = serializer; + this.noTypesInfo = noTypesInfo; + this.isWrap = isWrap; + } + + @SuppressWarnings("unchecked") + public byte[] serialize(Object value) throws IOException { + if (noTypesInfo && !isWrap) { + Object param = ((Map) value).values().iterator().next(); + if (param instanceof JsonObject) { + param = ((JsonObject) param).getMap(); + } + return this.rootSerializer.serialize(param); + } else { + return this.rootSerializer.serialize(value); + } + } +} diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootDeserializer.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootDeserializer.java new file mode 100644 index 00000000000..e64e45d8c62 --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootDeserializer.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.codec.protobuf.definition; + +import java.io.IOException; + +import org.apache.commons.lang3.ClassUtils; +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; + +import com.fasterxml.jackson.databind.JavaType; + +public class ResponseRootDeserializer { + private final RootDeserializer rootDeserializer; + + private final boolean empty; + + public ResponseRootDeserializer(RootDeserializer rootDeserializer, boolean empty) { + this.rootDeserializer = rootDeserializer; + this.empty = empty; + } + + @SuppressWarnings("unchecked") + public T deserialize(byte[] bytes, JavaType invocationTimeType) throws IOException { + if (empty) { + rootDeserializer.deserialize(bytes); // read buffers if possible. + return null; + } + + Object obj = rootDeserializer.deserialize(bytes); + if (obj instanceof PropertyWrapper) { + obj = ((PropertyWrapper) obj).getValue(); + } + if (needConvert(obj, invocationTimeType)) { + obj = HighwayJsonUtils.convertValue(obj, invocationTimeType); + } + return (T) obj; + } + + public static boolean needConvert(Object obj, JavaType invocationTimeType) { + if (obj == null || ClassUtils.isPrimitiveOrWrapper(obj.getClass()) || invocationTimeType.isPrimitive() + || ProtoConst.OBJECT_TYPE.equals(invocationTimeType)) { + return false; + } + + if (obj.getClass() == invocationTimeType.getRawClass()) { + return false; + } + + if (invocationTimeType.getRawClass().isAssignableFrom(obj.getClass())) { + if (invocationTimeType.getContentType() == null) { + return false; + } + } + + return true; + } +} 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 new file mode 100644 index 00000000000..8af18a4a016 --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootSerializer.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.codec.protobuf.definition; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.RootSerializer; + +public class ResponseRootSerializer { + private final RootSerializer rootSerializer; + + private final boolean noTypesInfo; + + private final boolean isWrap; + + public ResponseRootSerializer(RootSerializer serializer, boolean isWrap, boolean noTypesInfo) { + this.rootSerializer = serializer; + this.noTypesInfo = noTypesInfo; + this.isWrap = isWrap; + } + + public byte[] serialize(Object value) throws IOException { + if (noTypesInfo && !isWrap) { + return this.rootSerializer.serialize(value); + } else { + Map responseValue = new HashMap<>(1); + // key is fixed to "value" in IDL + responseValue.put("value", value); + return this.rootSerializer.serialize(responseValue); + } + } +} 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 new file mode 100644 index 00000000000..885dae823c9 --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoMethod.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.codec.protobuf.internal.converter; + +import java.util.HashMap; +import java.util.Map; +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 final Map responses = new HashMap<>(); + + private ProtoResponse defaultResponse; + + public String getArgTypeName() { + return argTypeName; + } + + public void setArgTypeName(String argTypeName) { + this.argTypeName = argTypeName; + } + + public void addResponse(String status, ProtoResponse response) { + if (status.equals("default")) { + defaultResponse = response; + return; + } + + int statusCode = Integer.parseInt(status); + responses.put(statusCode, response); + + if (defaultResponse == null && statusCode == Status.OK.getStatusCode()) { + defaultResponse = response; + } + } + + public ProtoResponse findResponse(int statusCode) { + ProtoResponse response = responses.get(statusCode); + if (response != null) { + 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()); + } + + return defaultResponse; + } +} diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoResponse.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoResponse.java new file mode 100644 index 00000000000..19510f62d10 --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoResponse.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.internal.converter; + +public class ProtoResponse { + private String typeName; + + public String getTypeName() { + return typeName; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } +} diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoToStringGenerator.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoToStringGenerator.java new file mode 100644 index 00000000000..589cad8a12d --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/ProtoToStringGenerator.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; + +import java.util.List; + +import com.google.common.base.Strings; + +import io.protostuff.compiler.model.Enum; +import io.protostuff.compiler.model.EnumConstant; +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.Import; +import io.protostuff.compiler.model.Message; +import io.protostuff.compiler.model.Proto; +import io.protostuff.compiler.model.Service; +import io.protostuff.compiler.model.ServiceMethod; + +public class ProtoToStringGenerator { + private final Proto proto; + + public ProtoToStringGenerator(Proto proto) { + this.proto = proto; + } + + public String protoToString() { + StringBuilder sb = new StringBuilder(); + appendLine(sb, "syntax = \"%s\";", proto.getSyntax()); + for (Import importValue : proto.getImports()) { + appendLine(sb, "import \"%s\";", importValue.getValue()); + } + appendLine(sb, "package %s;\n", proto.getPackage().getValue()); + + for (Message message : proto.getMessages()) { + messageToString(message, sb); + } + + for (Enum enumValue : proto.getEnums()) { + enumToString(enumValue, sb); + } + + for (Service service : proto.getServices()) { + serviceToString(service, sb); + } + return sb.toString(); + } + + private void serviceToString(Service service, StringBuilder sb) { + appendLine(sb, "service %s {", service.getName()); + for (ServiceMethod serviceMethod : service.getMethods()) { + commentsToString(serviceMethod.getCommentLines(), sb, 2); + appendLine(sb, " rpc %s (%s) returns (%s);\n", serviceMethod.getName(), serviceMethod.getArgTypeName(), + serviceMethod.getReturnTypeName()); + } + if (!service.getMethods().isEmpty()) { + sb.setLength(sb.length() - 1); + } + appendLine(sb, "}"); + } + + protected void enumToString(Enum enumValue, StringBuilder sb) { + appendLine(sb, "enum %s {", enumValue.getName()); + for (EnumConstant enumConstant : enumValue.getConstants()) { + appendLine(sb, " %s = %s;", enumConstant.getName(), enumConstant.getValue()); + } + sb.append("}\n\n"); + } + + private void commentsToString(List comments, StringBuilder sb, int padLeft) { + if (comments.isEmpty()) { + return; + } + + String pad = Strings.repeat(" ", padLeft) + "//"; + for (String comment : comments) { + sb.append(pad); + appendLine(sb, comment); + } + } + + private void messageToString(Message message, StringBuilder sb) { + commentsToString(message.getCommentLines(), sb, 0); + appendLine(sb, "message %s {", message.getName()); + for (Field field : message.getFields()) { + sb.append(" "); + fieldToString(field, field.isRepeated(), sb); + } + appendLine(sb, "}\n"); + } + + private void fieldToString(Field field, boolean repeated, StringBuilder sb) { + if (field.isMap()) { + fieldMapToString(field, sb); + return; + } + + if (repeated) { + fieldRepeatedToString(field, sb); + return; + } + + appendLine(sb, "%s %s = %d;", field.getTypeName(), field.getName(), field.getTag()); + } + + private void fieldRepeatedToString(Field field, StringBuilder sb) { + sb.append("repeated "); + fieldToString(field, false, sb); + } + + private void fieldMapToString(Field field, StringBuilder sb) { + Message entryMessage = (Message) field.getType(); + Field keyField = entryMessage.getField(1); + Field valueField = entryMessage.getField(2); + + // map name = 1; + appendLine(sb, "map<%s, %s> %s = %d;", keyField.getTypeName(), valueField.getTypeName(), field.getName(), + field.getTag()); + } +} 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/SwaggerToProtoGenerator.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SwaggerToProtoGenerator.java new file mode 100644 index 00000000000..482808ab816 --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SwaggerToProtoGenerator.java @@ -0,0 +1,450 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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.HashSet; +import java.util.LinkedHashMap; +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.generator.SwaggerConst; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.hash.Hashing; + +import io.protostuff.compiler.model.Message; +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.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 OpenAPI swagger; + + private final StringBuilder msgStringBuilder = new StringBuilder(); + + private final StringBuilder serviceBuilder = new StringBuilder(); + + private final Set imports = new HashSet<>(); + + private final Set messages = new HashSet<>(); + + private List pending = new ArrayList<>(); + + // not java package + // better to be: app_${app}.mid_{microservice}.sid_{schemaId} + public SwaggerToProtoGenerator(String protoPackage, OpenAPI swagger) { + this.protoPackage = escapePackageName(protoPackage); + this.swagger = swagger; + } + + public Proto convert() { + convertDefinitions(); + convertOperations(); + do { + List oldPending = pending; + pending = new ArrayList<>(); + for (Runnable runnable : oldPending) { + runnable.run(); + } + } while (!pending.isEmpty()); + + return createProto(); + } + + public static String escapePackageName(String name) { + return name.replaceAll("[\\-:]", "_"); + } + + public static String escapeMessageName(String name) { + return name.replaceAll("\\.", "_"); + } + + public static boolean isValidEnum(String name) { + return !name.contains(".") && !name.contains("-"); + } + + private void convertDefinitions() { + if (swagger.getComponents() == null || swagger.getComponents().getSchemas() == null) { + return; + } + + for (Entry entry : swagger.getComponents().getSchemas().entrySet()) { + convertDefinition(entry.getKey(), entry.getValue()); + } + } + + @SuppressWarnings("unchecked") + private void convertDefinition(String modelName, Schema model) { + Map properties = model.getProperties(); + if (properties == null) { + // it's a empty message + properties = Collections.emptyMap(); + } + + createMessage(modelName, properties); + } + + private void createMessage(String protoName, Map properties, String... annotations) { + if (!messages.add(protoName)) { + // already created + return; + } + + 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 = convertSwaggerType(property); + + appendLine(msgStringBuilder, " %s %s = %d;", propertyType, entry.getKey(), tag); + tag++; + } + appendLine(msgStringBuilder, "}"); + } + + private void addImports(Proto proto) { + imports.add(proto.getFilename()); + for (Message message : proto.getMessages()) { + messages.add(message.getCanonicalName()); + } + } + + private String convertSwaggerType(Object swaggerType) { + if (swaggerType == null) { + // void + addImports(ProtoConst.EMPTY_PROTO); + return ProtoConst.EMPTY.getCanonicalName(); + } + + SwaggerTypeAdapter adapter = SwaggerTypeAdapter.create(swaggerType); + String type = tryFindEnumType(adapter.getEnum()); + if (type != null) { + return type; + } + + type = findBaseType(adapter.getType(), adapter.getFormat()); + if (type != null) { + return type; + } + + type = adapter.getRefType(); + if (type != null) { + return type.substring(Components.COMPONENTS_SCHEMAS_REF.length()); + } + + Schema itemProperty = adapter.getArrayItem(); + if (itemProperty != null) { + return "repeated " + convertArrayOrMapItem(itemProperty); + } + + itemProperty = adapter.getMapItem(); + if (itemProperty != null) { + return String.format("map", convertArrayOrMapItem(itemProperty)); + } + + if (adapter.isJavaLangObject()) { + addImports(ProtoConst.ANY_PROTO); + return ProtoConst.ANY.getCanonicalName(); + } + + throw new IllegalStateException(String + .format("not support swagger type, class=%s, content=%s.", swaggerType.getClass().getName(), + Json.encode(swaggerType))); + } + + private String convertArrayOrMapItem(Schema itemProperty) { + SwaggerTypeAdapter itemAdapter = SwaggerTypeAdapter.create(itemProperty); + // List>, need to wrap + if (itemAdapter.getArrayItem() != null) { + String protoName = generateWrapPropertyName(List.class.getSimpleName(), itemAdapter.getArrayItem()); + pending.add(() -> wrapPropertyToMessage(protoName, itemProperty)); + return protoName; + } + + // List>, need to wrap + if (itemAdapter.getMapItem() != null) { + String protoName = generateWrapPropertyName(Map.class.getSimpleName(), itemAdapter.getMapItem()); + pending.add(() -> wrapPropertyToMessage(protoName, itemProperty)); + return protoName; + } + + return convertSwaggerType(itemProperty); + } + + private String generateWrapPropertyName(String prefix, Schema property) { + SwaggerTypeAdapter adapter = SwaggerTypeAdapter.create(property); + // List>, need to wrap + if (adapter.getArrayItem() != null) { + return generateWrapPropertyName(prefix + List.class.getSimpleName(), adapter.getArrayItem()); + } + + // List>, need to wrap + if (adapter.getMapItem() != null) { + return generateWrapPropertyName(prefix + Map.class.getSimpleName(), adapter.getMapItem()); + } + + // message name cannot have . (package separator) + return prefix + StringUtils.capitalize(escapeMessageName(convertSwaggerType(adapter))); + } + + private void wrapPropertyToMessage(String protoName, Schema property) { + createMessage(protoName, Collections.singletonMap("value", property), ProtoConst.ANNOTATION_WRAP_PROPERTY); + } + + 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 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, "}"); + } + + 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 void convertOperations() { + Paths paths = swagger.getPaths(); + if (paths == null || paths.isEmpty()) { + return; + } + + appendLine(serviceBuilder, "service MainService {"); + 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"); + continue; + } else if (isDownload(operation)) { + LOGGER.warn("Not support operation for highway {}.{}, {}", this.protoPackage, operation.getOperationId(), + "file download not supported"); + continue; + } + try { + convertOperation(operation); + } catch (Exception e) { + LOGGER.error("Not support operation for highway {}.{}", this.protoPackage, operation.getOperationId(), e); + } + } + } + + serviceBuilder.setLength(serviceBuilder.length() - 1); + + appendLine(serviceBuilder, "}"); + } + + private boolean isUpload(Operation operation) { + return operation.getRequestBody() != null && operation.getRequestBody().getContent() != null + && operation.getRequestBody().getContent().get(MediaType.MULTIPART_FORM_DATA) != null; + } + + private boolean isDownload(Operation operation) { + 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) { + ProtoMethod protoMethod = new ProtoMethod(); + fillRequestType(operation, protoMethod); + fillResponseType(operation, protoMethod); + + appendLine(serviceBuilder, " //%s%s", ProtoConst.ANNOTATION_RPC, Json.encode(protoMethod)); + appendLine(serviceBuilder, " rpc %s (%s) returns (%s);\n", operation.getOperationId(), + protoMethod.getArgTypeName(), + protoMethod.findResponse(Status.OK.getStatusCode()).getTypeName()); + } + + private void fillRequestType(Operation operation, ProtoMethod protoMethod) { + int parametersCount = parametersCount(operation); + if (parametersCount == 0) { + addImports(ProtoConst.EMPTY_PROTO); + protoMethod.setArgTypeName(ProtoConst.EMPTY.getCanonicalName()); + return; + } + + if (parametersCount == 1) { + String type = convertSwaggerType(oneSchema(operation)); + if (messages.contains(type)) { + protoMethod.setArgTypeName(type); + return; + } + } + + String wrapName = StringUtils.capitalize(operation.getOperationId()) + "RequestWrap"; + 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()) { + 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(); + protoResponse.setTypeName(type); + + if (wrapped) { + String wrapName = StringUtils.capitalize(operation.getOperationId()) + "ResponseWrap" + entry.getKey(); + wrapPropertyToMessage(wrapName, schema); + + protoResponse.setTypeName(wrapName); + } + protoMethod.addResponse(entry.getKey(), protoResponse); + } + } + + private void createWrapArgs(String wrapName, Map properties) { + createMessage(wrapName, properties, ProtoConst.ANNOTATION_WRAP_ARGUMENTS); + } + + protected Proto createProto() { + StringBuilder sb = new StringBuilder(); + appendLine(sb, "syntax = \"proto3\";"); + for (String importMsg : imports) { + appendLine(sb, "import \"%s\";", importMsg); + } + if (StringUtils.isNotEmpty(protoPackage)) { + sb.append("package ").append(protoPackage).append(";\n"); + } + sb.append(msgStringBuilder); + sb.append(serviceBuilder); + + ProtoParser protoParser = new ProtoParser(); + return protoParser.parseFromContent(sb.toString()); + } +} 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 new file mode 100644 index 00000000000..80a20fd2822 --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/internal/converter/SwaggerTypeAdapter.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.codec.protobuf.internal.converter; + +import java.util.List; + +import io.swagger.v3.oas.models.media.Schema; + +public interface SwaggerTypeAdapter { + static SwaggerTypeAdapter create(Object swaggerType) { + if (swaggerType instanceof SwaggerTypeAdapter) { + return (SwaggerTypeAdapter) swaggerType; + } + + if (swaggerType instanceof Schema) { + return new SchemaSwaggerTypeAdapter((Schema) swaggerType); + } + + throw new IllegalStateException("not support swagger type: " + swaggerType.getClass().getName()); + } + + String getRefType(); + + Schema getArrayItem(); + + Schema getMapItem(); + + List getEnum(); + + String getType(); + + String getFormat(); + + boolean isJavaLangObject(); +} 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 new file mode 100644 index 00000000000..3c7e5adbb64 --- /dev/null +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.utils; + +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.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; + +/** + * Manage swagger -> protoBuffer mappings. + * + * This class have the same lifecycle as MicroserviceMeta, so we need to create an instance + * 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() { + + } + + /** + * get the ProtoMapper from Swagger + */ + public ProtoMapper getOrCreateProtoMapper(SchemaMeta schemaMeta) { + return mapperCache.computeIfAbsent(schemaMeta.getSchemaId(), key -> { + OpenAPI swagger = schemaMeta.getSwagger(); + SwaggerToProtoGenerator generator = new SwaggerToProtoGenerator(schemaMeta.getMicroserviceQualifiedName(), + swagger); + Proto proto = generator.convert(); + ProtoMapperFactory protoMapperFactory = new ProtoMapperFactory(); + 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/io/protostuff/runtime/TestProtobufCompatibleUtils.java b/common/common-protobuf/src/test/java/io/protostuff/runtime/TestProtobufCompatibleUtils.java deleted file mode 100644 index 49247edff11..00000000000 --- a/common/common-protobuf/src/test/java/io/protostuff/runtime/TestProtobufCompatibleUtils.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.protostuff.runtime; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - -import io.protostuff.ByteArrayInput; -import io.protostuff.LinkedBuffer; -import io.protostuff.ProtobufOutput; -import io.protostuff.runtime.model.ModelProtobuf; -import io.protostuff.runtime.model.ModelProtostuff; -import io.protostuff.runtime.model.User; - -public class TestProtobufCompatibleUtils { - @Test - public void test() throws IOException { - ProtobufCompatibleUtils.init(); - - Map stringMap = new HashMap<>(); - stringMap.put("k1", "v1"); - stringMap.put("k2", "v2"); - - Map userMap = new HashMap<>(); - userMap.put("u1", new User("n1")); - userMap.put("u2", new User("n2")); - - byte[] protostuffBytes = testProtostuff(stringMap, userMap); - - io.protostuff.runtime.model.ModelProtobuf.RequestHeader r = - ModelProtobuf.RequestHeader.newBuilder() - .putAllCseContext(stringMap) - .putUserMap("u1", ModelProtobuf.User.newBuilder().setName("n1").build()) - .putUserMap("u2", ModelProtobuf.User.newBuilder().setName("n2").build()) - .addList("l1") - .addList("l2") - .build(); - - byte[] protoBufBytes = r.toByteArray(); - Assert.assertArrayEquals(protostuffBytes, protoBufBytes); - } - - protected byte[] testProtostuff(Map map, Map userMap) throws IOException { - ProtobufCompatibleUtils.init(); - - RuntimeSchema schema = RuntimeSchema.createFrom(ModelProtostuff.class); - ModelProtostuff model = new ModelProtostuff(); - - model.setContext(map); - model.setUserMap(userMap); - model.getList().add("l1"); - model.getList().add("l2"); - - LinkedBuffer linkedBuffer = LinkedBuffer.allocate(); - ProtobufOutput output = new ProtobufOutput(linkedBuffer); - ProtobufFeature protobufFeature = new ProtobufFeature(); - protobufFeature.setUseProtobufMapCodec(true); - - ProtobufFeatureUtils.setProtobufFeature(protobufFeature); - schema.writeTo(output, model); - - ByteArrayOutputStream s = new ByteArrayOutputStream(); - LinkedBuffer.writeTo(s, linkedBuffer); - byte[] bytes = s.toByteArray(); - - ModelProtostuff newModel = new ModelProtostuff(); - ByteArrayInput bai = new ByteArrayInput(bytes, false); - - schema.mergeFrom(bai, newModel); - - ProtobufFeatureUtils.removeProtobufFeature(); - Assert.assertEquals("v1", newModel.getContext().get("k1")); - Assert.assertEquals("v2", newModel.getContext().get("k2")); - Assert.assertEquals("n1", newModel.getUserMap().get("u1").getName()); - Assert.assertEquals("n2", newModel.getUserMap().get("u2").getName()); - Assert.assertEquals("l1", newModel.getList().get(0)); - Assert.assertEquals("l2", newModel.getList().get(1)); - - return bytes; - } -} diff --git a/common/common-protobuf/src/test/java/io/protostuff/runtime/model/ModelProtobuf.java b/common/common-protobuf/src/test/java/io/protostuff/runtime/model/ModelProtobuf.java deleted file mode 100644 index 340feee8bbd..00000000000 --- a/common/common-protobuf/src/test/java/io/protostuff/runtime/model/ModelProtobuf.java +++ /dev/null @@ -1,2881 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: ModelProtobuf.proto - -package io.protostuff.runtime.model; - -public final class ModelProtobuf { - private ModelProtobuf() { - } - - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistryLite registry) { - } - - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - registerAllExtensions( - (com.google.protobuf.ExtensionRegistryLite) registry); - } - - public interface UserOrBuilder extends - // @@protoc_insertion_point(interface_extends:io.protostuff.runtime.model.User) - com.google.protobuf.MessageOrBuilder { - - /** - * string name = 1; - */ - java.lang.String getName(); - - /** - * string name = 1; - */ - com.google.protobuf.ByteString getNameBytes(); - } - - /** - * Protobuf type {@code io.protostuff.runtime.model.User} - */ - public static final class User extends - com.google.protobuf.GeneratedMessageV3 implements - // @@protoc_insertion_point(message_implements:io.protostuff.runtime.model.User) - UserOrBuilder { - // Use User.newBuilder() to construct. - private User(com.google.protobuf.GeneratedMessageV3.Builder builder) { - super(builder); - } - - private User() { - name_ = ""; - } - - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet getUnknownFields() { - return com.google.protobuf.UnknownFieldSet.getDefaultInstance(); - } - - @SuppressWarnings("unused") - private User( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - this(); - int mutable_bitField0_ = 0; - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!input.skipField(tag)) { - done = true; - } - break; - } - case 10: { - java.lang.String s = input.readStringRequireUtf8(); - - name_ = s; - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e).setUnfinishedMessage(this); - } finally { - makeExtensionsImmutable(); - } - } - - public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.protostuff.runtime.model.ModelProtobuf.internal_static_io_protostuff_runtime_model_User_descriptor; - } - - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() { - return io.protostuff.runtime.model.ModelProtobuf.internal_static_io_protostuff_runtime_model_User_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.protostuff.runtime.model.ModelProtobuf.User.class, - io.protostuff.runtime.model.ModelProtobuf.User.Builder.class); - } - - public static final int NAME_FIELD_NUMBER = 1; - - private volatile java.lang.Object name_; - - /** - * string name = 1; - */ - public java.lang.String getName() { - java.lang.Object ref = name_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - name_ = s; - return s; - } - } - - /** - * string name = 1; - */ - public com.google.protobuf.ByteString getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private byte memoizedIsInitialized = -1; - - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized == 1) { - return true; - } - if (isInitialized == 0) { - return false; - } - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - if (!getNameBytes().isEmpty()) { - com.google.protobuf.GeneratedMessageV3.writeString(output, 1, name_); - } - } - - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) { - return size; - } - - size = 0; - if (!getNameBytes().isEmpty()) { - size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, name_); - } - memoizedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - - @java.lang.Override - public boolean equals(final java.lang.Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof io.protostuff.runtime.model.ModelProtobuf.User)) { - return super.equals(obj); - } - io.protostuff.runtime.model.ModelProtobuf.User other = - (io.protostuff.runtime.model.ModelProtobuf.User) obj; - - boolean result = true; - result = result && getName() - .equals(other.getName()); - return result; - } - - @SuppressWarnings("unchecked") - @java.lang.Override - public int hashCode() { - if (memoizedHashCode != 0) { - return memoizedHashCode; - } - int hash = 41; - hash = (19 * hash) + getDescriptor().hashCode(); - hash = (37 * hash) + NAME_FIELD_NUMBER; - hash = (53 * hash) + getName().hashCode(); - hash = (29 * hash) + unknownFields.hashCode(); - memoizedHashCode = hash; - return hash; - } - - public static io.protostuff.runtime.model.ModelProtobuf.User parseFrom( - java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - - public static io.protostuff.runtime.model.ModelProtobuf.User parseFrom( - java.nio.ByteBuffer data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - - public static io.protostuff.runtime.model.ModelProtobuf.User parseFrom( - com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - - public static io.protostuff.runtime.model.ModelProtobuf.User parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - - public static io.protostuff.runtime.model.ModelProtobuf.User parseFrom( - byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - - public static io.protostuff.runtime.model.ModelProtobuf.User parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - - public static io.protostuff.runtime.model.ModelProtobuf.User parseFrom( - java.io.InputStream input) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - - public static io.protostuff.runtime.model.ModelProtobuf.User parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public static io.protostuff.runtime.model.ModelProtobuf.User parseDelimitedFrom( - java.io.InputStream input) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input); - } - - public static io.protostuff.runtime.model.ModelProtobuf.User parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); - } - - public static io.protostuff.runtime.model.ModelProtobuf.User parseFrom( - com.google.protobuf.CodedInputStream input) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - - public static io.protostuff.runtime.model.ModelProtobuf.User parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public Builder newBuilderForType() { - return newBuilder(); - } - - public static Builder newBuilder() { - return DEFAULT_INSTANCE.toBuilder(); - } - - public static Builder newBuilder(io.protostuff.runtime.model.ModelProtobuf.User prototype) { - return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); - } - - public Builder toBuilder() { - return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); - } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - - /** - * Protobuf type {@code io.protostuff.runtime.model.User} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessageV3.Builder implements - // @@protoc_insertion_point(builder_implements:io.protostuff.runtime.model.User) - io.protostuff.runtime.model.ModelProtobuf.UserOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.protostuff.runtime.model.ModelProtobuf.internal_static_io_protostuff_runtime_model_User_descriptor; - } - - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() { - return io.protostuff.runtime.model.ModelProtobuf.internal_static_io_protostuff_runtime_model_User_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.protostuff.runtime.model.ModelProtobuf.User.class, - io.protostuff.runtime.model.ModelProtobuf.User.Builder.class); - } - - // Construct using io.protostuff.runtime.model.ModelProtobuf.User.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) { - } - } - - public Builder clear() { - super.clear(); - name_ = ""; - - return this; - } - - public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { - return io.protostuff.runtime.model.ModelProtobuf.internal_static_io_protostuff_runtime_model_User_descriptor; - } - - public io.protostuff.runtime.model.ModelProtobuf.User getDefaultInstanceForType() { - return io.protostuff.runtime.model.ModelProtobuf.User.getDefaultInstance(); - } - - public io.protostuff.runtime.model.ModelProtobuf.User build() { - io.protostuff.runtime.model.ModelProtobuf.User result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - public io.protostuff.runtime.model.ModelProtobuf.User buildPartial() { - io.protostuff.runtime.model.ModelProtobuf.User result = - new io.protostuff.runtime.model.ModelProtobuf.User(this); - result.name_ = name_; - onBuilt(); - return result; - } - - public Builder clone() { - return (Builder) super.clone(); - } - - public Builder setField( - com.google.protobuf.Descriptors.FieldDescriptor field, - Object value) { - return (Builder) super.setField(field, value); - } - - public Builder clearField( - com.google.protobuf.Descriptors.FieldDescriptor field) { - return (Builder) super.clearField(field); - } - - public Builder clearOneof( - com.google.protobuf.Descriptors.OneofDescriptor oneof) { - return (Builder) super.clearOneof(oneof); - } - - public Builder setRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - int index, Object value) { - return (Builder) super.setRepeatedField(field, index, value); - } - - public Builder addRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - Object value) { - return (Builder) super.addRepeatedField(field, value); - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.protostuff.runtime.model.ModelProtobuf.User) { - return mergeFrom((io.protostuff.runtime.model.ModelProtobuf.User) other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(io.protostuff.runtime.model.ModelProtobuf.User other) { - if (other == io.protostuff.runtime.model.ModelProtobuf.User.getDefaultInstance()) { - return this; - } - if (!other.getName().isEmpty()) { - name_ = other.name_; - onChanged(); - } - onChanged(); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - io.protostuff.runtime.model.ModelProtobuf.User parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (io.protostuff.runtime.model.ModelProtobuf.User) e.getUnfinishedMessage(); - throw e.unwrapIOException(); - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - - private java.lang.Object name_ = ""; - - /** - * string name = 1; - */ - public java.lang.String getName() { - java.lang.Object ref = name_; - if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - name_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - - /** - * string name = 1; - */ - public com.google.protobuf.ByteString getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - /** - * string name = 1; - */ - public Builder setName( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - - name_ = value; - onChanged(); - return this; - } - - /** - * string name = 1; - */ - public Builder clearName() { - - name_ = getDefaultInstance().getName(); - onChanged(); - return this; - } - - /** - * string name = 1; - */ - public Builder setNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - checkByteStringIsUtf8(value); - - name_ = value; - onChanged(); - return this; - } - - public final Builder setUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return this; - } - - public final Builder mergeUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return this; - } - - // @@protoc_insertion_point(builder_scope:io.protostuff.runtime.model.User) - } - - // @@protoc_insertion_point(class_scope:io.protostuff.runtime.model.User) - private static final io.protostuff.runtime.model.ModelProtobuf.User DEFAULT_INSTANCE; - - static { - DEFAULT_INSTANCE = new io.protostuff.runtime.model.ModelProtobuf.User(); - } - - public static io.protostuff.runtime.model.ModelProtobuf.User getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - private static final com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public User parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new User(input, extensionRegistry); - } - }; - - public static com.google.protobuf.Parser parser() { - return PARSER; - } - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public io.protostuff.runtime.model.ModelProtobuf.User getDefaultInstanceForType() { - return DEFAULT_INSTANCE; - } - } - - public interface RequestHeaderOrBuilder extends - // @@protoc_insertion_point(interface_extends:io.protostuff.runtime.model.RequestHeader) - com.google.protobuf.MessageOrBuilder { - - /** - * string destMicroservice = 1; - */ - java.lang.String getDestMicroservice(); - - /** - * string destMicroservice = 1; - */ - com.google.protobuf.ByteString getDestMicroserviceBytes(); - - /** - * int32 msgType = 2; - */ - int getMsgType(); - - /** - * int32 flags = 3; - */ - int getFlags(); - - /** - * string schemaId = 5; - */ - java.lang.String getSchemaId(); - - /** - * string schemaId = 5; - */ - com.google.protobuf.ByteString getSchemaIdBytes(); - - /** - * string operationName = 6; - */ - java.lang.String getOperationName(); - - /** - * string operationName = 6; - */ - com.google.protobuf.ByteString getOperationNameBytes(); - - /** - * map<string, string> cseContext = 7; - */ - int getCseContextCount(); - - /** - * map<string, string> cseContext = 7; - */ - boolean containsCseContext( - java.lang.String key); - - /** - * Use {@link #getCseContextMap()} instead. - */ - @java.lang.Deprecated - java.util.Map getCseContext(); - - /** - * map<string, string> cseContext = 7; - */ - java.util.Map getCseContextMap(); - - /** - * map<string, string> cseContext = 7; - */ - - java.lang.String getCseContextOrDefault( - java.lang.String key, - java.lang.String defaultValue); - - /** - * map<string, string> cseContext = 7; - */ - - java.lang.String getCseContextOrThrow( - java.lang.String key); - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - int getUserMapCount(); - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - boolean containsUserMap( - java.lang.String key); - - /** - * Use {@link #getUserMapMap()} instead. - */ - @java.lang.Deprecated - java.util.Map getUserMap(); - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - java.util.Map getUserMapMap(); - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - - io.protostuff.runtime.model.ModelProtobuf.User getUserMapOrDefault( - java.lang.String key, - io.protostuff.runtime.model.ModelProtobuf.User defaultValue); - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - - io.protostuff.runtime.model.ModelProtobuf.User getUserMapOrThrow( - java.lang.String key); - - /** - * repeated string list = 9; - */ - java.util.List getListList(); - - /** - * repeated string list = 9; - */ - int getListCount(); - - /** - * repeated string list = 9; - */ - java.lang.String getList(int index); - - /** - * repeated string list = 9; - */ - com.google.protobuf.ByteString getListBytes(int index); - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - java.util.List getUserListList(); - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - io.protostuff.runtime.model.ModelProtobuf.User getUserList(int index); - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - int getUserListCount(); - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - java.util.List getUserListOrBuilderList(); - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - io.protostuff.runtime.model.ModelProtobuf.UserOrBuilder getUserListOrBuilder( - int index); - } - - /** - * Protobuf type {@code io.protostuff.runtime.model.RequestHeader} - */ - public static final class RequestHeader extends - com.google.protobuf.GeneratedMessageV3 implements - // @@protoc_insertion_point(message_implements:io.protostuff.runtime.model.RequestHeader) - RequestHeaderOrBuilder { - // Use RequestHeader.newBuilder() to construct. - private RequestHeader(com.google.protobuf.GeneratedMessageV3.Builder builder) { - super(builder); - } - - private RequestHeader() { - destMicroservice_ = ""; - msgType_ = 0; - flags_ = 0; - schemaId_ = ""; - operationName_ = ""; - list_ = com.google.protobuf.LazyStringArrayList.EMPTY; - userList_ = java.util.Collections.emptyList(); - } - - @java.lang.Override - public final com.google.protobuf.UnknownFieldSet getUnknownFields() { - return com.google.protobuf.UnknownFieldSet.getDefaultInstance(); - } - - private RequestHeader( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - this(); - int mutable_bitField0_ = 0; - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!input.skipField(tag)) { - done = true; - } - break; - } - case 10: { - java.lang.String s = input.readStringRequireUtf8(); - - destMicroservice_ = s; - break; - } - case 16: { - - msgType_ = input.readInt32(); - break; - } - case 24: { - - flags_ = input.readInt32(); - break; - } - case 42: { - java.lang.String s = input.readStringRequireUtf8(); - - schemaId_ = s; - break; - } - case 50: { - java.lang.String s = input.readStringRequireUtf8(); - - operationName_ = s; - break; - } - case 58: { - if (!((mutable_bitField0_ & 0x00000020) == 0x00000020)) { - cseContext_ = com.google.protobuf.MapField.newMapField( - CseContextDefaultEntryHolder.defaultEntry); - mutable_bitField0_ |= 0x00000020; - } - com.google.protobuf.MapEntry cseContext__ = - input.readMessage( - CseContextDefaultEntryHolder.defaultEntry.getParserForType(), - extensionRegistry); - cseContext_.getMutableMap().put( - cseContext__.getKey(), - cseContext__.getValue()); - break; - } - case 66: { - if (!((mutable_bitField0_ & 0x00000040) == 0x00000040)) { - userMap_ = com.google.protobuf.MapField.newMapField( - UserMapDefaultEntryHolder.defaultEntry); - mutable_bitField0_ |= 0x00000040; - } - com.google.protobuf.MapEntry userMap__ = - input.readMessage( - UserMapDefaultEntryHolder.defaultEntry.getParserForType(), - extensionRegistry); - userMap_.getMutableMap().put( - userMap__.getKey(), - userMap__.getValue()); - break; - } - case 74: { - java.lang.String s = input.readStringRequireUtf8(); - if (!((mutable_bitField0_ & 0x00000080) == 0x00000080)) { - list_ = new com.google.protobuf.LazyStringArrayList(); - mutable_bitField0_ |= 0x00000080; - } - list_.add(s); - break; - } - case 82: { - if (!((mutable_bitField0_ & 0x00000100) == 0x00000100)) { - userList_ = new java.util.ArrayList(); - mutable_bitField0_ |= 0x00000100; - } - userList_.add( - input.readMessage(io.protostuff.runtime.model.ModelProtobuf.User.parser(), - extensionRegistry)); - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e).setUnfinishedMessage(this); - } finally { - if (((mutable_bitField0_ & 0x00000080) == 0x00000080)) { - list_ = list_.getUnmodifiableView(); - } - if (((mutable_bitField0_ & 0x00000100) == 0x00000100)) { - userList_ = java.util.Collections.unmodifiableList(userList_); - } - makeExtensionsImmutable(); - } - } - - public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.protostuff.runtime.model.ModelProtobuf.internal_static_io_protostuff_runtime_model_RequestHeader_descriptor; - } - - @SuppressWarnings({"rawtypes"}) - protected com.google.protobuf.MapField internalGetMapField( - int number) { - switch (number) { - case 7: - return internalGetCseContext(); - case 8: - return internalGetUserMap(); - default: - throw new RuntimeException( - "Invalid map field number: " + number); - } - } - - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() { - return io.protostuff.runtime.model.ModelProtobuf.internal_static_io_protostuff_runtime_model_RequestHeader_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.protostuff.runtime.model.ModelProtobuf.RequestHeader.class, - io.protostuff.runtime.model.ModelProtobuf.RequestHeader.Builder.class); - } - - @SuppressWarnings("unused") - private int bitField0_; - - public static final int DESTMICROSERVICE_FIELD_NUMBER = 1; - - private volatile java.lang.Object destMicroservice_; - - /** - * string destMicroservice = 1; - */ - public java.lang.String getDestMicroservice() { - java.lang.Object ref = destMicroservice_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - destMicroservice_ = s; - return s; - } - } - - /** - * string destMicroservice = 1; - */ - public com.google.protobuf.ByteString getDestMicroserviceBytes() { - java.lang.Object ref = destMicroservice_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - destMicroservice_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - public static final int MSGTYPE_FIELD_NUMBER = 2; - - private int msgType_; - - /** - * int32 msgType = 2; - */ - public int getMsgType() { - return msgType_; - } - - public static final int FLAGS_FIELD_NUMBER = 3; - - private int flags_; - - /** - * int32 flags = 3; - */ - public int getFlags() { - return flags_; - } - - public static final int SCHEMAID_FIELD_NUMBER = 5; - - private volatile java.lang.Object schemaId_; - - /** - * string schemaId = 5; - */ - public java.lang.String getSchemaId() { - java.lang.Object ref = schemaId_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - schemaId_ = s; - return s; - } - } - - /** - * string schemaId = 5; - */ - public com.google.protobuf.ByteString getSchemaIdBytes() { - java.lang.Object ref = schemaId_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - schemaId_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - public static final int OPERATIONNAME_FIELD_NUMBER = 6; - - private volatile java.lang.Object operationName_; - - /** - * string operationName = 6; - */ - public java.lang.String getOperationName() { - java.lang.Object ref = operationName_; - if (ref instanceof java.lang.String) { - return (java.lang.String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - operationName_ = s; - return s; - } - } - - /** - * string operationName = 6; - */ - public com.google.protobuf.ByteString getOperationNameBytes() { - java.lang.Object ref = operationName_; - if (ref instanceof java.lang.String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - operationName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - public static final int CSECONTEXT_FIELD_NUMBER = 7; - - private static final class CseContextDefaultEntryHolder { - static final com.google.protobuf.MapEntry defaultEntry = - com.google.protobuf.MapEntry.newDefaultInstance( - io.protostuff.runtime.model.ModelProtobuf.internal_static_io_protostuff_runtime_model_RequestHeader_CseContextEntry_descriptor, - com.google.protobuf.WireFormat.FieldType.STRING, - "", - com.google.protobuf.WireFormat.FieldType.STRING, - ""); - } - - private com.google.protobuf.MapField cseContext_; - - private com.google.protobuf.MapField internalGetCseContext() { - if (cseContext_ == null) { - return com.google.protobuf.MapField.emptyMapField( - CseContextDefaultEntryHolder.defaultEntry); - } - return cseContext_; - } - - public int getCseContextCount() { - return internalGetCseContext().getMap().size(); - } - - /** - * map<string, string> cseContext = 7; - */ - - public boolean containsCseContext( - java.lang.String key) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - return internalGetCseContext().getMap().containsKey(key); - } - - /** - * Use {@link #getCseContextMap()} instead. - */ - @java.lang.Deprecated - public java.util.Map getCseContext() { - return getCseContextMap(); - } - - /** - * map<string, string> cseContext = 7; - */ - - public java.util.Map getCseContextMap() { - return internalGetCseContext().getMap(); - } - - /** - * map<string, string> cseContext = 7; - */ - - public java.lang.String getCseContextOrDefault( - java.lang.String key, - java.lang.String defaultValue) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - java.util.Map map = - internalGetCseContext().getMap(); - return map.containsKey(key) ? map.get(key) : defaultValue; - } - - /** - * map<string, string> cseContext = 7; - */ - - public java.lang.String getCseContextOrThrow( - java.lang.String key) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - java.util.Map map = - internalGetCseContext().getMap(); - if (!map.containsKey(key)) { - throw new java.lang.IllegalArgumentException(); - } - return map.get(key); - } - - public static final int USERMAP_FIELD_NUMBER = 8; - - private static final class UserMapDefaultEntryHolder { - static final com.google.protobuf.MapEntry defaultEntry = - com.google.protobuf.MapEntry.newDefaultInstance( - io.protostuff.runtime.model.ModelProtobuf.internal_static_io_protostuff_runtime_model_RequestHeader_UserMapEntry_descriptor, - com.google.protobuf.WireFormat.FieldType.STRING, - "", - com.google.protobuf.WireFormat.FieldType.MESSAGE, - io.protostuff.runtime.model.ModelProtobuf.User.getDefaultInstance()); - } - - private com.google.protobuf.MapField userMap_; - - private com.google.protobuf.MapField internalGetUserMap() { - if (userMap_ == null) { - return com.google.protobuf.MapField.emptyMapField( - UserMapDefaultEntryHolder.defaultEntry); - } - return userMap_; - } - - public int getUserMapCount() { - return internalGetUserMap().getMap().size(); - } - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - - public boolean containsUserMap( - java.lang.String key) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - return internalGetUserMap().getMap().containsKey(key); - } - - /** - * Use {@link #getUserMapMap()} instead. - */ - @java.lang.Deprecated - public java.util.Map getUserMap() { - return getUserMapMap(); - } - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - - public java.util.Map getUserMapMap() { - return internalGetUserMap().getMap(); - } - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - - public io.protostuff.runtime.model.ModelProtobuf.User getUserMapOrDefault( - java.lang.String key, - io.protostuff.runtime.model.ModelProtobuf.User defaultValue) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - java.util.Map map = - internalGetUserMap().getMap(); - return map.containsKey(key) ? map.get(key) : defaultValue; - } - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - - public io.protostuff.runtime.model.ModelProtobuf.User getUserMapOrThrow( - java.lang.String key) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - java.util.Map map = - internalGetUserMap().getMap(); - if (!map.containsKey(key)) { - throw new java.lang.IllegalArgumentException(); - } - return map.get(key); - } - - public static final int LIST_FIELD_NUMBER = 9; - - private com.google.protobuf.LazyStringList list_; - - /** - * repeated string list = 9; - */ - public com.google.protobuf.ProtocolStringList getListList() { - return list_; - } - - /** - * repeated string list = 9; - */ - public int getListCount() { - return list_.size(); - } - - /** - * repeated string list = 9; - */ - public java.lang.String getList(int index) { - return list_.get(index); - } - - /** - * repeated string list = 9; - */ - public com.google.protobuf.ByteString getListBytes(int index) { - return list_.getByteString(index); - } - - public static final int USERLIST_FIELD_NUMBER = 10; - - private java.util.List userList_; - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public java.util.List getUserListList() { - return userList_; - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public java.util.List getUserListOrBuilderList() { - return userList_; - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public int getUserListCount() { - return userList_.size(); - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public io.protostuff.runtime.model.ModelProtobuf.User getUserList(int index) { - return userList_.get(index); - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public io.protostuff.runtime.model.ModelProtobuf.UserOrBuilder getUserListOrBuilder( - int index) { - return userList_.get(index); - } - - private byte memoizedIsInitialized = -1; - - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized == 1) { - return true; - } - if (isInitialized == 0) { - return false; - } - - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { - if (!getDestMicroserviceBytes().isEmpty()) { - com.google.protobuf.GeneratedMessageV3.writeString(output, 1, destMicroservice_); - } - if (msgType_ != 0) { - output.writeInt32(2, msgType_); - } - if (flags_ != 0) { - output.writeInt32(3, flags_); - } - if (!getSchemaIdBytes().isEmpty()) { - com.google.protobuf.GeneratedMessageV3.writeString(output, 5, schemaId_); - } - if (!getOperationNameBytes().isEmpty()) { - com.google.protobuf.GeneratedMessageV3.writeString(output, 6, operationName_); - } - com.google.protobuf.GeneratedMessageV3 - .serializeStringMapTo( - output, - internalGetCseContext(), - CseContextDefaultEntryHolder.defaultEntry, - 7); - com.google.protobuf.GeneratedMessageV3 - .serializeStringMapTo( - output, - internalGetUserMap(), - UserMapDefaultEntryHolder.defaultEntry, - 8); - for (int i = 0; i < list_.size(); i++) { - com.google.protobuf.GeneratedMessageV3.writeString(output, 9, list_.getRaw(i)); - } - for (int i = 0; i < userList_.size(); i++) { - output.writeMessage(10, userList_.get(i)); - } - } - - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) { - return size; - } - - size = 0; - if (!getDestMicroserviceBytes().isEmpty()) { - size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, destMicroservice_); - } - if (msgType_ != 0) { - size += com.google.protobuf.CodedOutputStream - .computeInt32Size(2, msgType_); - } - if (flags_ != 0) { - size += com.google.protobuf.CodedOutputStream - .computeInt32Size(3, flags_); - } - if (!getSchemaIdBytes().isEmpty()) { - size += com.google.protobuf.GeneratedMessageV3.computeStringSize(5, schemaId_); - } - if (!getOperationNameBytes().isEmpty()) { - size += com.google.protobuf.GeneratedMessageV3.computeStringSize(6, operationName_); - } - for (java.util.Map.Entry entry : internalGetCseContext().getMap() - .entrySet()) { - com.google.protobuf.MapEntry cseContext__ = - CseContextDefaultEntryHolder.defaultEntry.newBuilderForType() - .setKey(entry.getKey()) - .setValue(entry.getValue()) - .build(); - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(7, cseContext__); - } - for (java.util.Map.Entry entry : internalGetUserMap() - .getMap() - .entrySet()) { - com.google.protobuf.MapEntry userMap__ = - UserMapDefaultEntryHolder.defaultEntry.newBuilderForType() - .setKey(entry.getKey()) - .setValue(entry.getValue()) - .build(); - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(8, userMap__); - } - { - int dataSize = 0; - for (int i = 0; i < list_.size(); i++) { - dataSize += computeStringSizeNoTag(list_.getRaw(i)); - } - size += dataSize; - size += 1 * getListList().size(); - } - for (int i = 0; i < userList_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(10, userList_.get(i)); - } - memoizedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - - @java.lang.Override - public boolean equals(final java.lang.Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof io.protostuff.runtime.model.ModelProtobuf.RequestHeader)) { - return super.equals(obj); - } - io.protostuff.runtime.model.ModelProtobuf.RequestHeader other = - (io.protostuff.runtime.model.ModelProtobuf.RequestHeader) obj; - - boolean result = true; - result = result && getDestMicroservice() - .equals(other.getDestMicroservice()); - result = result && (getMsgType() == other.getMsgType()); - result = result && (getFlags() == other.getFlags()); - result = result && getSchemaId() - .equals(other.getSchemaId()); - result = result && getOperationName() - .equals(other.getOperationName()); - result = result && internalGetCseContext().equals( - other.internalGetCseContext()); - result = result && internalGetUserMap().equals( - other.internalGetUserMap()); - result = result && getListList() - .equals(other.getListList()); - result = result && getUserListList() - .equals(other.getUserListList()); - return result; - } - - @SuppressWarnings("unchecked") - @java.lang.Override - public int hashCode() { - if (memoizedHashCode != 0) { - return memoizedHashCode; - } - int hash = 41; - hash = (19 * hash) + getDescriptor().hashCode(); - hash = (37 * hash) + DESTMICROSERVICE_FIELD_NUMBER; - hash = (53 * hash) + getDestMicroservice().hashCode(); - hash = (37 * hash) + MSGTYPE_FIELD_NUMBER; - hash = (53 * hash) + getMsgType(); - hash = (37 * hash) + FLAGS_FIELD_NUMBER; - hash = (53 * hash) + getFlags(); - hash = (37 * hash) + SCHEMAID_FIELD_NUMBER; - hash = (53 * hash) + getSchemaId().hashCode(); - hash = (37 * hash) + OPERATIONNAME_FIELD_NUMBER; - hash = (53 * hash) + getOperationName().hashCode(); - if (!internalGetCseContext().getMap().isEmpty()) { - hash = (37 * hash) + CSECONTEXT_FIELD_NUMBER; - hash = (53 * hash) + internalGetCseContext().hashCode(); - } - if (!internalGetUserMap().getMap().isEmpty()) { - hash = (37 * hash) + USERMAP_FIELD_NUMBER; - hash = (53 * hash) + internalGetUserMap().hashCode(); - } - if (getListCount() > 0) { - hash = (37 * hash) + LIST_FIELD_NUMBER; - hash = (53 * hash) + getListList().hashCode(); - } - if (getUserListCount() > 0) { - hash = (37 * hash) + USERLIST_FIELD_NUMBER; - hash = (53 * hash) + getUserListList().hashCode(); - } - hash = (29 * hash) + unknownFields.hashCode(); - memoizedHashCode = hash; - return hash; - } - - public static io.protostuff.runtime.model.ModelProtobuf.RequestHeader parseFrom( - java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - - public static io.protostuff.runtime.model.ModelProtobuf.RequestHeader parseFrom( - java.nio.ByteBuffer data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - - public static io.protostuff.runtime.model.ModelProtobuf.RequestHeader parseFrom( - com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - - public static io.protostuff.runtime.model.ModelProtobuf.RequestHeader parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - - public static io.protostuff.runtime.model.ModelProtobuf.RequestHeader parseFrom( - byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - - public static io.protostuff.runtime.model.ModelProtobuf.RequestHeader parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - - public static io.protostuff.runtime.model.ModelProtobuf.RequestHeader parseFrom( - java.io.InputStream input) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - - public static io.protostuff.runtime.model.ModelProtobuf.RequestHeader parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public static io.protostuff.runtime.model.ModelProtobuf.RequestHeader parseDelimitedFrom( - java.io.InputStream input) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input); - } - - public static io.protostuff.runtime.model.ModelProtobuf.RequestHeader parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); - } - - public static io.protostuff.runtime.model.ModelProtobuf.RequestHeader parseFrom( - com.google.protobuf.CodedInputStream input) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - - public static io.protostuff.runtime.model.ModelProtobuf.RequestHeader parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - - public Builder newBuilderForType() { - return newBuilder(); - } - - public static Builder newBuilder() { - return DEFAULT_INSTANCE.toBuilder(); - } - - public static Builder newBuilder(io.protostuff.runtime.model.ModelProtobuf.RequestHeader prototype) { - return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); - } - - public Builder toBuilder() { - return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); - } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - - /** - * Protobuf type {@code io.protostuff.runtime.model.RequestHeader} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessageV3.Builder implements - // @@protoc_insertion_point(builder_implements:io.protostuff.runtime.model.RequestHeader) - io.protostuff.runtime.model.ModelProtobuf.RequestHeaderOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return io.protostuff.runtime.model.ModelProtobuf.internal_static_io_protostuff_runtime_model_RequestHeader_descriptor; - } - - @SuppressWarnings({"rawtypes"}) - protected com.google.protobuf.MapField internalGetMapField( - int number) { - switch (number) { - case 7: - return internalGetCseContext(); - case 8: - return internalGetUserMap(); - default: - throw new RuntimeException( - "Invalid map field number: " + number); - } - } - - @SuppressWarnings({"rawtypes"}) - protected com.google.protobuf.MapField internalGetMutableMapField( - int number) { - switch (number) { - case 7: - return internalGetMutableCseContext(); - case 8: - return internalGetMutableUserMap(); - default: - throw new RuntimeException( - "Invalid map field number: " + number); - } - } - - protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internalGetFieldAccessorTable() { - return io.protostuff.runtime.model.ModelProtobuf.internal_static_io_protostuff_runtime_model_RequestHeader_fieldAccessorTable - .ensureFieldAccessorsInitialized( - io.protostuff.runtime.model.ModelProtobuf.RequestHeader.class, - io.protostuff.runtime.model.ModelProtobuf.RequestHeader.Builder.class); - } - - // Construct using io.protostuff.runtime.model.ModelProtobuf.RequestHeader.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder( - com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) { - getUserListFieldBuilder(); - } - } - - public Builder clear() { - super.clear(); - destMicroservice_ = ""; - - msgType_ = 0; - - flags_ = 0; - - schemaId_ = ""; - - operationName_ = ""; - - internalGetMutableCseContext().clear(); - internalGetMutableUserMap().clear(); - list_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000080); - if (userListBuilder_ == null) { - userList_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000100); - } else { - userListBuilder_.clear(); - } - return this; - } - - public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { - return io.protostuff.runtime.model.ModelProtobuf.internal_static_io_protostuff_runtime_model_RequestHeader_descriptor; - } - - public io.protostuff.runtime.model.ModelProtobuf.RequestHeader getDefaultInstanceForType() { - return io.protostuff.runtime.model.ModelProtobuf.RequestHeader.getDefaultInstance(); - } - - public io.protostuff.runtime.model.ModelProtobuf.RequestHeader build() { - io.protostuff.runtime.model.ModelProtobuf.RequestHeader result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - @SuppressWarnings("unused") - public io.protostuff.runtime.model.ModelProtobuf.RequestHeader buildPartial() { - io.protostuff.runtime.model.ModelProtobuf.RequestHeader result = - new io.protostuff.runtime.model.ModelProtobuf.RequestHeader(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - result.destMicroservice_ = destMicroservice_; - result.msgType_ = msgType_; - result.flags_ = flags_; - result.schemaId_ = schemaId_; - result.operationName_ = operationName_; - result.cseContext_ = internalGetCseContext(); - result.cseContext_.makeImmutable(); - result.userMap_ = internalGetUserMap(); - result.userMap_.makeImmutable(); - if (((bitField0_ & 0x00000080) == 0x00000080)) { - list_ = list_.getUnmodifiableView(); - bitField0_ = (bitField0_ & ~0x00000080); - } - result.list_ = list_; - if (userListBuilder_ == null) { - if (((bitField0_ & 0x00000100) == 0x00000100)) { - userList_ = java.util.Collections.unmodifiableList(userList_); - bitField0_ = (bitField0_ & ~0x00000100); - } - result.userList_ = userList_; - } else { - result.userList_ = userListBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder clone() { - return (Builder) super.clone(); - } - - public Builder setField( - com.google.protobuf.Descriptors.FieldDescriptor field, - Object value) { - return (Builder) super.setField(field, value); - } - - public Builder clearField( - com.google.protobuf.Descriptors.FieldDescriptor field) { - return (Builder) super.clearField(field); - } - - public Builder clearOneof( - com.google.protobuf.Descriptors.OneofDescriptor oneof) { - return (Builder) super.clearOneof(oneof); - } - - public Builder setRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - int index, Object value) { - return (Builder) super.setRepeatedField(field, index, value); - } - - public Builder addRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - Object value) { - return (Builder) super.addRepeatedField(field, value); - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof io.protostuff.runtime.model.ModelProtobuf.RequestHeader) { - return mergeFrom((io.protostuff.runtime.model.ModelProtobuf.RequestHeader) other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(io.protostuff.runtime.model.ModelProtobuf.RequestHeader other) { - if (other == io.protostuff.runtime.model.ModelProtobuf.RequestHeader.getDefaultInstance()) { - return this; - } - if (!other.getDestMicroservice().isEmpty()) { - destMicroservice_ = other.destMicroservice_; - onChanged(); - } - if (other.getMsgType() != 0) { - setMsgType(other.getMsgType()); - } - if (other.getFlags() != 0) { - setFlags(other.getFlags()); - } - if (!other.getSchemaId().isEmpty()) { - schemaId_ = other.schemaId_; - onChanged(); - } - if (!other.getOperationName().isEmpty()) { - operationName_ = other.operationName_; - onChanged(); - } - internalGetMutableCseContext().mergeFrom( - other.internalGetCseContext()); - internalGetMutableUserMap().mergeFrom( - other.internalGetUserMap()); - if (!other.list_.isEmpty()) { - if (list_.isEmpty()) { - list_ = other.list_; - bitField0_ = (bitField0_ & ~0x00000080); - } else { - ensureListIsMutable(); - list_.addAll(other.list_); - } - onChanged(); - } - if (userListBuilder_ == null) { - if (!other.userList_.isEmpty()) { - if (userList_.isEmpty()) { - userList_ = other.userList_; - bitField0_ = (bitField0_ & ~0x00000100); - } else { - ensureUserListIsMutable(); - userList_.addAll(other.userList_); - } - onChanged(); - } - } else { - if (!other.userList_.isEmpty()) { - if (userListBuilder_.isEmpty()) { - userListBuilder_.dispose(); - userListBuilder_ = null; - userList_ = other.userList_; - bitField0_ = (bitField0_ & ~0x00000100); - userListBuilder_ = - com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders - ? getUserListFieldBuilder() - : null; - } else { - userListBuilder_.addAllMessages(other.userList_); - } - } - } - onChanged(); - return this; - } - - public final boolean isInitialized() { - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { - io.protostuff.runtime.model.ModelProtobuf.RequestHeader parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = - (io.protostuff.runtime.model.ModelProtobuf.RequestHeader) e.getUnfinishedMessage(); - throw e.unwrapIOException(); - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } - - private int bitField0_; - - private java.lang.Object destMicroservice_ = ""; - - /** - * string destMicroservice = 1; - */ - public java.lang.String getDestMicroservice() { - java.lang.Object ref = destMicroservice_; - if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - destMicroservice_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - - /** - * string destMicroservice = 1; - */ - public com.google.protobuf.ByteString getDestMicroserviceBytes() { - java.lang.Object ref = destMicroservice_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - destMicroservice_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - /** - * string destMicroservice = 1; - */ - public Builder setDestMicroservice( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - - destMicroservice_ = value; - onChanged(); - return this; - } - - /** - * string destMicroservice = 1; - */ - public Builder clearDestMicroservice() { - - destMicroservice_ = getDefaultInstance().getDestMicroservice(); - onChanged(); - return this; - } - - /** - * string destMicroservice = 1; - */ - public Builder setDestMicroserviceBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - checkByteStringIsUtf8(value); - - destMicroservice_ = value; - onChanged(); - return this; - } - - private int msgType_; - - /** - * int32 msgType = 2; - */ - public int getMsgType() { - return msgType_; - } - - /** - * int32 msgType = 2; - */ - public Builder setMsgType(int value) { - - msgType_ = value; - onChanged(); - return this; - } - - /** - * int32 msgType = 2; - */ - public Builder clearMsgType() { - - msgType_ = 0; - onChanged(); - return this; - } - - private int flags_; - - /** - * int32 flags = 3; - */ - public int getFlags() { - return flags_; - } - - /** - * int32 flags = 3; - */ - public Builder setFlags(int value) { - - flags_ = value; - onChanged(); - return this; - } - - /** - * int32 flags = 3; - */ - public Builder clearFlags() { - - flags_ = 0; - onChanged(); - return this; - } - - private java.lang.Object schemaId_ = ""; - - /** - * string schemaId = 5; - */ - public java.lang.String getSchemaId() { - java.lang.Object ref = schemaId_; - if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - schemaId_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - - /** - * string schemaId = 5; - */ - public com.google.protobuf.ByteString getSchemaIdBytes() { - java.lang.Object ref = schemaId_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - schemaId_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - /** - * string schemaId = 5; - */ - public Builder setSchemaId( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - - schemaId_ = value; - onChanged(); - return this; - } - - /** - * string schemaId = 5; - */ - public Builder clearSchemaId() { - - schemaId_ = getDefaultInstance().getSchemaId(); - onChanged(); - return this; - } - - /** - * string schemaId = 5; - */ - public Builder setSchemaIdBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - checkByteStringIsUtf8(value); - - schemaId_ = value; - onChanged(); - return this; - } - - private java.lang.Object operationName_ = ""; - - /** - * string operationName = 6; - */ - public java.lang.String getOperationName() { - java.lang.Object ref = operationName_; - if (!(ref instanceof java.lang.String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - java.lang.String s = bs.toStringUtf8(); - operationName_ = s; - return s; - } else { - return (java.lang.String) ref; - } - } - - /** - * string operationName = 6; - */ - public com.google.protobuf.ByteString getOperationNameBytes() { - java.lang.Object ref = operationName_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (java.lang.String) ref); - operationName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - /** - * string operationName = 6; - */ - public Builder setOperationName( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - - operationName_ = value; - onChanged(); - return this; - } - - /** - * string operationName = 6; - */ - public Builder clearOperationName() { - - operationName_ = getDefaultInstance().getOperationName(); - onChanged(); - return this; - } - - /** - * string operationName = 6; - */ - public Builder setOperationNameBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - checkByteStringIsUtf8(value); - - operationName_ = value; - onChanged(); - return this; - } - - private com.google.protobuf.MapField cseContext_; - - private com.google.protobuf.MapField internalGetCseContext() { - if (cseContext_ == null) { - return com.google.protobuf.MapField.emptyMapField( - CseContextDefaultEntryHolder.defaultEntry); - } - return cseContext_; - } - - private com.google.protobuf.MapField internalGetMutableCseContext() { - onChanged(); - if (cseContext_ == null) { - cseContext_ = com.google.protobuf.MapField.newMapField( - CseContextDefaultEntryHolder.defaultEntry); - } - if (!cseContext_.isMutable()) { - cseContext_ = cseContext_.copy(); - } - return cseContext_; - } - - public int getCseContextCount() { - return internalGetCseContext().getMap().size(); - } - - /** - * map<string, string> cseContext = 7; - */ - - public boolean containsCseContext( - java.lang.String key) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - return internalGetCseContext().getMap().containsKey(key); - } - - /** - * Use {@link #getCseContextMap()} instead. - */ - @java.lang.Deprecated - public java.util.Map getCseContext() { - return getCseContextMap(); - } - - /** - * map<string, string> cseContext = 7; - */ - - public java.util.Map getCseContextMap() { - return internalGetCseContext().getMap(); - } - - /** - * map<string, string> cseContext = 7; - */ - - public java.lang.String getCseContextOrDefault( - java.lang.String key, - java.lang.String defaultValue) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - java.util.Map map = - internalGetCseContext().getMap(); - return map.containsKey(key) ? map.get(key) : defaultValue; - } - - /** - * map<string, string> cseContext = 7; - */ - - public java.lang.String getCseContextOrThrow( - java.lang.String key) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - java.util.Map map = - internalGetCseContext().getMap(); - if (!map.containsKey(key)) { - throw new java.lang.IllegalArgumentException(); - } - return map.get(key); - } - - public Builder clearCseContext() { - internalGetMutableCseContext().getMutableMap() - .clear(); - return this; - } - - /** - * map<string, string> cseContext = 7; - */ - - public Builder removeCseContext( - java.lang.String key) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - internalGetMutableCseContext().getMutableMap() - .remove(key); - return this; - } - - /** - * Use alternate mutation accessors instead. - */ - @java.lang.Deprecated - public java.util.Map getMutableCseContext() { - return internalGetMutableCseContext().getMutableMap(); - } - - /** - * map<string, string> cseContext = 7; - */ - public Builder putCseContext( - java.lang.String key, - java.lang.String value) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - if (value == null) { - throw new java.lang.NullPointerException(); - } - internalGetMutableCseContext().getMutableMap() - .put(key, value); - return this; - } - - /** - * map<string, string> cseContext = 7; - */ - - public Builder putAllCseContext( - java.util.Map values) { - internalGetMutableCseContext().getMutableMap() - .putAll(values); - return this; - } - - private com.google.protobuf.MapField userMap_; - - private com.google.protobuf.MapField internalGetUserMap() { - if (userMap_ == null) { - return com.google.protobuf.MapField.emptyMapField( - UserMapDefaultEntryHolder.defaultEntry); - } - return userMap_; - } - - private com.google.protobuf.MapField internalGetMutableUserMap() { - onChanged(); - if (userMap_ == null) { - userMap_ = com.google.protobuf.MapField.newMapField( - UserMapDefaultEntryHolder.defaultEntry); - } - if (!userMap_.isMutable()) { - userMap_ = userMap_.copy(); - } - return userMap_; - } - - public int getUserMapCount() { - return internalGetUserMap().getMap().size(); - } - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - - public boolean containsUserMap( - java.lang.String key) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - return internalGetUserMap().getMap().containsKey(key); - } - - /** - * Use {@link #getUserMapMap()} instead. - */ - @java.lang.Deprecated - public java.util.Map getUserMap() { - return getUserMapMap(); - } - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - - public java.util.Map getUserMapMap() { - return internalGetUserMap().getMap(); - } - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - - public io.protostuff.runtime.model.ModelProtobuf.User getUserMapOrDefault( - java.lang.String key, - io.protostuff.runtime.model.ModelProtobuf.User defaultValue) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - java.util.Map map = - internalGetUserMap().getMap(); - return map.containsKey(key) ? map.get(key) : defaultValue; - } - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - - public io.protostuff.runtime.model.ModelProtobuf.User getUserMapOrThrow( - java.lang.String key) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - java.util.Map map = - internalGetUserMap().getMap(); - if (!map.containsKey(key)) { - throw new java.lang.IllegalArgumentException(); - } - return map.get(key); - } - - public Builder clearUserMap() { - internalGetMutableUserMap().getMutableMap() - .clear(); - return this; - } - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - - public Builder removeUserMap( - java.lang.String key) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - internalGetMutableUserMap().getMutableMap() - .remove(key); - return this; - } - - /** - * Use alternate mutation accessors instead. - */ - @java.lang.Deprecated - public java.util.Map getMutableUserMap() { - return internalGetMutableUserMap().getMutableMap(); - } - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - public Builder putUserMap( - java.lang.String key, - io.protostuff.runtime.model.ModelProtobuf.User value) { - if (key == null) { - throw new java.lang.NullPointerException(); - } - if (value == null) { - throw new java.lang.NullPointerException(); - } - internalGetMutableUserMap().getMutableMap() - .put(key, value); - return this; - } - - /** - * map<string, .io.protostuff.runtime.model.User> userMap = 8; - */ - - public Builder putAllUserMap( - java.util.Map values) { - internalGetMutableUserMap().getMutableMap() - .putAll(values); - return this; - } - - private com.google.protobuf.LazyStringList list_ = com.google.protobuf.LazyStringArrayList.EMPTY; - - private void ensureListIsMutable() { - if (!((bitField0_ & 0x00000080) == 0x00000080)) { - list_ = new com.google.protobuf.LazyStringArrayList(list_); - bitField0_ |= 0x00000080; - } - } - - /** - * repeated string list = 9; - */ - public com.google.protobuf.ProtocolStringList getListList() { - return list_.getUnmodifiableView(); - } - - /** - * repeated string list = 9; - */ - public int getListCount() { - return list_.size(); - } - - /** - * repeated string list = 9; - */ - public java.lang.String getList(int index) { - return list_.get(index); - } - - /** - * repeated string list = 9; - */ - public com.google.protobuf.ByteString getListBytes(int index) { - return list_.getByteString(index); - } - - /** - * repeated string list = 9; - */ - public Builder setList( - int index, java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureListIsMutable(); - list_.set(index, value); - onChanged(); - return this; - } - - /** - * repeated string list = 9; - */ - public Builder addList( - java.lang.String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureListIsMutable(); - list_.add(value); - onChanged(); - return this; - } - - /** - * repeated string list = 9; - */ - public Builder addAllList( - java.lang.Iterable values) { - ensureListIsMutable(); - com.google.protobuf.AbstractMessageLite.Builder.addAll( - values, - list_); - onChanged(); - return this; - } - - /** - * repeated string list = 9; - */ - public Builder clearList() { - list_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000080); - onChanged(); - return this; - } - - /** - * repeated string list = 9; - */ - public Builder addListBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - checkByteStringIsUtf8(value); - ensureListIsMutable(); - list_.add(value); - onChanged(); - return this; - } - - private java.util.List userList_ = - java.util.Collections.emptyList(); - - private void ensureUserListIsMutable() { - if (!((bitField0_ & 0x00000100) == 0x00000100)) { - userList_ = new java.util.ArrayList(userList_); - bitField0_ |= 0x00000100; - } - } - - private com.google.protobuf.RepeatedFieldBuilderV3 userListBuilder_; - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public java.util.List getUserListList() { - if (userListBuilder_ == null) { - return java.util.Collections.unmodifiableList(userList_); - } else { - return userListBuilder_.getMessageList(); - } - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public int getUserListCount() { - if (userListBuilder_ == null) { - return userList_.size(); - } else { - return userListBuilder_.getCount(); - } - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public io.protostuff.runtime.model.ModelProtobuf.User getUserList(int index) { - if (userListBuilder_ == null) { - return userList_.get(index); - } else { - return userListBuilder_.getMessage(index); - } - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public Builder setUserList( - int index, io.protostuff.runtime.model.ModelProtobuf.User value) { - if (userListBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureUserListIsMutable(); - userList_.set(index, value); - onChanged(); - } else { - userListBuilder_.setMessage(index, value); - } - return this; - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public Builder setUserList( - int index, io.protostuff.runtime.model.ModelProtobuf.User.Builder builderForValue) { - if (userListBuilder_ == null) { - ensureUserListIsMutable(); - userList_.set(index, builderForValue.build()); - onChanged(); - } else { - userListBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public Builder addUserList(io.protostuff.runtime.model.ModelProtobuf.User value) { - if (userListBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureUserListIsMutable(); - userList_.add(value); - onChanged(); - } else { - userListBuilder_.addMessage(value); - } - return this; - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public Builder addUserList( - int index, io.protostuff.runtime.model.ModelProtobuf.User value) { - if (userListBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureUserListIsMutable(); - userList_.add(index, value); - onChanged(); - } else { - userListBuilder_.addMessage(index, value); - } - return this; - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public Builder addUserList( - io.protostuff.runtime.model.ModelProtobuf.User.Builder builderForValue) { - if (userListBuilder_ == null) { - ensureUserListIsMutable(); - userList_.add(builderForValue.build()); - onChanged(); - } else { - userListBuilder_.addMessage(builderForValue.build()); - } - return this; - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public Builder addUserList( - int index, io.protostuff.runtime.model.ModelProtobuf.User.Builder builderForValue) { - if (userListBuilder_ == null) { - ensureUserListIsMutable(); - userList_.add(index, builderForValue.build()); - onChanged(); - } else { - userListBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public Builder addAllUserList( - java.lang.Iterable values) { - if (userListBuilder_ == null) { - ensureUserListIsMutable(); - com.google.protobuf.AbstractMessageLite.Builder.addAll( - values, - userList_); - onChanged(); - } else { - userListBuilder_.addAllMessages(values); - } - return this; - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public Builder clearUserList() { - if (userListBuilder_ == null) { - userList_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000100); - onChanged(); - } else { - userListBuilder_.clear(); - } - return this; - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public Builder removeUserList(int index) { - if (userListBuilder_ == null) { - ensureUserListIsMutable(); - userList_.remove(index); - onChanged(); - } else { - userListBuilder_.remove(index); - } - return this; - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public io.protostuff.runtime.model.ModelProtobuf.User.Builder getUserListBuilder( - int index) { - return getUserListFieldBuilder().getBuilder(index); - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public io.protostuff.runtime.model.ModelProtobuf.UserOrBuilder getUserListOrBuilder( - int index) { - if (userListBuilder_ == null) { - return userList_.get(index); - } else { - return userListBuilder_.getMessageOrBuilder(index); - } - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public java.util.List getUserListOrBuilderList() { - if (userListBuilder_ != null) { - return userListBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(userList_); - } - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public io.protostuff.runtime.model.ModelProtobuf.User.Builder addUserListBuilder() { - return getUserListFieldBuilder().addBuilder( - io.protostuff.runtime.model.ModelProtobuf.User.getDefaultInstance()); - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public io.protostuff.runtime.model.ModelProtobuf.User.Builder addUserListBuilder( - int index) { - return getUserListFieldBuilder().addBuilder( - index, - io.protostuff.runtime.model.ModelProtobuf.User.getDefaultInstance()); - } - - /** - * repeated .io.protostuff.runtime.model.User userList = 10; - */ - public java.util.List getUserListBuilderList() { - return getUserListFieldBuilder().getBuilderList(); - } - - private com.google.protobuf.RepeatedFieldBuilderV3 getUserListFieldBuilder() { - if (userListBuilder_ == null) { - userListBuilder_ = - new com.google.protobuf.RepeatedFieldBuilderV3( - userList_, - ((bitField0_ & 0x00000100) == 0x00000100), - getParentForChildren(), - isClean()); - userList_ = null; - } - return userListBuilder_; - } - - public final Builder setUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return this; - } - - public final Builder mergeUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return this; - } - - // @@protoc_insertion_point(builder_scope:io.protostuff.runtime.model.RequestHeader) - } - - // @@protoc_insertion_point(class_scope:io.protostuff.runtime.model.RequestHeader) - private static final io.protostuff.runtime.model.ModelProtobuf.RequestHeader DEFAULT_INSTANCE; - - static { - DEFAULT_INSTANCE = new io.protostuff.runtime.model.ModelProtobuf.RequestHeader(); - } - - public static io.protostuff.runtime.model.ModelProtobuf.RequestHeader getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - private static final com.google.protobuf.Parser PARSER = - new com.google.protobuf.AbstractParser() { - public RequestHeader parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new RequestHeader(input, extensionRegistry); - } - }; - - public static com.google.protobuf.Parser parser() { - return PARSER; - } - - @java.lang.Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } - - public io.protostuff.runtime.model.ModelProtobuf.RequestHeader getDefaultInstanceForType() { - return DEFAULT_INSTANCE; - } - } - - private static final com.google.protobuf.Descriptors.Descriptor internal_static_io_protostuff_runtime_model_User_descriptor; - - private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_protostuff_runtime_model_User_fieldAccessorTable; - - private static final com.google.protobuf.Descriptors.Descriptor internal_static_io_protostuff_runtime_model_RequestHeader_descriptor; - - private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_protostuff_runtime_model_RequestHeader_fieldAccessorTable; - - private static final com.google.protobuf.Descriptors.Descriptor internal_static_io_protostuff_runtime_model_RequestHeader_CseContextEntry_descriptor; - - @SuppressWarnings("unused") - private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_protostuff_runtime_model_RequestHeader_CseContextEntry_fieldAccessorTable; - - private static final com.google.protobuf.Descriptors.Descriptor internal_static_io_protostuff_runtime_model_RequestHeader_UserMapEntry_descriptor; - - @SuppressWarnings("unused") - private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_io_protostuff_runtime_model_RequestHeader_UserMapEntry_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { - return descriptor; - } - - private static com.google.protobuf.Descriptors.FileDescriptor descriptor; - - static { - java.lang.String[] descriptorData = { - "\n\023ModelProtobuf.proto\022\033io.protostuff.run" + - "time.model\"\024\n\004User\022\014\n\004name\030\001 \001(\t\"\325\003\n\rReq" + - "uestHeader\022\030\n\020destMicroservice\030\001 \001(\t\022\017\n\007" + - "msgType\030\002 \001(\005\022\r\n\005flags\030\003 \001(\005\022\020\n\010schemaId" + - "\030\005 \001(\t\022\025\n\roperationName\030\006 \001(\t\022N\n\ncseCont" + - "ext\030\007 \003(\0132:.io.protostuff.runtime.model." + - "RequestHeader.CseContextEntry\022H\n\007userMap" + - "\030\010 \003(\01327.io.protostuff.runtime.model.Req" + - "uestHeader.UserMapEntry\022\014\n\004list\030\t \003(\t\0223\n" + - "\010userList\030\n \003(\0132!.io.protostuff.runtime.", - "model.User\0321\n\017CseContextEntry\022\013\n\003key\030\001 \001" + - "(\t\022\r\n\005value\030\002 \001(\t:\0028\001\032Q\n\014UserMapEntry\022\013\n" + - "\003key\030\001 \001(\t\0220\n\005value\030\002 \001(\0132!.io.protostuf" + - "f.runtime.model.User:\0028\001b\006proto3" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, - assigner); - internal_static_io_protostuff_runtime_model_User_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_io_protostuff_runtime_model_User_fieldAccessorTable = - new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( - internal_static_io_protostuff_runtime_model_User_descriptor, - new java.lang.String[] {"Name",}); - internal_static_io_protostuff_runtime_model_RequestHeader_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_io_protostuff_runtime_model_RequestHeader_fieldAccessorTable = - new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( - internal_static_io_protostuff_runtime_model_RequestHeader_descriptor, - new java.lang.String[] {"DestMicroservice", "MsgType", "Flags", "SchemaId", "OperationName", - "CseContext", "UserMap", "List", "UserList",}); - internal_static_io_protostuff_runtime_model_RequestHeader_CseContextEntry_descriptor = - internal_static_io_protostuff_runtime_model_RequestHeader_descriptor.getNestedTypes().get(0); - internal_static_io_protostuff_runtime_model_RequestHeader_CseContextEntry_fieldAccessorTable = - new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( - internal_static_io_protostuff_runtime_model_RequestHeader_CseContextEntry_descriptor, - new java.lang.String[] {"Key", "Value",}); - internal_static_io_protostuff_runtime_model_RequestHeader_UserMapEntry_descriptor = - internal_static_io_protostuff_runtime_model_RequestHeader_descriptor.getNestedTypes().get(1); - internal_static_io_protostuff_runtime_model_RequestHeader_UserMapEntry_fieldAccessorTable = - new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( - internal_static_io_protostuff_runtime_model_RequestHeader_UserMapEntry_descriptor, - new java.lang.String[] {"Key", "Value",}); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/common/common-protobuf/src/test/java/io/protostuff/runtime/model/ModelProtobuf.proto b/common/common-protobuf/src/test/java/io/protostuff/runtime/model/ModelProtobuf.proto deleted file mode 100644 index 52d537d9daa..00000000000 --- a/common/common-protobuf/src/test/java/io/protostuff/runtime/model/ModelProtobuf.proto +++ /dev/null @@ -1,18 +0,0 @@ -syntax = "proto3"; -package io.protostuff.runtime.model; - -message User{ - string name = 1; -} - -message RequestHeader{ - string destMicroservice = 1; - int32 msgType = 2; - int32 flags = 3; - string schemaId = 5; - string operationName = 6; - map cseContext = 7; - map userMap = 8; - repeated string list = 9; - repeated User userList = 10; -} \ No newline at end of file diff --git a/common/common-protobuf/src/test/java/io/protostuff/runtime/model/ModelProtostuff.java b/common/common-protobuf/src/test/java/io/protostuff/runtime/model/ModelProtostuff.java index 3ae48c7bd8c..c9f41070fa0 100644 --- a/common/common-protobuf/src/test/java/io/protostuff/runtime/model/ModelProtostuff.java +++ b/common/common-protobuf/src/test/java/io/protostuff/runtime/model/ModelProtostuff.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -23,7 +24,6 @@ public class ModelProtostuff { - //CHECKSTYLE:OFF: magicnumber @Tag(1) private String destMicroservice; diff --git a/common/common-protobuf/src/test/java/io/protostuff/runtime/model/User.java b/common/common-protobuf/src/test/java/io/protostuff/runtime/model/User.java index 071456a2122..b026ea73e68 100644 --- a/common/common-protobuf/src/test/java/io/protostuff/runtime/model/User.java +++ b/common/common-protobuf/src/test/java/io/protostuff/runtime/model/User.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestAbstractCodec.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestAbstractCodec.java deleted file mode 100644 index cde8a66bc6a..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestAbstractCodec.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.codec; - -import java.lang.reflect.Type; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -public class TestAbstractCodec extends AbstractCodec { - - private AbstractCodec abstractCodecTest = null; - - @Before - public void setUp() throws Exception { - abstractCodecTest = new TestAbstractCodec(); - } - - @After - public void tearDown() throws Exception { - abstractCodecTest = null; - } - - @Test - public void testGetWriter() { - Assert.assertNull(abstractCodecTest.getWriter()); - } - - @Test - public void testGetReader() { - Assert.assertNull(abstractCodecTest.getReader()); - } - - @Override - public void init(ProtobufSchema schema, Type... types) { - /* Do not worry, overridden method*/ - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestAbstractFieldCodec.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestAbstractFieldCodec.java deleted file mode 100644 index 516509eb825..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestAbstractFieldCodec.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.codec; - -import java.lang.reflect.Type; -import java.util.Arrays; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.ObjectReader; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.dataformat.protobuf.protoparser.protoparser.FieldElement; -import com.fasterxml.jackson.dataformat.protobuf.schema.FieldType; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufField; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufMessage; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -import io.servicecomb.codec.protobuf.definition.ProtobufManager; -import io.servicecomb.codec.protobuf.jackson.CseObjectReader; -import io.servicecomb.codec.protobuf.jackson.CseObjectWriter; -import io.servicecomb.codec.protobuf.jackson.ParamDeserializer; -import io.servicecomb.codec.protobuf.jackson.ParamSerializer; - -public class TestAbstractFieldCodec extends AbstractFieldCodec { - - private AbstractFieldCodec abstractFieldCodec = null; - - private ProtobufSchema schema = null; - - @Before - public void setUp() throws Exception { - abstractFieldCodec = new TestAbstractFieldCodec(); - schema = Mockito.mock(ProtobufSchema.class); - } - - @After - public void tearDown() throws Exception { - abstractFieldCodec = null; - schema = null; - } - - @Override - public ObjectWriter getWriter() { - return writer; - } - - @Override - public ObjectReader getReader() { - return reader; - } - - @Test - public void testInit() { - ProtobufField[] protobufFieldArray = new ProtobufField[5]; - FieldElement rawType = null; - FieldType type = FieldType.STRING; - ProtobufField p = new ProtobufField(rawType, type); - protobufFieldArray[0] = p; - Type[] types = new Type[1]; - types[0] = Integer.TYPE; - - Mockito.when(schema.getRootType()).thenReturn(Mockito.mock(ProtobufMessage.class)); - Mockito.when(schema.getRootType().getFieldCount()).thenReturn(1); - Mockito.when(schema.getRootType().fields()).thenReturn(Arrays.asList(protobufFieldArray)); - abstractFieldCodec.init(schema, types); - Assert.assertNotNull(abstractFieldCodec.readerHelpDataMap.get("UNKNOWN")); - } - - @Test - public void testFindInfo() { - Assert.assertNull(abstractFieldCodec.findInfo("name")); - } - - @Override - public void init(ProtobufSchema schema, Type... types) { - writer = new CseObjectWriter(ProtobufManager.getWriter(), schema, new ParamSerializer()); - reader = new CseObjectReader(ProtobufManager.getReader(), schema, new ParamDeserializer(readerHelpDataMap)); - super.init(schema, types); - } - - @Test - public void testReaderHelpData() { - ReaderHelpData ReaderHelpData = new ReaderHelpData(); - ReaderHelpData.setIndex(10); - ReaderHelpData.getDeser(); - @SuppressWarnings("unchecked") - JsonDeserializer j = Mockito.mock(JsonDeserializer.class); - ReaderHelpData.setDeser(j); - Assert.assertEquals(10, ReaderHelpData.getIndex()); - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestParamFieldCodec.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestParamFieldCodec.java deleted file mode 100644 index a2167ef389b..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestParamFieldCodec.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.codec; - -import java.lang.reflect.Type; -import java.util.Arrays; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import com.fasterxml.jackson.dataformat.protobuf.protoparser.protoparser.FieldElement; -import com.fasterxml.jackson.dataformat.protobuf.schema.FieldType; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufField; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufMessage; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -public class TestParamFieldCodec { - - private ParamFieldCodec paramFieldCodec = null; - - private ProtobufSchema schema = null; - - @Before - public void setUp() throws Exception { - paramFieldCodec = new ParamFieldCodec(); - schema = Mockito.mock(ProtobufSchema.class); - } - - @After - public void tearDown() throws Exception { - paramFieldCodec = null; - schema = null; - } - - @Test - public void testInit() { - Assert.assertNotNull(paramFieldCodec); - - ProtobufField[] protobufFieldArray = new ProtobufField[5]; - FieldElement rawType = null; - FieldType type = FieldType.STRING; - ProtobufField p = new ProtobufField(rawType, type); - protobufFieldArray[0] = p; - Type[] types = new Type[10]; - types[0] = Integer.TYPE; - - Mockito.when(schema.getRootType()).thenReturn(Mockito.mock(ProtobufMessage.class)); - Mockito.when(schema.getRootType().getFieldCount()).thenReturn(1); - Mockito.when(schema.getRootType().fields()).thenReturn(Arrays.asList(protobufFieldArray)); - Assert.assertNull(paramFieldCodec.reader); - Assert.assertNull(paramFieldCodec.writer); - paramFieldCodec.init(schema, types); - Assert.assertNotNull(paramFieldCodec.reader); - Assert.assertNotNull(paramFieldCodec.writer); - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestResultFieldCodec.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestResultFieldCodec.java deleted file mode 100644 index b46dc62cfcf..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestResultFieldCodec.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.codec; - -import java.lang.reflect.Type; -import java.util.Arrays; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import com.fasterxml.jackson.dataformat.protobuf.protoparser.protoparser.FieldElement; -import com.fasterxml.jackson.dataformat.protobuf.schema.FieldType; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufField; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufMessage; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -public class TestResultFieldCodec { - private ResultFieldCodec resultFieldCodec = null; - - private ProtobufSchema schema = null; - - @Before - public void setUp() throws Exception { - resultFieldCodec = new ResultFieldCodec(); - schema = Mockito.mock(ProtobufSchema.class); - } - - @After - public void tearDown() throws Exception { - resultFieldCodec = null; - schema = null; - } - - @Test - public void testInit() { - Assert.assertNotNull(resultFieldCodec); - - ProtobufField[] protobufFieldArray = new ProtobufField[5]; - FieldElement rawType = null; - FieldType type = FieldType.STRING; - ProtobufField p = new ProtobufField(rawType, type); - protobufFieldArray[0] = p; - Type[] types = new Type[10]; - types[0] = Integer.TYPE; - - Mockito.when(schema.getRootType()).thenReturn(Mockito.mock(ProtobufMessage.class)); - Mockito.when(schema.getRootType().getFieldCount()).thenReturn(1); - Mockito.when(schema.getRootType().fields()).thenReturn(Arrays.asList(protobufFieldArray)); - Assert.assertNull(resultFieldCodec.reader); - Assert.assertNull(resultFieldCodec.writer); - resultFieldCodec.init(schema, types); - Assert.assertNotNull(resultFieldCodec.reader); - Assert.assertNotNull(resultFieldCodec.writer); - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestStandardParamCodec.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestStandardParamCodec.java deleted file mode 100644 index c87263d3b5e..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestStandardParamCodec.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.codec; - -import java.lang.reflect.Type; -import java.util.Arrays; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import com.fasterxml.jackson.dataformat.protobuf.protoparser.protoparser.FieldElement; -import com.fasterxml.jackson.dataformat.protobuf.schema.FieldType; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufField; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufMessage; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -public class TestStandardParamCodec { - public final static String FORMAT_NAME_PROTOBUF = "protobuf"; - - private StandardParamCodec standardParamCodec = null; - - private ProtobufSchema schema = null; - - @Before - public void setUp() throws Exception { - standardParamCodec = new StandardParamCodec(); - schema = Mockito.mock(ProtobufSchema.class); - } - - @After - public void tearDown() throws Exception { - standardParamCodec = null; - schema = null; - } - - @Test - public void testInit() { - Assert.assertNotNull(standardParamCodec); - ProtobufField[] protobufFieldArray = new ProtobufField[5]; - FieldElement rawType = null; - FieldType type = FieldType.STRING; - ProtobufField p = new ProtobufField(rawType, type); - protobufFieldArray[0] = p; - Type[] types = new Type[1]; - types[0] = Integer.TYPE; - - Mockito.when(schema.getSchemaType()).thenReturn(FORMAT_NAME_PROTOBUF); - Mockito.when(schema.getRootType()).thenReturn(Mockito.mock(ProtobufMessage.class)); - Mockito.when(schema.getRootType().getFieldCount()).thenReturn(1); - Mockito.when(schema.getRootType().fields()).thenReturn(Arrays.asList(protobufFieldArray)); - Assert.assertNull(standardParamCodec.writer); - Assert.assertNull(standardParamCodec.reader); - standardParamCodec.init(schema, types); - Assert.assertNotNull(standardParamCodec.writer); - Assert.assertNotNull(standardParamCodec.reader); - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestStandardResultCodec.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestStandardResultCodec.java deleted file mode 100644 index 763f6a429d2..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/codec/TestStandardResultCodec.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.codec; - -import java.lang.reflect.Type; -import java.util.Arrays; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import com.fasterxml.jackson.dataformat.protobuf.protoparser.protoparser.FieldElement; -import com.fasterxml.jackson.dataformat.protobuf.schema.FieldType; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufField; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufMessage; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -public class TestStandardResultCodec { - - private StandardResultCodec standardResultCodec = null; - - private ProtobufSchema schema = null; - - public final static String FORMAT_NAME_PROTOBUF = "protobuf"; - - @Before - public void setUp() throws Exception { - standardResultCodec = new StandardResultCodec(); - schema = Mockito.mock(ProtobufSchema.class); - } - - @After - public void tearDown() throws Exception { - standardResultCodec = null; - schema = null; - } - - @Test - public void testInit() { - Assert.assertNotNull(standardResultCodec); - - ProtobufField[] protobufFieldArray = new ProtobufField[5]; - FieldElement rawType = null; - FieldType type = FieldType.STRING; - ProtobufField p = new ProtobufField(rawType, type); - protobufFieldArray[0] = p; - Type[] types = new Type[1]; - types[0] = Integer.TYPE; - - Mockito.when(schema.getSchemaType()).thenReturn(FORMAT_NAME_PROTOBUF); - Mockito.when(schema.getRootType()).thenReturn(Mockito.mock(ProtobufMessage.class)); - Mockito.when(schema.getRootType().getFieldCount()).thenReturn(1); - Mockito.when(schema.getRootType().fields()).thenReturn(Arrays.asList(protobufFieldArray)); - Assert.assertNull(standardResultCodec.reader); - Assert.assertNull(standardResultCodec.writer); - standardResultCodec.init(schema, types); - Assert.assertNotNull(standardResultCodec.reader); - Assert.assertNotNull(standardResultCodec.writer); - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/definition/TestOperationProtobuf.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/definition/TestOperationProtobuf.java deleted file mode 100644 index 4bf14057d6f..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/definition/TestOperationProtobuf.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.definition; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.servicecomb.codec.protobuf.utils.schema.ArgsNotWrapSchema; -import io.servicecomb.codec.protobuf.utils.schema.NormalWrapSchema; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.unittest.UnitTestMeta; -import io.swagger.annotations.ApiResponse; - -public class TestOperationProtobuf { - class Impl { - @ApiResponse(code = 300, response = String.class, message = "") - public int test(int x) { - return 100; - } - } - - @Test - public void testOperationProtobuf() throws Exception { - UnitTestMeta meta = new UnitTestMeta(); - SchemaMeta schemaMeta = meta.getOrCreateSchemaMeta(Impl.class); - OperationMeta operationMeta = schemaMeta.findOperation("test"); - - OperationProtobuf operationProtobuf = ProtobufManager.getOrCreateOperation(operationMeta); - Assert.assertEquals(operationMeta, operationProtobuf.getOperationMeta()); - Assert.assertEquals(ArgsNotWrapSchema.class, operationProtobuf.getRequestSchema().getClass()); - Assert.assertEquals(NormalWrapSchema.class, operationProtobuf.getResponseSchema().getClass()); - - WrapSchema responseSchema = operationProtobuf.findResponseSchema(200); - Assert.assertEquals(operationProtobuf.getResponseSchema(), responseSchema); - - responseSchema = operationProtobuf.findResponseSchema(300); - Assert.assertNotNull(responseSchema); - Assert.assertNotEquals(operationProtobuf.getResponseSchema(), responseSchema); - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestAbstractDeserializer.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestAbstractDeserializer.java deleted file mode 100644 index aa9e37c88c4..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestAbstractDeserializer.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; - -import io.servicecomb.codec.protobuf.codec.AbstractFieldCodec.ReaderHelpData; - -public class TestAbstractDeserializer extends AbstractDeserializer { - - private AbstractDeserializer abstractDeserializer = null; - - private JsonParser jsonParser = Mockito.mock(JsonParser.class); - - static ReaderHelpData readerHelpData = Mockito.mock(ReaderHelpData.class); - - static Map readerHelpDataMap = new HashMap(); - - public static void setReaderHelpDataMap(Map readerHelpDataMap) { - TestAbstractDeserializer.readerHelpDataMap = readerHelpDataMap; - readerHelpDataMap.put("abc", readerHelpData); - readerHelpDataMap.put("null", readerHelpData); - } - - static { - TestAbstractDeserializer.setReaderHelpDataMap(readerHelpDataMap); - } - - public TestAbstractDeserializer() { - super(readerHelpDataMap); - } - - @Before - public void setUp() throws Exception { - abstractDeserializer = new TestAbstractDeserializer(); - } - - @After - public void tearDown() throws Exception { - abstractDeserializer = null; - jsonParser = null; - } - - @SuppressWarnings("unchecked") - @Test - public void testDeserialize() { - boolean status = false; - try { - DeserializationContext ctxt = Mockito.mock(DeserializationContext.class); - @SuppressWarnings("rawtypes") - JsonDeserializer JsonDeserializer = Mockito.mock(JsonDeserializer.class); - Object object = null; - Mockito.when(jsonParser.nextFieldName()).thenReturn("abc", (String) null); - Mockito.when(readerHelpData.getDeser()).thenReturn(JsonDeserializer); - Mockito.when(JsonDeserializer.deserialize(jsonParser, ctxt)).thenReturn(object); - Object deserializeObject = abstractDeserializer.deserialize(jsonParser, ctxt); - Assert.assertNotNull(deserializeObject); - } catch (Exception e) { - status = true; - } - Assert.assertFalse(status); - } - - @Override - protected Object createResult() { - return null; - } - - @Override - protected Object updateResult(Object result, Object value, ReaderHelpData helpData) { - /* Do not worry, overridden method*/ - try { - Mockito.when(jsonParser.nextToken()).thenReturn(JsonToken.VALUE_NULL); - } catch (Exception e) { - } - return new Object(); - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestParamDeserializer.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestParamDeserializer.java deleted file mode 100644 index a95242b3d38..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestParamDeserializer.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.servicecomb.codec.protobuf.codec.AbstractFieldCodec.ReaderHelpData; - -public class TestParamDeserializer { - - private ParamDeserializer paramDeserializer = null; - - private Map readerHelpDataMap = new HashMap<>(); - - @Before - public void setUp() throws Exception { - paramDeserializer = new ParamDeserializer(readerHelpDataMap); - } - - @After - public void tearDown() throws Exception { - paramDeserializer = null; - } - - @Test - public void testCreateResult() { - Object object = paramDeserializer.createResult(); - Assert.assertNotNull(object); - // object is created but no values inside to assert - } - - @Test - public void testUpdateResult() { - String[] stringArray = new String[1]; - stringArray[0] = "abc"; - Object[] object = new Object[1]; - Object paramObject = paramDeserializer.updateResult(object, stringArray, new ReaderHelpData()); - Assert.assertNotNull(paramObject); - Assert.assertEquals(paramObject, object); - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestParamSerializer.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestParamSerializer.java deleted file mode 100644 index afdd35be837..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestParamSerializer.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.ObjectCodec; -import com.fasterxml.jackson.core.io.IOContext; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.dataformat.protobuf.ProtobufGenerator; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufField; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufMessage; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -import mockit.Mock; -import mockit.MockUp; - -public class TestParamSerializer { - - private ParamSerializer paramSerializer = null; - - @Before - public void setUp() throws Exception { - paramSerializer = new ParamSerializer(); - } - - @After - public void tearDown() throws Exception { - paramSerializer = null; - } - - @Test - public void testSerialize() { - boolean status = true; - Assert.assertNotNull(paramSerializer); - String[] stringArray = new String[1]; - stringArray[0] = "abc"; - - ProtobufGenerator obj = null; - try { - obj = new ProtobufGenerator(Mockito.mock(IOContext.class), 2, Mockito.mock(ObjectCodec.class), - Mockito.mock(OutputStream.class)); - } catch (IOException exce) { - } - - Assert.assertNotNull(obj); - new MockUp() { - - @Mock - public void writeStartObject() throws IOException { - - } - - ProtobufSchema protobufSchema = new ProtobufSchema(null, null); - - @Mock - public ProtobufSchema getSchema() { - return protobufSchema; - } - }; - - ProtobufMessage protobufMessage = new ProtobufMessage(null, null); - new MockUp() { - @Mock - public ProtobufMessage getRootType() { - return protobufMessage; - } - }; - - List listProtobufField = new ArrayList(); - listProtobufField.add(Mockito.mock(ProtobufField.class)); - - new MockUp() { - @Mock - public Iterable fields() { - return listProtobufField; - } - }; - - new MockUp() { - @Mock - public void writeObjectField(String fieldName, Object pojo) throws IOException { - - } - }; - - new MockUp() { - - @Mock - public void writeEndObject() throws IOException { - - } - }; - - try { - paramSerializer.serialize(stringArray, - obj, - Mockito.mock(SerializerProvider.class)); - } catch (JsonProcessingException e) { - status = false; - } catch (IOException e) { - status = false; - } - - Assert.assertTrue(status); - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestResultSerializer.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestResultSerializer.java deleted file mode 100644 index 2d72994da40..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestResultSerializer.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import java.io.IOException; -import java.io.OutputStream; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.ObjectCodec; -import com.fasterxml.jackson.core.io.IOContext; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.dataformat.protobuf.ProtobufGenerator; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufField; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufMessage; -import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; - -import io.servicecomb.foundation.vertx.stream.BufferOutputStream; -import mockit.Mock; -import mockit.MockUp; - -public class TestResultSerializer { - - private ResultSerializer resultSerializer = null; - - OutputStream outputStream = null; - - @Before - public void setUp() throws Exception { - resultSerializer = new ResultSerializer(); - outputStream = new BufferOutputStream(); - } - - @After - public void tearDown() throws Exception { - resultSerializer = null; - outputStream = null; - } - - @Test - public void testSerialize() { - boolean status = true; - Assert.assertNotNull(resultSerializer); - String[] stringArray = new String[1]; - stringArray[0] = "abc"; - - ProtobufGenerator obj = null; - try { - obj = new ProtobufGenerator(Mockito.mock(IOContext.class), 2, Mockito.mock(ObjectCodec.class), - outputStream); - } catch (IOException exce) { - } - - Assert.assertNotNull(obj); - - new MockUp() { - - @Mock - public void writeStartObject() throws IOException { - - } - - ProtobufSchema protobufSchema = new ProtobufSchema(null, null); - - @Mock - public ProtobufSchema getSchema() { - return protobufSchema; - } - }; - ProtobufMessage protobufMessage = new ProtobufMessage(null, null); - new MockUp() { - @Mock - public ProtobufMessage getRootType() { - return protobufMessage; - } - }; - - new MockUp() { - @Mock - public ProtobufField firstField() { - return Mockito.mock(ProtobufField.class); - } - }; - - new MockUp() { - @Mock - public void writeObjectField(String fieldName, Object pojo) throws IOException { - - } - }; - - new MockUp() { - - @Mock - public void writeEndObject() throws IOException { - - } - }; - - try { - resultSerializer.serialize(stringArray, - obj, - Mockito.mock(SerializerProvider.class)); - } catch (JsonProcessingException e) { - status = false; - } catch (IOException e) { - status = false; - } - - Assert.assertTrue(status); - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestStandardObjectWriter.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestStandardObjectWriter.java deleted file mode 100644 index 77d3d50a98c..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/jackson/TestStandardObjectWriter.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.jackson; - -import java.io.IOException; -import java.io.OutputStream; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import com.fasterxml.jackson.core.JsonGenerationException; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectWriter; - -import io.servicecomb.foundation.vertx.stream.BufferOutputStream; -import mockit.Mock; -import mockit.MockUp; - -public class TestStandardObjectWriter { - - private StandardObjectWriter StandardObjectWriter = null; - - private OutputStream outputStream = null; - - @Before - public void setUp() throws Exception { - StandardObjectWriter = new StandardObjectWriter(Mockito.mock(ObjectWriter.class)); - outputStream = new BufferOutputStream(); - } - - @After - public void tearDown() throws Exception { - StandardObjectWriter = null; - outputStream = null; - } - - @Test - public void testWriteValueOutputStreamObject() { - boolean status = true; - String[] stringArray = new String[1]; - stringArray[0] = "abc"; - - new MockUp() { - @Mock - public void writeValue(OutputStream out, - Object value) throws IOException, JsonGenerationException, JsonMappingException { - - } - }; - try { - StandardObjectWriter.writeValue(outputStream, - stringArray); - } catch (JsonGenerationException e) { - status = false; - } catch (JsonMappingException e) { - status = false; - } catch (IOException e) { - status = false; - } - - Assert.assertTrue(status); - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/utils/TestProtobufSchemaUtils.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/utils/TestProtobufSchemaUtils.java deleted file mode 100644 index 0fcbfda0db5..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/utils/TestProtobufSchemaUtils.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.utils; - -import java.lang.reflect.Array; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - -import io.protostuff.ByteArrayInput; -import io.protostuff.Input; -import io.protostuff.LinkedBuffer; -import io.protostuff.ProtobufOutput; -import io.servicecomb.common.javassist.FieldConfig; - -public class TestProtobufSchemaUtils { - public static class TestMap { - public Map map = new HashMap<>(); - - public TestMap() { - map.put("asdf", "jjj"); - } - } - - @Test - public void testMap() throws Exception { - TestMap tm = new TestMap(); - TestMap tmResult = writeThenRead(tm); - Assert.assertEquals(tm.map, tmResult.map); - - Map map = new HashMap<>(); - map.put("aaa", "bbb"); - testSchema(map); - } - - @Test - public void wrapPrimitive() throws Exception { - Assert.assertNotNull(WrapType.ARGS_WRAP); - Assert.assertNotNull(WrapType.NORMAL_WRAP); - testSchema((int) 1); - testSchema("test"); - testSchema(WrapType.ARGS_WRAP); - Assert.assertTrue(true); - } - - @Test - public void wrapArray() throws Exception { - Assert.assertNotNull(WrapType.ARGS_WRAP); - Assert.assertNotNull(WrapType.NORMAL_WRAP); - testArraySchema(new byte[] {0, 1, 2}); - testArraySchema(new int[] {0, 1, 2}); - testArraySchema(new String[] {"a", "b"}); - testArraySchema(new WrapType[] {WrapType.ARGS_WRAP, WrapType.NORMAL_WRAP}); - Assert.assertTrue(true); - } - - @Test - public void notWrap() throws Exception { - FieldConfig expect = new FieldConfig(); - expect.setName("test"); - - FieldConfig result = (FieldConfig) writeThenRead(expect); - Assert.assertEquals(expect.getName(), result.getName()); - } - - private void testSchema(Object expect) throws Exception { - Object result = writeThenRead(expect); - Assert.assertEquals(expect, result); - } - - private void testArraySchema(Object expect) throws Exception { - Object result = writeThenRead(expect); - - int expectLen = Array.getLength(expect); - Assert.assertEquals(expectLen, Array.getLength(result)); - for (int idx = 0; idx < expectLen; idx++) { - Assert.assertEquals(Array.get(expect, idx), Array.get(result, idx)); - } - } - - @SuppressWarnings("unchecked") - private T writeThenRead(T value) throws Exception { - WrapSchema schema = ProtobufSchemaUtils.getOrCreateSchema(value.getClass()); - - byte[] bytes = toByteArray(schema, value); - Object result = toObject(schema, bytes); - return (T) result; - } - - private byte[] toByteArray(WrapSchema schema, Object value) throws Exception { - LinkedBuffer linkedBuffer = LinkedBuffer.allocate(); - ProtobufOutput output = new ProtobufOutput(linkedBuffer); - - schema.writeObject(output, value); - return output.toByteArray(); - } - - private Object toObject(WrapSchema schema, byte[] bytes) throws Exception { - Input input = new ByteArrayInput(bytes, false); - - return schema.readObject(input); - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/utils/schema/TestArgsNotWrapSchema.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/utils/schema/TestArgsNotWrapSchema.java deleted file mode 100644 index 2ce1edc82b2..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/utils/schema/TestArgsNotWrapSchema.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.utils.schema; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.protostuff.Input; -import io.protostuff.LinkedBuffer; -import io.protostuff.ProtobufOutput; -import io.protostuff.Schema; - -public class TestArgsNotWrapSchema { - - private ArgsNotWrapSchema argsNotWrapSchema = null; - - @Before - public void setUp() throws Exception { - argsNotWrapSchema = new ArgsNotWrapSchema(Mockito.mock(Schema.class)); - } - - @After - public void tearDown() throws Exception { - argsNotWrapSchema = null; - } - - @Test - public void testReadFromEmpty() { - Assert.assertNotNull(argsNotWrapSchema); - Object object = argsNotWrapSchema.readFromEmpty(); - Assert.assertNotNull(object); - // object is created but no values inside to assert - } - - @Test - public void testReadObject() { - boolean status = true; - Input input = null; - try { - Object object = argsNotWrapSchema.readObject(input); - Assert.assertNotNull(object); - // object is created but no values inside to assert - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testWriteObject() { - boolean status = true; - LinkedBuffer linkedBuffer = LinkedBuffer.allocate(); - ProtobufOutput output = new ProtobufOutput(linkedBuffer); - String[] stringArray = new String[1]; - try { - argsNotWrapSchema.writeObject(output, stringArray); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testWriteObjectToSchema() { - boolean status = true; - LinkedBuffer linkedBuffer = LinkedBuffer.allocate(); - ProtobufOutput output = new ProtobufOutput(linkedBuffer); - String[] stringArray = new String[1]; - stringArray[0] = "abc"; - try { - argsNotWrapSchema.writeObject(output, stringArray); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } -} diff --git a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/utils/schema/TestArgsWrapSchema.java b/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/utils/schema/TestArgsWrapSchema.java deleted file mode 100644 index fba44e549b4..00000000000 --- a/common/common-protobuf/src/test/java/io/servicecomb/codec/protobuf/utils/schema/TestArgsWrapSchema.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.codec.protobuf.utils.schema; - -import java.io.IOException; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.protostuff.Input; -import io.protostuff.LinkedBuffer; -import io.protostuff.ProtobufOutput; -import io.protostuff.Schema; -import io.servicecomb.common.javassist.MultiWrapper; - -public class TestArgsWrapSchema { - - private ArgsWrapSchema argsWrapSchema = null; - - @SuppressWarnings("rawtypes") - private Schema schema = null; - - @Before - public void setUp() throws Exception { - schema = Mockito.mock(Schema.class); - argsWrapSchema = new ArgsWrapSchema(schema); - } - - @After - public void tearDown() throws Exception { - argsWrapSchema = null; - } - - @Test - public void testReadFromEmpty() { - - MultiWrapper multiWrapper = Mockito.mock(MultiWrapper.class); - Mockito.when(schema.newMessage()).thenReturn(multiWrapper); - Assert.assertNotNull(argsWrapSchema); - Object object = argsWrapSchema.readFromEmpty(); - Assert.assertNull(object); - } - - @Test - public void testWriteObject() { - boolean status = true; - LinkedBuffer linkedBuffer = LinkedBuffer.allocate(); - ProtobufOutput output = new ProtobufOutput(linkedBuffer); - String[] stringArray = new String[1]; - stringArray[0] = "abc"; - - MultiWrapper multiWrapper = Mockito.mock(MultiWrapper.class); - Mockito.when(schema.newMessage()).thenReturn(multiWrapper); - try { - argsWrapSchema.writeObject(output, stringArray); - } catch (IOException e) { - status = true; - } - Assert.assertTrue(status); - } - - @Test - public void testReadObject() { - boolean status = true; - Input input = null; - String[] stringArray = new String[1]; - stringArray[0] = "abc"; - - MultiWrapper multiWrapper = Mockito.mock(MultiWrapper.class); - Mockito.when(schema.newMessage()).thenReturn(multiWrapper); - try { - Object object = argsWrapSchema.readObject(input); - Assert.assertNull(object); - } catch (IOException e) { - status = true; - } - Assert.assertTrue(status); - } -} diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/definition/TestResponseRootDeserializer.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/definition/TestResponseRootDeserializer.java new file mode 100644 index 00000000000..bf8a98f5c67 --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/definition/TestResponseRootDeserializer.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.codec.protobuf.definition; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; +import org.junit.jupiter.api.Assertions; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.type.SimpleType; +import com.fasterxml.jackson.databind.type.TypeFactory; +import org.junit.jupiter.api.Test; + +class Model { + +} + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class TestResponseRootDeserializer { + @Test + public void testNeedConvert() { + Assertions.assertEquals(SimpleType.constructUnsafe(Object.class), ProtoConst.OBJECT_TYPE); + Assertions.assertFalse(ResponseRootDeserializer.needConvert(1, TypeFactory.defaultInstance().constructType(int.class))); + Assertions.assertFalse(ResponseRootDeserializer.needConvert(1, TypeFactory.defaultInstance().constructType(Integer.class))); + Assertions.assertFalse(ResponseRootDeserializer + .needConvert(1, TypeFactory.defaultInstance().constructType(int.class))); + Assertions.assertFalse(ResponseRootDeserializer + .needConvert(1, TypeFactory.defaultInstance().constructType(Integer.class))); + Assertions.assertTrue(ResponseRootDeserializer + .needConvert(new HashMap<>(), TypeFactory.defaultInstance().constructType(Model.class))); + Assertions.assertFalse(ResponseRootDeserializer + .needConvert(new Model(), TypeFactory.defaultInstance().constructType(Model.class))); + Assertions.assertFalse(ResponseRootDeserializer + .needConvert(new Model(), TypeFactory.defaultInstance().constructType(Object.class))); + List modelList = new ArrayList<>(); + List modelMapList = new ArrayList<>(); + Assertions.assertTrue(ResponseRootDeserializer + .needConvert(modelMapList, TypeFactory.defaultInstance().constructType(new TypeReference>() { + }))); + // This case should be false, however it is not exists in real applications, for simpler, take it true. + Assertions.assertTrue(ResponseRootDeserializer + .needConvert(modelList, TypeFactory.defaultInstance().constructType(new TypeReference>() { + }))); + } +} diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestProtoToStringGenerator.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestProtoToStringGenerator.java new file mode 100644 index 00000000000..d3ed28720b8 --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestProtoToStringGenerator.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.codec.protobuf.internal.converter; + +import org.apache.servicecomb.foundation.protobuf.internal.parser.ProtoParser; +import org.junit.jupiter.api.Assertions; + +import io.protostuff.compiler.model.Proto; +import org.junit.jupiter.api.Test; + +public class TestProtoToStringGenerator { + static final String content = "syntax = \"proto3\";\n" + + "import \"google/protobuf/any.proto\";\n" + + "package org.apache.servicecomb.foundation.protobuf.internal.model;\n" + + "\n" + + "message Root {\n" + + " int32 int32 = 1;\n" + + " int64 int64 = 2;\n" + + " uint32 uint32 = 3;\n" + + " uint64 uint64 = 4;\n" + + " sint32 sint32 = 5;\n" + + " sint64 sint64 = 6;\n" + + " fixed32 fixed32 = 7;\n" + + " fixed64 fixed64 = 8;\n" + + " sfixed32 sfixed32 = 9;\n" + + " sfixed64 sfixed64 = 10;\n" + + " float floatValue = 11;\n" + + " double doubleValue = 12;\n" + + " bool bool = 13;\n" + + " string string = 14;\n" + + " bytes bytes = 15;\n" + + " Color color = 16;\n" + + " User user = 17;\n" + + " map ssMap = 18;\n" + + " map spMap = 19;\n" + + " repeated string sList = 20;\n" + + " repeated User pList = 21;\n" + + " google.protobuf.Any any = 22;\n" + + " repeated google.protobuf.Any anys = 23;\n" + + " Root typeRecursive = 24;\n" + + "}\n" + + "\n" + + "message User {\n" + + " string name = 1;\n" + + " Root typeRecursive = 2;\n" + + "}\n" + + "\n" + + "enum Color {\n" + + " RED = 0;\n" + + " YELLOW = 1;\n" + + " BLUE = 2;\n" + + "}\n" + + "\n" + + "service Service {\n" + + " //comment\n" + + " rpc op1 (User) returns (Root);\n" + + "\n" + + " rpc op2 (Root) returns (User);\n" + + "}\n"; + + @Test + public void protoToString() { + ProtoParser protoParser = new ProtoParser(); + Proto proto = protoParser.parseFromContent(content); + String newContent = new ProtoToStringGenerator(proto).protoToString(); + + Assertions.assertEquals(content, newContent); + } +} 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 new file mode 100644 index 00000000000..141b7cf4b2c --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodec.java @@ -0,0 +1,546 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.IOException; +import java.time.LocalDate; +import java.time.temporal.ChronoField; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; +import org.apache.servicecomb.codec.protobuf.definition.ProtobufManager; +import org.apache.servicecomb.codec.protobuf.definition.RequestRootDeserializer; +import org.apache.servicecomb.codec.protobuf.definition.RequestRootSerializer; +import org.apache.servicecomb.codec.protobuf.definition.ResponseRootDeserializer; +import org.apache.servicecomb.codec.protobuf.definition.ResponseRootSerializer; +import org.apache.servicecomb.codec.protobuf.internal.converter.model.ProtoSchema; +import org.apache.servicecomb.codec.protobuf.internal.converter.model.ProtoSchemaPojo; +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.MicroserviceMeta; +import org.apache.servicecomb.core.definition.MicroserviceVersionsMeta; +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.test.scaffolding.model.Color; +import org.apache.servicecomb.foundation.test.scaffolding.model.Empty; +import org.apache.servicecomb.foundation.test.scaffolding.model.People; +import org.apache.servicecomb.foundation.test.scaffolding.model.User; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.engine.SwaggerProducer; +import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; +import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; +import org.apache.servicecomb.swagger.generator.pojo.PojoSwaggerGenerator; +import org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * SchemaMetaCodec test cases. This test cases covers POJO invoker and producer. + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +public class TestSchemaMetaCodec { + + private SchemaMeta providerSchemaMeta; + + private SchemaMeta consumerSchemaMeta; + + @BeforeEach + public void setUp() { + ProtobufManager.clear(); + } + + private void mockSchemaMeta(String schemaId, AbstractSwaggerGenerator swaggerGenerator, Object producerInstance) + throws Exception { + MicroserviceVersionsMeta microserviceVersionsMeta = Mockito.mock(MicroserviceVersionsMeta.class); + ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getExecutorManager()).thenReturn(executorManager); + + MicroserviceMeta providerMicroserviceMeta = Mockito.mock(MicroserviceMeta.class); + Mockito.when(providerMicroserviceMeta.getScbEngine()).thenReturn(scbEngine); + Mockito.when(providerMicroserviceMeta.getMicroserviceVersionsMeta()).thenReturn(microserviceVersionsMeta); + Mockito.when(providerMicroserviceMeta.getMicroserviceName()).thenReturn("test"); + Mockito.when(providerMicroserviceMeta.getExtData(ProtobufManager.EXT_ID)).thenReturn(null); + + MicroserviceMeta consumerMicroserviceMeta = Mockito.mock(MicroserviceMeta.class); + Mockito.when(consumerMicroserviceMeta.getScbEngine()).thenReturn(scbEngine); + Mockito.when(consumerMicroserviceMeta.getMicroserviceVersionsMeta()).thenReturn(microserviceVersionsMeta); + Mockito.when(consumerMicroserviceMeta.getMicroserviceName()).thenReturn("test"); + Mockito.when(consumerMicroserviceMeta.getExtData(ProtobufManager.EXT_ID)).thenReturn(null); + SwaggerEnvironment swaggerEnvironment = new SwaggerEnvironment(); + 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, swaggerProducer.getSwagger()); + } + + @Test + public void testProtoSchemaOperationUserSpringMVC() throws Exception { + mockSchemaMeta("ProtoSchema", new SpringmvcSwaggerGenerator(ProtoSchema.class), new ProtoSchema()); + testProtoSchemaOperationUserImpl(); + } + + @Test + public void testProtoSchemaOperationUserPOJO() throws Exception { + mockSchemaMeta("ProtoSchemaPojo", new PojoSwaggerGenerator(ProtoSchemaPojo.class), new ProtoSchemaPojo()); + testProtoSchemaOperationUserImpl(); + } + + private Invocation mockInvocation(String operation, InvocationType invocationType) { + OperationMeta operationMeta; + boolean isProvider; + Invocation invocation = Mockito.mock(Invocation.class); + InvocationRuntimeType invocationRuntimeType; + + if (InvocationType.CONSUMER == invocationType) { + operationMeta = consumerSchemaMeta.getOperations().get(operation); + isProvider = false; + Mockito.when(invocation.getSchemaMeta()).thenReturn(consumerSchemaMeta); + invocationRuntimeType = operationMeta.buildBaseConsumerRuntimeType(); + } else { + operationMeta = providerSchemaMeta.getOperations().get(operation); + isProvider = true; + Mockito.when(invocation.getSchemaMeta()).thenReturn(providerSchemaMeta); + invocationRuntimeType = operationMeta.buildBaseProviderRuntimeType(); + } + + MicroserviceMeta microserviceMeta = operationMeta.getMicroserviceMeta(); + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + Mockito.when(invocation.getInvocationRuntimeType()) + .thenReturn(invocationRuntimeType); + Mockito.when(invocation.findResponseType(200)) + .thenReturn(invocationRuntimeType.findResponseType(200)); + Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); + Mockito.when(invocation.getMicroserviceMeta()).thenReturn(microserviceMeta); + + Mockito.when(invocation.isProducer()).thenReturn(isProvider); + return invocation; + } + + private void testProtoSchemaOperationUserImpl() throws IOException { + Invocation consumerInvocation = mockInvocation("user", InvocationType.CONSUMER); + Invocation providerInvocation = mockInvocation("user", InvocationType.PROVIDER); + + OperationProtobuf providerOperationProtobuf = ProtobufManager + .getOrCreateOperation(providerInvocation); + OperationProtobuf consumerOperationProtobuf = ProtobufManager + .getOrCreateOperation(consumerInvocation); + User user = new User(); + user.name = "user"; + User friend = new User(); + friend.name = "friend"; + List friends = new ArrayList<>(); + friends.add(friend); + user.friends = friends; + byte[] values; + + // request message + Map args = new HashMap<>(); + RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); + user.friends = friends; + args.put("user", user); + values = requestSerializer.serialize(args); + + RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); + Map decodedUserArgs = requestDeserializer.deserialize(values); + Assertions.assertEquals(user.name, ((User) decodedUserArgs.get("user")).name); + Assertions.assertEquals(user.friends.get(0).name, ((User) decodedUserArgs.get("user")).friends.get(0).name); + + // write request map (pojo) + args = new HashMap<>(); + Map userMap = new HashMap<>(); + userMap.put("name", "user"); + Map friendMap = new HashMap<>(); + friendMap.put("name", "friend"); + List> friendsList = new ArrayList<>(); + friendsList.add(friendMap); + userMap.put("friends", friendsList); + args.put("user", userMap); + values = requestSerializer.serialize(args); + + decodedUserArgs = requestDeserializer.deserialize(values); + Assertions.assertEquals(user.name, ((User) decodedUserArgs.get("user")).name); + Assertions.assertEquals(user.friends.get(0).name, ((User) decodedUserArgs.get("user")).friends.get(0).name); + + // response message + ResponseRootSerializer responseSerializer = providerOperationProtobuf.findResponseRootSerializer(200); + values = responseSerializer.serialize(user); + ResponseRootDeserializer responseDeserializer = consumerOperationProtobuf.findResponseRootDeserializer(200); + User decodedUser = (User) responseDeserializer + .deserialize(values, TypeFactory.defaultInstance().constructType(User.class)); + Assertions.assertEquals(user.name, decodedUser.name); + Assertions.assertEquals(user.friends.get(0).name, decodedUser.friends.get(0).name); + + user.friends = new ArrayList<>(); + values = responseSerializer.serialize(user); + decodedUser = (User) responseDeserializer + .deserialize(values, TypeFactory.defaultInstance().constructType(User.class)); + Assertions.assertEquals(user.name, decodedUser.name); + // proto buffer encode and decode empty list to be null + Assertions.assertNull(decodedUser.friends); + } + + @Test + public void testProtoSchemaOperationmapUserSpringMVC() throws Exception { + mockSchemaMeta("ProtoSchema", new SpringmvcSwaggerGenerator(ProtoSchema.class), new ProtoSchema()); + testProtoSchemaOperationmapUserImpl(false); + } + + @Test + public void testProtoSchemaOperationmapUserPOJO() throws Exception { + mockSchemaMeta("ProtoSchemaPojo", new PojoSwaggerGenerator(ProtoSchemaPojo.class), new ProtoSchemaPojo()); + testProtoSchemaOperationmapUserImpl(true); + } + + private void testProtoSchemaOperationmapUserImpl(boolean isPojo) throws IOException { + Invocation consumerInvocation = mockInvocation("mapUser", InvocationType.CONSUMER); + Invocation providerInvocation = mockInvocation("mapUser", InvocationType.PROVIDER); + + OperationProtobuf providerOperationProtobuf = ProtobufManager + .getOrCreateOperation(providerInvocation); + OperationProtobuf consumerOperationProtobuf = ProtobufManager + .getOrCreateOperation(consumerInvocation); + User user = new User(); + user.name = "user"; + User friend = new User(); + friend.name = "friend"; + List friends = new ArrayList<>(); + friends.add(friend); + user.friends = friends; + byte[] values; + Map userMap = new HashMap<>(); + userMap.put("test", user); + + // request message + Map args = new HashMap<>(); + RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); + user.friends = friends; + args.put("users", userMap); + if (isPojo) { + Map swaggerArgs = new HashMap<>(1); + swaggerArgs.put("listListUserBody", args); + values = requestSerializer.serialize(swaggerArgs); + } else { + values = requestSerializer.serialize(args); + } + RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); + Map decodedUserArgs = requestDeserializer.deserialize(values); + if (isPojo) { + decodedUserArgs = (Map) decodedUserArgs.get("mapUserBody"); + Assertions.assertEquals(user.name, + ((Map>) decodedUserArgs.get("users")).get("test").get("name")); + Assertions.assertEquals(user.friends.get(0).name, + ((List>) ((Map>) decodedUserArgs.get("users")).get("test") + .get("friends")).get(0).get("name")); + } else { + Assertions.assertEquals(user.name, ((Map) decodedUserArgs.get("users")).get("test").name); + Assertions.assertEquals(user.friends.get(0).name, + ((Map) decodedUserArgs.get("users")).get("test").friends.get(0).name); + } + // response message + ResponseRootSerializer responseSerializer = providerOperationProtobuf.findResponseRootSerializer(200); + values = responseSerializer.serialize(userMap); + ResponseRootDeserializer responseDeserializer = consumerOperationProtobuf.findResponseRootDeserializer(200); + 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, + 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); + } + + @Test + public void testProtoSchemaOperationBaseSpringMVC() throws Exception { + mockSchemaMeta("ProtoSchema", new SpringmvcSwaggerGenerator(ProtoSchema.class), new ProtoSchema()); + testProtoSchemaOperationBaseImpl(false); + } + + @Test + public void testProtoSchemaOperationBasePOJO() throws Exception { + mockSchemaMeta("ProtoSchemaPojo", new PojoSwaggerGenerator(ProtoSchemaPojo.class), new ProtoSchemaPojo()); + testProtoSchemaOperationBaseImpl(true); + } + + private void testProtoSchemaOperationBaseImpl(boolean isPojo) throws IOException { + Invocation consumerInvocation = mockInvocation("base", InvocationType.CONSUMER); + Invocation providerInvocation = mockInvocation("base", InvocationType.PROVIDER); + + OperationProtobuf providerOperationProtobuf = ProtobufManager + .getOrCreateOperation(providerInvocation); + OperationProtobuf consumerOperationProtobuf = ProtobufManager + .getOrCreateOperation(consumerInvocation); + byte[] values; + + // request message + RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); + boolean boolValue = true; + int iValue = 20; + long lValue = 30L; + float fValue = 40f; + double dValue = 50D; + String sValue = "hello"; + int[] iArray = new int[] {60, 70}; + Color color = Color.BLUE; + LocalDate localDate = LocalDate.of(2019, 10, 1); + Date date = new Date(); + Empty empty = new Empty(); + Map args = new HashMap<>(); + args.put("boolValue", boolValue); + args.put("iValue", iValue); + args.put("lValue", lValue); + args.put("fValue", fValue); + args.put("dValue", dValue); + args.put("sValue", sValue); + args.put("iArray", iArray); + args.put("color", color); + args.put("localDate", localDate); + args.put("date", date); + args.put("empty", empty); + if (isPojo) { + Map swaggerArgs = new HashMap<>(); + swaggerArgs.put("baseBody", args); + values = requestSerializer.serialize(swaggerArgs); + } else { + values = requestSerializer.serialize(args); + } + RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); + Map decodedSwaggerArgs = requestDeserializer.deserialize(values); + Map decodedArgs; + if (isPojo) { + Assertions.assertEquals(1, decodedSwaggerArgs.size()); + decodedArgs = (Map) decodedSwaggerArgs.get("baseBody"); + } else { + decodedArgs = decodedSwaggerArgs; + } + Assertions.assertEquals(boolValue, decodedArgs.get("boolValue")); + Assertions.assertEquals(iValue, decodedArgs.get("iValue")); + Assertions.assertEquals(lValue, decodedArgs.get("lValue")); + Assertions.assertEquals(fValue, decodedArgs.get("fValue")); + Assertions.assertEquals(dValue, decodedArgs.get("dValue")); + if (isPojo) { + Assertions.assertEquals(2, ((List) decodedArgs.get("iArray")).size()); + Assertions.assertEquals(60, (((List) decodedArgs.get("iArray")).get(0).intValue())); + Assertions.assertEquals(70, (((List) decodedArgs.get("iArray")).get(1).intValue())); + Assertions.assertEquals(color.ordinal(), decodedArgs.get("color")); + Assertions.assertEquals(date.getTime(), decodedArgs.get("date")); + Assertions.assertEquals(localDate.getLong(ChronoField.EPOCH_DAY), decodedArgs.get("localDate")); + Assertions.assertTrue(((Map) decodedArgs.get("empty")).isEmpty()); + } else { + Assertions.assertArrayEquals(iArray, (int[]) decodedArgs.get("iArray")); + Assertions.assertEquals(color, decodedArgs.get("color")); + Assertions.assertEquals(date, decodedArgs.get("date")); + Assertions.assertTrue(decodedArgs.get("localDate") instanceof LocalDate); + Assertions.assertEquals(localDate, decodedArgs.get("localDate")); + Assertions.assertTrue(decodedArgs.get("empty") instanceof Empty); + } + + // default value testing + args.put("boolValue", false); + args.put("iValue", 0); + args.put("lValue", 0L); + args.put("fValue", 0F); + args.put("dValue", 0D); + args.put("sValue", null); + args.put("iArray", new int[0]); + args.put("color", null); + args.put("localDate", null); + args.put("date", null); + args.put("empty", null); + values = requestSerializer.serialize(args); + decodedArgs = requestDeserializer.deserialize(values); + Assertions.assertNull(decodedArgs.get("boolValue")); + Assertions.assertNull(decodedArgs.get("iValue")); + Assertions.assertNull(decodedArgs.get("lValue")); + Assertions.assertNull(decodedArgs.get("fValue")); + Assertions.assertNull(decodedArgs.get("dValue")); + Assertions.assertNull(decodedArgs.get("iArray")); + Assertions.assertNull(decodedArgs.get("color")); + Assertions.assertNull(decodedArgs.get("localDate")); + Assertions.assertNull(decodedArgs.get("date")); + Assertions.assertNull(decodedArgs.get("empty")); + + // response message + ResponseRootSerializer responseSerializer = providerOperationProtobuf.findResponseRootSerializer(200); + values = responseSerializer.serialize(30); + ResponseRootDeserializer responseDeserializer = consumerOperationProtobuf.findResponseRootDeserializer(200); + Object decodedValue = responseDeserializer + .deserialize(values, TypeFactory.defaultInstance().constructType(int.class)); + Assertions.assertEquals(30, (int) decodedValue); + } + + @Test + public void testProtoSchemaOperationlistListUserSpringMVC() throws Exception { + mockSchemaMeta("ProtoSchema", new SpringmvcSwaggerGenerator(ProtoSchema.class), new ProtoSchema()); + testProtoSchemaOperationlistListUserImpl(false); + } + + @Test + public void testProtoSchemaOperationlistListUserPOJO() throws Exception { + mockSchemaMeta("ProtoSchemaPojo", new PojoSwaggerGenerator(ProtoSchemaPojo.class), new ProtoSchemaPojo()); + testProtoSchemaOperationlistListUserImpl(true); + } + + private void testProtoSchemaOperationlistListUserImpl(boolean isPojo) throws IOException { + Invocation consumerInvocation = mockInvocation("listListUser", InvocationType.CONSUMER); + Invocation providerInvocation = mockInvocation("listListUser", InvocationType.PROVIDER); + + OperationProtobuf providerOperationProtobuf = ProtobufManager + .getOrCreateOperation(providerInvocation); + OperationProtobuf consumerOperationProtobuf = ProtobufManager + .getOrCreateOperation(consumerInvocation); + byte[] values; + + // request message + RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); + User user = new User(); + user.name = "user"; + User friend = new User(); + friend.name = "friend"; + List friends = new ArrayList<>(); + friends.add(friend); + user.friends = friends; + List users = new ArrayList<>(); + users.add(user); + List> listOfUsers = new ArrayList<>(); + listOfUsers.add(users); + Map args = new HashMap<>(); + args.put("value", listOfUsers); + + if (isPojo) { + Map swaggerArgs = new HashMap<>(); + swaggerArgs.put("listListUserBody", args); + values = requestSerializer.serialize(swaggerArgs); + } else { + values = requestSerializer.serialize(args); + } + RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); + Map decodedSwaggerArgs = requestDeserializer.deserialize(values); + Map decodedArgs; + if (isPojo) { + Assertions.assertEquals(1, decodedSwaggerArgs.size()); + decodedArgs = (Map) decodedSwaggerArgs.get("listListUserBody"); + } else { + decodedArgs = decodedSwaggerArgs; + } + List> listOfUsersRaw = (List>) decodedArgs.get("value"); + Assertions.assertEquals(1, listOfUsersRaw.size()); + List mapUsersRaw = (List) listOfUsersRaw.get(0); + Assertions.assertEquals(1, mapUsersRaw.size()); + if (isPojo) { + Map userMap = (Map) mapUsersRaw.get(0); + Assertions.assertEquals("user", userMap.get("name")); + // proto buffer encode and decode empty list to be null + friends = (List) userMap.get("friends"); + Map friendMap = (Map) friends.get(0); + Assertions.assertEquals("friend", friendMap.get("name")); + } else { + user = (User) mapUsersRaw.get(0); + Assertions.assertEquals("user", user.name); + // proto buffer encode and decode empty list to be null + Assertions.assertEquals("friend", user.friends.get(0).name); + } + } + + @Test + public void testProtoSchemaOperationObjSpringMVC() throws Exception { + mockSchemaMeta("ProtoSchema", new SpringmvcSwaggerGenerator(ProtoSchema.class), new ProtoSchema()); + testProtoSchemaOperationObjImpl(false); + } + + @Test + public void testProtoSchemaOperationObjPOJO() throws Exception { + mockSchemaMeta("ProtoSchemaPojo", new PojoSwaggerGenerator(ProtoSchemaPojo.class), new ProtoSchemaPojo()); + testProtoSchemaOperationObjImpl(true); + } + + private void testProtoSchemaOperationObjImpl(boolean isPojo) throws IOException { + Invocation consumerInvocation = mockInvocation("obj", InvocationType.CONSUMER); + Invocation providerInvocation = mockInvocation("obj", InvocationType.PROVIDER); + + OperationProtobuf providerOperationProtobuf = ProtobufManager + .getOrCreateOperation(providerInvocation); + OperationProtobuf consumerOperationProtobuf = ProtobufManager + .getOrCreateOperation(consumerInvocation); + byte[] values; + + // request message + RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); + Map args = new HashMap<>(); + args.put("value", 2); + + values = requestSerializer.serialize(args); + RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); + Map decodedArgs = requestDeserializer.deserialize(values); + int result = (int) decodedArgs.get("value"); + Assertions.assertEquals(2, result); + + User user = new User(); + user.name = "user"; + User friend = new User(); + friend.name = "friend"; + List friends = new ArrayList<>(); + friends.add(friend); + user.friends = friends; + args.put("value", user); + values = requestSerializer.serialize(args); + decodedArgs = requestDeserializer.deserialize(values); + Map userMap = (Map) decodedArgs.get("value"); + Assertions.assertEquals("user", userMap.get("name")); + // proto buffer encode and decode empty list to be null + friends = (List) userMap.get("friends"); + Map friendMap = (Map) friends.get(0); + Assertions.assertEquals("friend", friendMap.get("name")); + + args.clear(); + People people = new People(); + people.name = "user"; + People pFriend = new People(); + pFriend.name = "friend"; + List pFriends = new ArrayList<>(); + pFriends.add(pFriend); + people.friends = pFriends; + args.put("value", people); + values = requestSerializer.serialize(args); + decodedArgs = requestDeserializer.deserialize(values); + people = (People) decodedArgs.get("value"); + Assertions.assertEquals("user", people.name); + // proto buffer encode and decode empty list to be null + Assertions.assertEquals("friend", people.friends.get(0).name); + } +} 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 new file mode 100644 index 00000000000..2a861d202be --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodecRestTemplate.java @@ -0,0 +1,264 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.time.LocalDate; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; +import org.apache.servicecomb.codec.protobuf.definition.ProtobufManager; +import org.apache.servicecomb.codec.protobuf.definition.RequestRootDeserializer; +import org.apache.servicecomb.codec.protobuf.definition.RequestRootSerializer; +import org.apache.servicecomb.codec.protobuf.definition.ResponseRootDeserializer; +import org.apache.servicecomb.codec.protobuf.definition.ResponseRootSerializer; +import org.apache.servicecomb.codec.protobuf.internal.converter.model.ProtoSchema; +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.MicroserviceMeta; +import org.apache.servicecomb.core.definition.MicroserviceVersionsMeta; +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.test.scaffolding.model.Color; +import org.apache.servicecomb.foundation.test.scaffolding.model.Empty; +import org.apache.servicecomb.foundation.test.scaffolding.model.User; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.engine.SwaggerProducer; +import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; +import org.apache.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * SchemaMetaCodec test cases. This test cases covers RestTemplate invoker and producer. + */ +public class TestSchemaMetaCodecRestTemplate { + + private SchemaMeta providerSchemaMeta; + + private SchemaMeta consumerSchemaMeta; + + @BeforeEach + public void setUp() { + ProtobufManager.clear(); + MicroserviceVersionsMeta microserviceVersionsMeta = Mockito.mock(MicroserviceVersionsMeta.class); + ExecutorManager executorManager = Mockito.mock(ExecutorManager.class); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getExecutorManager()).thenReturn(executorManager); + + MicroserviceMeta providerMicroserviceMeta = Mockito.mock(MicroserviceMeta.class); + Mockito.when(providerMicroserviceMeta.getScbEngine()).thenReturn(scbEngine); + Mockito.when(providerMicroserviceMeta.getMicroserviceVersionsMeta()).thenReturn(microserviceVersionsMeta); + Mockito.when(providerMicroserviceMeta.getMicroserviceName()).thenReturn("test"); + Mockito.when(providerMicroserviceMeta.getExtData(ProtobufManager.EXT_ID)).thenReturn(null); + + MicroserviceMeta consumerMicroserviceMeta = Mockito.mock(MicroserviceMeta.class); + Mockito.when(consumerMicroserviceMeta.getScbEngine()).thenReturn(scbEngine); + Mockito.when(consumerMicroserviceMeta.getMicroserviceVersionsMeta()).thenReturn(microserviceVersionsMeta); + Mockito.when(consumerMicroserviceMeta.getMicroserviceName()).thenReturn("test"); + Mockito.when(consumerMicroserviceMeta.getExtData(ProtobufManager.EXT_ID)).thenReturn(null); + + SpringmvcSwaggerGenerator swaggerGenerator = new SpringmvcSwaggerGenerator(ProtoSchema.class); + SwaggerEnvironment swaggerEnvironment = new SwaggerEnvironment(); + + 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", swaggerProducer.getSwagger()); + } + + private Invocation mockInvocation(String operation, InvocationType invocationType) { + OperationMeta operationMeta; + boolean isProvider; + Invocation invocation = Mockito.mock(Invocation.class); + InvocationRuntimeType invocationRuntimeType; + + if (InvocationType.CONSUMER == invocationType) { + operationMeta = consumerSchemaMeta.getOperations().get(operation); + isProvider = false; + Mockito.when(invocation.getSchemaMeta()).thenReturn(consumerSchemaMeta); + invocationRuntimeType = operationMeta.buildBaseConsumerRuntimeType(); + } else { + operationMeta = providerSchemaMeta.getOperations().get(operation); + isProvider = true; + Mockito.when(invocation.getSchemaMeta()).thenReturn(providerSchemaMeta); + invocationRuntimeType = operationMeta.buildBaseProviderRuntimeType(); + } + + MicroserviceMeta microserviceMeta = operationMeta.getMicroserviceMeta(); + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + Mockito.when(invocation.getInvocationRuntimeType()) + .thenReturn(invocationRuntimeType); + Mockito.when(invocation.findResponseType(200)) + .thenReturn(invocationRuntimeType.findResponseType(200)); + Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); + Mockito.when(invocation.getMicroserviceMeta()).thenReturn(microserviceMeta); + + 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.PROVIDER); + + OperationProtobuf providerOperationProtobuf = ProtobufManager + .getOrCreateOperation(providerInvocation); + OperationProtobuf consumerOperationProtobuf = ProtobufManager + .getOrCreateOperation(consumerInvocation); + User user = new User(); + user.name = "user"; + User friend = new User(); + friend.name = "friend"; + List friends = new ArrayList<>(); + friends.add(friend); + user.friends = friends; + byte[] values; + + // request message + Map args = new HashMap<>(); + RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); + user.friends = friends; + args.put("user", user); + values = requestSerializer.serialize(args); + + RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); + Map decodedUserArgs = requestDeserializer.deserialize(values); + Assertions.assertEquals(user.name, ((User) decodedUserArgs.get("user")).name); + Assertions.assertEquals(user.friends.get(0).name, ((User) decodedUserArgs.get("user")).friends.get(0).name); + + // response message + ResponseRootSerializer responseSerializer = providerOperationProtobuf.findResponseRootSerializer(200); + values = responseSerializer.serialize(user); + ResponseRootDeserializer responseDeserializer = consumerOperationProtobuf.findResponseRootDeserializer(200); + User decodedUser = (User) responseDeserializer + .deserialize(values, TypeFactory.defaultInstance().constructType(User.class)); + Assertions.assertEquals(user.name, decodedUser.name); + Assertions.assertEquals(user.friends.get(0).name, decodedUser.friends.get(0).name); + + user.friends = new ArrayList<>(); + values = responseSerializer.serialize(user); + decodedUser = (User) responseDeserializer + .deserialize(values, TypeFactory.defaultInstance().constructType(User.class)); + Assertions.assertEquals(user.name, decodedUser.name); + // proto buffer encode and decode empty list to be null + Assertions.assertNull(decodedUser.friends); + } + + @Test + @SuppressWarnings({"rawtypes", "unchecked"}) + public void testProtoSchemaOperationBase() throws Exception { + Invocation consumerInvocation = mockInvocation("base", InvocationType.CONSUMER); + Invocation providerInvocation = mockInvocation("base", InvocationType.PROVIDER); + + OperationProtobuf providerOperationProtobuf = ProtobufManager + .getOrCreateOperation(providerInvocation); + OperationProtobuf consumerOperationProtobuf = ProtobufManager + .getOrCreateOperation(consumerInvocation); + byte[] values; + + // request message + RequestRootSerializer requestSerializer = consumerOperationProtobuf.getRequestRootSerializer(); + boolean boolValue = true; + int iValue = 20; + long lValue = 30L; + float fValue = 40f; + double dValue = 50D; + String sValue = "hello"; + int[] iArray = new int[] {60, 70}; + Color color = Color.BLUE; + LocalDate localDate = LocalDate.of(2019, 10, 1); + Date date = new Date(); + Empty empty = new Empty(); + Map args = new HashMap<>(); + args.put("boolValue", boolValue); + args.put("iValue", iValue); + args.put("lValue", lValue); + args.put("fValue", fValue); + args.put("dValue", dValue); + args.put("sValue", sValue); + args.put("iArray", iArray); + args.put("color", color); + args.put("localDate", localDate); + args.put("date", date); + args.put("empty", empty); + values = requestSerializer.serialize(args); + RequestRootDeserializer requestDeserializer = providerOperationProtobuf.getRequestRootDeserializer(); + Map decodedArgs = requestDeserializer.deserialize(values); + Assertions.assertEquals(boolValue, decodedArgs.get("boolValue")); + Assertions.assertEquals(iValue, decodedArgs.get("iValue")); + Assertions.assertEquals(lValue, decodedArgs.get("lValue")); + Assertions.assertEquals(fValue, decodedArgs.get("fValue")); + Assertions.assertEquals(dValue, decodedArgs.get("dValue")); + Assertions.assertArrayEquals(iArray, (int[]) decodedArgs.get("iArray")); + Assertions.assertEquals(color, decodedArgs.get("color")); + Assertions.assertEquals(date, decodedArgs.get("date")); + Assertions.assertTrue(decodedArgs.get("localDate") instanceof LocalDate); + Assertions.assertEquals(localDate, decodedArgs.get("localDate")); + Assertions.assertTrue(decodedArgs.get("empty") instanceof Empty); + + // default value testing + args.put("boolValue", false); + args.put("iValue", 0); + args.put("lValue", 0L); + args.put("fValue", 0F); + args.put("dValue", 0D); + args.put("sValue", null); + args.put("iArray", new int[0]); + args.put("color", null); + args.put("localDate", null); + args.put("date", null); + args.put("empty", null); + values = requestSerializer.serialize(args); + decodedArgs = requestDeserializer.deserialize(values); + Assertions.assertNull(decodedArgs.get("boolValue")); + Assertions.assertNull(decodedArgs.get("iValue")); + Assertions.assertNull(decodedArgs.get("lValue")); + Assertions.assertNull(decodedArgs.get("fValue")); + Assertions.assertNull(decodedArgs.get("dValue")); + Assertions.assertNull(decodedArgs.get("iArray")); + Assertions.assertNull(decodedArgs.get("color")); + Assertions.assertNull(decodedArgs.get("localDate")); + Assertions.assertNull(decodedArgs.get("date")); + Assertions.assertNull(decodedArgs.get("empty")); + + // response message + ResponseRootSerializer responseSerializer = providerOperationProtobuf.findResponseRootSerializer(200); + values = responseSerializer.serialize(30); + ResponseRootDeserializer responseDeserializer = consumerOperationProtobuf.findResponseRootDeserializer(200); + Object decodedValue = responseDeserializer + .deserialize(values, TypeFactory.defaultInstance().constructType(int.class)); + Assertions.assertEquals(30, (int) decodedValue); + } +} 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 new file mode 100644 index 00000000000..535404f7bc0 --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSwaggerToProtoGenerator.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.codec.protobuf.internal.converter; + +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 io.protostuff.compiler.model.Proto; +import io.swagger.v3.oas.models.OpenAPI; + +public class TestSwaggerToProtoGenerator { + @Test + public void convert() throws IOException { + URL url = TestSwaggerToProtoGenerator.class.getClassLoader().getResource("ProtoSchema.proto"); + String protoContent = IOUtils.toString(url, StandardCharsets.UTF_8); + int idx = protoContent.indexOf("syntax = "); + protoContent = protoContent.substring(idx); + + SpringmvcSwaggerGenerator swaggerGenerator = new SpringmvcSwaggerGenerator(ProtoSchema.class); + OpenAPI swagger = swaggerGenerator.generate(); + + SwaggerToProtoGenerator generator = new SwaggerToProtoGenerator("a.b", swagger); + Proto proto = generator.convert(); + + Assertions.assertEquals(protoContent.replaceAll("\r\n", "\n"), + new ProtoToStringGenerator(proto).protoToString().replaceAll("\r\n", "\n")); + } + + @Test + public void testEscape() { + Assertions.assertEquals("hello_my_service", SwaggerToProtoGenerator.escapeMessageName("hello.my.service")); + Assertions.assertEquals("hello_my_service", SwaggerToProtoGenerator.escapeMessageName("hello_my_service")); + Assertions.assertEquals("hello.my_service", SwaggerToProtoGenerator.escapePackageName("hello.my-service")); + Assertions.assertEquals("hello.test.test", SwaggerToProtoGenerator.escapePackageName("hello.test.test")); + Assertions.assertEquals("hello_my.test.test", SwaggerToProtoGenerator.escapePackageName("hello:my.test.test")); + Assertions.assertFalse(SwaggerToProtoGenerator.isValidEnum("hello.test.test")); + Assertions.assertFalse(SwaggerToProtoGenerator.isValidEnum("hello.my-service")); + Assertions.assertTrue(SwaggerToProtoGenerator.isValidEnum("My_ENum")); + } +} diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/FieldNeedWrap.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/FieldNeedWrap.java new file mode 100644 index 00000000000..f97b6b50a7d --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/FieldNeedWrap.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.codec.protobuf.internal.converter.model; + +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.foundation.test.scaffolding.model.User; + +public class FieldNeedWrap { + public List> listListUser; + + public List> listMapUser; + + public Map> mapListUser; + + public Map> mapMapUser; + + public List>> listListListUser; + + public List>> listListMapUser; + + public List>> listMapListUser; + + public List>> listMapMapUser; + + public Map>> mapMapListUser; + + public Map>> mapMapMapUser; +} 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 new file mode 100644 index 00000000000..169e742c9b5 --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchema.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.model; + + +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import java.util.Map; + +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.User; +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 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(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, + LocalDate localDate, Date date, @RequestBody Empty empty) { + return 0; + } + + @GetMapping(path = "/bytes") + public byte[] bytes(@RequestBody byte[] value) { + return null; + } + + @GetMapping(path = "/colorBody") + public Color colorBody(@RequestBody Color color) { + return null; + } + + @GetMapping(path = "/obj") + public Object obj(@RequestBody Object value) { + return null; + } + + @GetMapping(path = "/user") + public User user(@RequestBody User user) { + return null; + } + + @GetMapping(path = "/userWrapInProtobuf") + public User userWrapInProtobuf(@RequestBody User user, int ivalue) { + return null; + } + + @GetMapping(path = "/listObj") + public List listObj(@RequestBody List objs) { + return null; + } + + @GetMapping(path = "/listUser") + public List listUser(@RequestBody List users) { + return null; + } + + @GetMapping(path = "/mapUser") + public Map mapUser(@RequestBody Map users) { + return null; + } + + @GetMapping(path = "/mapObj") + public Map mapObj(@RequestBody Map objs) { + return null; + } + + @GetMapping(path = "/ref") + public Ref2 ref(@RequestBody Ref1 ref) { + return null; + } + + @GetMapping(path = "/noParamVoid") + public void noParamVoid() { + } + + @PostMapping(path = "/listListString") + public List> listListString(@RequestBody List> value) { + return value; + } + + @PostMapping(path = "/listListUser") + public List> listListUser(@RequestBody List> value) { + return value; + } + + @PostMapping(path = "/listMapString") + public List> listMapString(@RequestBody List> value) { + return value; + } + + @PostMapping(path = "/listMapUser") + public List> listMapUser(@RequestBody List> value) { + return value; + } + + @PostMapping(path = "/mapListString") + public Map> mapListString(@RequestBody Map> value) { + return value; + } + + @PostMapping(path = "/mapListUser") + public Map> mapListUser(@RequestBody Map> value) { + return value; + } + + @PostMapping(path = "/mapMapString") + public Map> mapMapString(@RequestBody Map> value) { + return value; + } + + @PostMapping(path = "/mapMapUser") + public Map> mapMapUser(@RequestBody Map> value) { + return value; + } + + @PostMapping(path = "/listListListString") + public List>> listListListString(@RequestBody List>> value) { + return value; + } + + @PostMapping(path = "/listListMapString") + public List>> listListMapString(@RequestBody List>> value) { + return value; + } + + @PostMapping(path = "/listMapListString") + public List>> listMapListString(@RequestBody List>> value) { + return value; + } + + @PostMapping(path = "/listMapMapString") + public List>> listMapMapString( + @RequestBody List>> value) { + return value; + } + + @PostMapping(path = "/mapMapListString") + public Map>> mapMapListString( + @RequestBody Map>> value) { + return value; + } + + @PostMapping(path = "/mapMapMapString") + public Map>> mapMapMapString( + @RequestBody Map>> value) { + return value; + } + + @PostMapping(path = "/fieldNeedWrap") + 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/ProtoSchemaIntf.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchemaIntf.java new file mode 100644 index 00000000000..55c91561138 --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchemaIntf.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.codec.protobuf.internal.converter.model; + + +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import java.util.Map; + +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.User; + +public interface ProtoSchemaIntf { + int base(boolean boolValue, int iValue, long lValue, float fValue, double dValue, String sValue, int[] iArray, + Color color, + LocalDate localDate, Date date, Empty empty); + + byte[] bytes(byte[] value); + + + Color colorBody(Color color); + + + Object obj(Object value); + + + User user(User user); + + User userWrapInProtobuf(User user, int ivalue); + + + List listObj(List objs); + + List listUser(List users); + + + Map mapUser(Map users); + + Map mapObj(Map objs); + + Ref2 ref(Ref1 ref); + + + void noParamVoid(); + + List> listListString(List> value); + + + List> listListUser(List> value); + + + List> listMapString(List> value); + + + List> listMapUser(List> value); + + + Map> mapListString(Map> value); + + + Map> mapListUser(Map> value); + + Map> mapMapString(Map> value); + + + Map> mapMapUser(Map> value); + + + List>> listListListString(List>> value); + + + List>> listListMapString(List>> value); + + List>> listMapListString(List>> value); + + + List>> listMapMapString( + List>> value); + + + Map>> mapMapListString( + Map>> value); + + Map>> mapMapMapString( + Map>> value); + + FieldNeedWrap fieldNeedWrap(FieldNeedWrap fieldNeedWrap); +} 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 new file mode 100644 index 00000000000..af01e2e2325 --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/ProtoSchemaPojo.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.codec.protobuf.internal.converter.model; + + +import java.time.LocalDate; +import java.util.Date; +import java.util.List; +import java.util.Map; + +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.User; + +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(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) { + return 0; + } + + public byte[] bytes(byte[] value) { + return null; + } + + + public Color colorBody(Color color) { + return null; + } + + + public Object obj(Object value) { + return null; + } + + + public User user(User user) { + return null; + } + + + public User userWrapInProtobuf(User user, int ivalue) { + return null; + } + + + public List listObj(List objs) { + return null; + } + + public List listUser(List users) { + return null; + } + + + public Map mapUser(Map users) { + return null; + } + + public Map mapObj(Map objs) { + return null; + } + + public Ref2 ref(Ref1 ref) { + return null; + } + + + public void noParamVoid() { + } + + + public List> listListString(List> value) { + return value; + } + + + public List> listListUser(List> value) { + return value; + } + + + public List> listMapString(List> value) { + return value; + } + + + public List> listMapUser(List> value) { + return value; + } + + + public Map> mapListString(Map> value) { + return value; + } + + + public Map> mapListUser(Map> value) { + return value; + } + + + public Map> mapMapString(Map> value) { + return value; + } + + + public Map> mapMapUser(Map> value) { + return value; + } + + + public List>> listListListString(List>> value) { + return value; + } + + + public List>> listListMapString(List>> value) { + return value; + } + + public List>> listMapListString(List>> value) { + return value; + } + + + public List>> listMapMapString( + List>> value) { + return value; + } + + + public Map>> mapMapListString( + Map>> value) { + return value; + } + + public Map>> mapMapMapString( + Map>> value) { + return value; + } + + public FieldNeedWrap fieldNeedWrap(FieldNeedWrap fieldNeedWrap) { + return fieldNeedWrap; + } +} diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/Ref1.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/Ref1.java new file mode 100644 index 00000000000..c637bf59e23 --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/Ref1.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.codec.protobuf.internal.converter.model; + +public class Ref1 { + public Ref2 ref; +} diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/Ref2.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/Ref2.java new file mode 100644 index 00000000000..f4b9d9e5a8a --- /dev/null +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/model/Ref2.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.codec.protobuf.internal.converter.model; + +public class Ref2 { + public Ref1 ref; +} 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 new file mode 100644 index 00000000000..c1fb2ed34dd --- /dev/null +++ b/common/common-protobuf/src/test/proto/ModelProtobuf.proto @@ -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. + */ + +syntax = "proto3"; +package proto; + +option java_package = "io.protostuff.runtime.model"; +option java_outer_classname = "ModelProtobuf"; + +message User{ + string name = 1; +} + +message RequestHeader{ + string destMicroservice = 1; + int32 msgType = 2; + int32 flags = 3; + string schemaId = 5; + string operationName = 6; + map cseContext = 7; + map userMap = 8; + repeated string list = 9; + repeated User userList = 10; +} diff --git a/common/common-protobuf/src/test/resources/ProtoSchema.proto b/common/common-protobuf/src/test/resources/ProtoSchema.proto new file mode 100644 index 00000000000..bbb1a476948 --- /dev/null +++ b/common/common-protobuf/src/test/resources/ProtoSchema.proto @@ -0,0 +1,443 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +syntax = "proto3"; +import "google/protobuf/empty.proto"; +import "google/protobuf/any.proto"; +package a.b; + +message Empty { +} + +message FieldNeedWrap { + repeated ListUser listListUser = 1; + repeated MapUser listMapUser = 2; + map mapListUser = 3; + map mapMapUser = 4; + repeated ListListUser listListListUser = 5; + repeated ListMapUser listListMapUser = 6; + repeated MapListUser listMapListUser = 7; + repeated MapMapUser listMapMapUser = 8; + map mapMapListUser = 9; + map mapMapMapUser = 10; +} + +message User { + string name = 1; + repeated User friends = 2; +} + +message Ref1 { + Ref2 ref = 1; +} + +message Ref2 { + Ref1 ref = 1; +} + +//@WrapArguments +message BaseRequestWrap { + bool boolValue = 1; + sint32 iValue = 2; + sint64 lValue = 3; + float fValue = 4; + double dValue = 5; + string sValue = 6; + repeated sint32 iArray = 7; + Enum_2610aa5dc6cd086cf20168892802c9c765a557f4951557340ad9f0982c53e055 color = 8; + int64 localDate = 9; + int64 date = 10; + Empty empty = 11; +} + +//@WrapProperty +message BaseResponseWrap200 { + sint32 value = 1; +} + +//@WrapProperty +message BaseResponseWrap444 { + Enum_2610aa5dc6cd086cf20168892802c9c765a557f4951557340ad9f0982c53e055 value = 1; +} + +//@WrapArguments +message BytesRequestWrap { + bytes value = 1; +} + +//@WrapProperty +message BytesResponseWrap200 { + bytes value = 1; +} + +//@WrapArguments +message ColorBodyRequestWrap { + Enum_2610aa5dc6cd086cf20168892802c9c765a557f4951557340ad9f0982c53e055 color = 1; +} + +//@WrapProperty +message ColorBodyResponseWrap200 { + Enum_2610aa5dc6cd086cf20168892802c9c765a557f4951557340ad9f0982c53e055 value = 1; +} + +//@WrapArguments +message ListListListStringRequestWrap { + repeated ListListString value = 1; +} + +//@WrapProperty +message ListListListStringResponseWrap200 { + repeated ListListString value = 1; +} + +//@WrapArguments +message ListListMapStringRequestWrap { + repeated ListMapString value = 1; +} + +//@WrapProperty +message ListListMapStringResponseWrap200 { + repeated ListMapString value = 1; +} + +//@WrapArguments +message ListListStringRequestWrap { + repeated ListString value = 1; +} + +//@WrapProperty +message ListListStringResponseWrap200 { + repeated ListString value = 1; +} + +//@WrapArguments +message ListListUserRequestWrap { + repeated ListUser value = 1; +} + +//@WrapProperty +message ListListUserResponseWrap200 { + repeated ListUser value = 1; +} + +//@WrapArguments +message ListMapListStringRequestWrap { + repeated MapListString value = 1; +} + +//@WrapProperty +message ListMapListStringResponseWrap200 { + repeated MapListString value = 1; +} + +//@WrapArguments +message ListMapMapStringRequestWrap { + repeated MapMapString value = 1; +} + +//@WrapProperty +message ListMapMapStringResponseWrap200 { + repeated MapMapString value = 1; +} + +//@WrapArguments +message ListMapStringRequestWrap { + repeated MapString value = 1; +} + +//@WrapProperty +message ListMapStringResponseWrap200 { + repeated MapString value = 1; +} + +//@WrapArguments +message ListMapUserRequestWrap { + repeated MapUser value = 1; +} + +//@WrapProperty +message ListMapUserResponseWrap200 { + repeated MapUser value = 1; +} + +//@WrapArguments +message ListObjRequestWrap { + repeated google.protobuf.Any objs = 1; +} + +//@WrapProperty +message ListObjResponseWrap200 { + repeated google.protobuf.Any value = 1; +} + +//@WrapArguments +message ListUserRequestWrap { + repeated User users = 1; +} + +//@WrapProperty +message ListUserResponseWrap200 { + repeated User value = 1; +} + +//@WrapArguments +message MapListStringRequestWrap { + map value = 1; +} + +//@WrapProperty +message MapListStringResponseWrap200 { + map value = 1; +} + +//@WrapArguments +message MapListUserRequestWrap { + map value = 1; +} + +//@WrapProperty +message MapListUserResponseWrap200 { + map value = 1; +} + +//@WrapArguments +message MapMapListStringRequestWrap { + map value = 1; +} + +//@WrapProperty +message MapMapListStringResponseWrap200 { + map value = 1; +} + +//@WrapArguments +message MapMapMapStringRequestWrap { + map value = 1; +} + +//@WrapProperty +message MapMapMapStringResponseWrap200 { + map value = 1; +} + +//@WrapArguments +message MapMapStringRequestWrap { + map value = 1; +} + +//@WrapProperty +message MapMapStringResponseWrap200 { + map value = 1; +} + +//@WrapArguments +message MapMapUserRequestWrap { + map value = 1; +} + +//@WrapProperty +message MapMapUserResponseWrap200 { + map value = 1; +} + +//@WrapArguments +message MapObjRequestWrap { + map objs = 1; +} + +//@WrapProperty +message MapObjResponseWrap200 { + map value = 1; +} + +//@WrapArguments +message MapUserRequestWrap { + map users = 1; +} + +//@WrapProperty +message MapUserResponseWrap200 { + map value = 1; +} + +//@WrapArguments +message TestTextPlainRequestWrap { + string fieldNeedWrap = 1; +} + +//@WrapProperty +message TestTextPlainResponseWrap200 { + string value = 1; +} + +//@WrapArguments +message UserWrapInProtobufRequestWrap { + sint32 ivalue = 1; + User user = 2; +} + +//@WrapProperty +message ListUser { + repeated User value = 1; +} + +//@WrapProperty +message MapUser { + map value = 1; +} + +//@WrapProperty +message ListListUser { + repeated ListUser value = 1; +} + +//@WrapProperty +message ListMapUser { + repeated MapUser value = 1; +} + +//@WrapProperty +message MapListUser { + map value = 1; +} + +//@WrapProperty +message MapMapUser { + map value = 1; +} + +//@WrapProperty +message ListListString { + repeated ListString value = 1; +} + +//@WrapProperty +message ListMapString { + repeated MapString value = 1; +} + +//@WrapProperty +message ListString { + repeated string value = 1; +} + +//@WrapProperty +message MapListString { + map value = 1; +} + +//@WrapProperty +message MapMapString { + map value = 1; +} + +//@WrapProperty +message MapString { + map value = 1; +} + +enum Enum_2610aa5dc6cd086cf20168892802c9c765a557f4951557340ad9f0982c53e055 { + RED = 0; + YELLOW = 1; + BLUE = 2; +} + +service MainService { + //@Rpc{"argTypeName":"BaseRequestWrap","responses":{"200":{"typeName":"BaseResponseWrap200"},"444":{"typeName":"BaseResponseWrap444"}}} + rpc base (BaseRequestWrap) returns (BaseResponseWrap200); + + //@Rpc{"argTypeName":"BytesRequestWrap","responses":{"200":{"typeName":"BytesResponseWrap200"}}} + rpc bytes (BytesRequestWrap) returns (BytesResponseWrap200); + + //@Rpc{"argTypeName":"ColorBodyRequestWrap","responses":{"200":{"typeName":"ColorBodyResponseWrap200"}}} + rpc colorBody (ColorBodyRequestWrap) returns (ColorBodyResponseWrap200); + + //@Rpc{"argTypeName":"FieldNeedWrap","responses":{"200":{"typeName":"FieldNeedWrap"}}} + rpc fieldNeedWrap (FieldNeedWrap) returns (FieldNeedWrap); + + //@Rpc{"argTypeName":"ListListListStringRequestWrap","responses":{"200":{"typeName":"ListListListStringResponseWrap200"}}} + rpc listListListString (ListListListStringRequestWrap) returns (ListListListStringResponseWrap200); + + //@Rpc{"argTypeName":"ListListMapStringRequestWrap","responses":{"200":{"typeName":"ListListMapStringResponseWrap200"}}} + rpc listListMapString (ListListMapStringRequestWrap) returns (ListListMapStringResponseWrap200); + + //@Rpc{"argTypeName":"ListListStringRequestWrap","responses":{"200":{"typeName":"ListListStringResponseWrap200"}}} + rpc listListString (ListListStringRequestWrap) returns (ListListStringResponseWrap200); + + //@Rpc{"argTypeName":"ListListUserRequestWrap","responses":{"200":{"typeName":"ListListUserResponseWrap200"}}} + rpc listListUser (ListListUserRequestWrap) returns (ListListUserResponseWrap200); + + //@Rpc{"argTypeName":"ListMapListStringRequestWrap","responses":{"200":{"typeName":"ListMapListStringResponseWrap200"}}} + rpc listMapListString (ListMapListStringRequestWrap) returns (ListMapListStringResponseWrap200); + + //@Rpc{"argTypeName":"ListMapMapStringRequestWrap","responses":{"200":{"typeName":"ListMapMapStringResponseWrap200"}}} + rpc listMapMapString (ListMapMapStringRequestWrap) returns (ListMapMapStringResponseWrap200); + + //@Rpc{"argTypeName":"ListMapStringRequestWrap","responses":{"200":{"typeName":"ListMapStringResponseWrap200"}}} + rpc listMapString (ListMapStringRequestWrap) returns (ListMapStringResponseWrap200); + + //@Rpc{"argTypeName":"ListMapUserRequestWrap","responses":{"200":{"typeName":"ListMapUserResponseWrap200"}}} + rpc listMapUser (ListMapUserRequestWrap) returns (ListMapUserResponseWrap200); + + //@Rpc{"argTypeName":"ListObjRequestWrap","responses":{"200":{"typeName":"ListObjResponseWrap200"}}} + rpc listObj (ListObjRequestWrap) returns (ListObjResponseWrap200); + + //@Rpc{"argTypeName":"ListUserRequestWrap","responses":{"200":{"typeName":"ListUserResponseWrap200"}}} + rpc listUser (ListUserRequestWrap) returns (ListUserResponseWrap200); + + //@Rpc{"argTypeName":"MapListStringRequestWrap","responses":{"200":{"typeName":"MapListStringResponseWrap200"}}} + rpc mapListString (MapListStringRequestWrap) returns (MapListStringResponseWrap200); + + //@Rpc{"argTypeName":"MapListUserRequestWrap","responses":{"200":{"typeName":"MapListUserResponseWrap200"}}} + rpc mapListUser (MapListUserRequestWrap) returns (MapListUserResponseWrap200); + + //@Rpc{"argTypeName":"MapMapListStringRequestWrap","responses":{"200":{"typeName":"MapMapListStringResponseWrap200"}}} + rpc mapMapListString (MapMapListStringRequestWrap) returns (MapMapListStringResponseWrap200); + + //@Rpc{"argTypeName":"MapMapMapStringRequestWrap","responses":{"200":{"typeName":"MapMapMapStringResponseWrap200"}}} + rpc mapMapMapString (MapMapMapStringRequestWrap) returns (MapMapMapStringResponseWrap200); + + //@Rpc{"argTypeName":"MapMapStringRequestWrap","responses":{"200":{"typeName":"MapMapStringResponseWrap200"}}} + rpc mapMapString (MapMapStringRequestWrap) returns (MapMapStringResponseWrap200); + + //@Rpc{"argTypeName":"MapMapUserRequestWrap","responses":{"200":{"typeName":"MapMapUserResponseWrap200"}}} + rpc mapMapUser (MapMapUserRequestWrap) returns (MapMapUserResponseWrap200); + + //@Rpc{"argTypeName":"MapObjRequestWrap","responses":{"200":{"typeName":"MapObjResponseWrap200"}}} + rpc mapObj (MapObjRequestWrap) returns (MapObjResponseWrap200); + + //@Rpc{"argTypeName":"MapUserRequestWrap","responses":{"200":{"typeName":"MapUserResponseWrap200"}}} + rpc mapUser (MapUserRequestWrap) returns (MapUserResponseWrap200); + + //@Rpc{"argTypeName":"google.protobuf.Empty","responses":{"200":{"typeName":"google.protobuf.Empty"}}} + rpc noParamVoid (google.protobuf.Empty) returns (google.protobuf.Empty); + + //@Rpc{"argTypeName":"google.protobuf.Any","responses":{"200":{"typeName":"google.protobuf.Any"}}} + rpc obj (google.protobuf.Any) returns (google.protobuf.Any); + + //@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); + + //@Rpc{"argTypeName":"UserWrapInProtobufRequestWrap","responses":{"200":{"typeName":"User"}}} + rpc userWrapInProtobuf (UserWrapInProtobufRequestWrap) returns (User); +} diff --git a/common/common-protobuf/src/test/resources/log4j.properties b/common/common-protobuf/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/common/common-protobuf/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/common/common-rest/pom.xml b/common/common-rest/pom.xml index 6ab359bd8ed..f1f2c84b45b 100644 --- a/common/common-rest/pom.xml +++ b/common/common-rest/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb - common - 0.3.0-SNAPSHOT - - common-rest + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + + org.apache.servicecomb + common + 3.4.0-SNAPSHOT + + + common-rest + Java Chassis::Common::Rest + + + + org.apache.servicecomb + java-chassis-core + + + org.apache.servicecomb + common-protobuf + - - - io.servicecomb - java-chassis-core - - - javax.servlet - javax.servlet-api - + + io.vertx + vertx-codegen + provided + - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + + org.apache.servicecomb + registry-local + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.servicecomb + foundation-test-scaffolding + + + org.apache.servicecomb + swagger-generator-jaxrs + test + + + org.mockito + mockito-inline + test + + diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/AbstractRestServer.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/AbstractRestServer.java deleted file mode 100644 index 4b7dcdf5cfe..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/AbstractRestServer.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest; - -import java.nio.charset.StandardCharsets; -import java.util.Map; - -import javax.ws.rs.core.Response.Status; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.common.rest.codec.RestCodec; -import io.servicecomb.common.rest.codec.RestServerRequestInternal; -import io.servicecomb.common.rest.codec.produce.ProduceProcessor; -import io.servicecomb.common.rest.codec.produce.ProduceProcessorManager; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.common.rest.locator.OperationLocator; -import io.servicecomb.common.rest.locator.ServicePathManager; -import io.servicecomb.core.Const; -import io.servicecomb.core.CseContext; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.Transport; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.invocation.InvocationFactory; -import io.servicecomb.foundation.common.utils.JsonUtils; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -public abstract class AbstractRestServer { - private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRestServer.class); - - // 所属的Transport - protected Transport transport; - - public void setTransport(Transport transport) { - this.transport = transport; - } - - protected void setContext(Invocation invocation, RestServerRequestInternal restRequest) throws Exception { - String strCseContext = restRequest.getHeaderParam(Const.CSE_CONTEXT); - if (StringUtils.isEmpty(strCseContext)) { - return; - } - @SuppressWarnings("unchecked") - Map cseContext = - JsonUtils.readValue(strCseContext.getBytes(StandardCharsets.UTF_8), Map.class); - invocation.setContext(cseContext); - } - - protected void handleRequest(RestServerRequestInternal restRequest, HTTP_RESPONSE httpResponse) { - if (transport == null) { - transport = CseContext.getInstance().getTransportManager().findTransport(Const.RESTFUL); - } - - try { - RestOperationMeta restOperation = findRestOperation(restRequest); - OperationMeta operationMeta = restOperation.getOperationMeta(); - - operationMeta.getExecutor().execute(() -> { - try { - runOnExecutor(restRequest, restOperation, httpResponse); - } catch (Exception e) { - LOGGER.error("rest server onRequest error", e); - sendFailResponse(restRequest, httpResponse, e); - } - }); - } catch (Exception e) { - LOGGER.error("rest server onRequest error", e); - sendFailResponse(restRequest, httpResponse, e); - } - } - - protected void runOnExecutor(RestServerRequestInternal restRequest, RestOperationMeta restOperation, - HTTP_RESPONSE httpResponse) throws Exception { - String acceptType = restRequest.getHeaderParam("Accept"); - ProduceProcessor produceProcessor = - locateProduceProcessor(restRequest, httpResponse, restOperation, acceptType); - if (produceProcessor == null) { - // locateProduceProcessor内部已经应答了 - return; - } - - Object[] args = RestCodec.restToArgs(restRequest, restOperation); - Invocation invocation = - InvocationFactory.forProvider(transport.getEndpoint(), - restOperation.getOperationMeta(), - args); - - this.setContext(invocation, restRequest); - this.setHttpRequestContext(invocation, restRequest); - - invocation.next(resp -> { - sendResponse(restRequest, httpResponse, produceProcessor, resp); - }); - } - - protected RestOperationMeta findRestOperation(RestServerRequestInternal restRequest) { - String targetMicroserviceName = restRequest.getHeaderParam(Const.TARGET_MICROSERVICE); - if (targetMicroserviceName == null) { - // for compatible - targetMicroserviceName = RegistryUtils.getMicroservice().getServiceName(); - } - MicroserviceMeta selfMicroserviceMeta = - CseContext.getInstance().getMicroserviceMetaManager().ensureFindValue(targetMicroserviceName); - ServicePathManager servicePathManager = ServicePathManager.getServicePathManager(selfMicroserviceMeta); - if (servicePathManager == null) { - LOGGER.error("No schema in microservice"); - throw new InvocationException(Status.NOT_FOUND, Status.NOT_FOUND.getReasonPhrase()); - } - - OperationLocator locator = - servicePathManager.producerLocateOperation(restRequest.getPath(), restRequest.getMethod()); - restRequest.setPathParamMap(locator.getPathVarMap()); - - return locator.getOperation(); - } - - // 找不到processor,则已经完成了应答,外界不必再处理 - protected ProduceProcessor locateProduceProcessor(RestServerRequestInternal restRequest, - HTTP_RESPONSE httpResponse, - RestOperationMeta restOperation, String acceptType) { - ProduceProcessor produceProcessor = restOperation.ensureFindProduceProcessor(acceptType); - if (produceProcessor != null) { - return produceProcessor; - } - - String msg = String.format("Accept %s is not supported", acceptType); - InvocationException exception = new InvocationException(Status.NOT_ACCEPTABLE, msg); - sendFailResponse(restRequest, httpResponse, exception); - return null; - } - - public void sendFailResponse(RestServerRequestInternal restRequest, HTTP_RESPONSE httpResponse, - Throwable throwable) { - Response response = Response.createProducerFail(throwable); - sendResponse(restRequest, httpResponse, ProduceProcessorManager.DEFAULT_PROCESSOR, response); - } - - // 成功、失败的统一应答处理,这里不能再出异常了,再出了异常也没办法处理 - protected void sendResponse(RestServerRequestInternal restRequest, HTTP_RESPONSE httpServerResponse, - ProduceProcessor produceProcessor, Response response) { - try { - doSendResponse(httpServerResponse, produceProcessor, response); - } catch (Throwable e) { - // 这只能是bug,没有办法再兜底了,只能记录日志 - // 如果统一处理为500错误,也无法确定swagger中500对应的数据模型 - // 并且本次调用本身可能就是500进来的 - LOGGER.error("send response failed.", e); - } finally { - if (restRequest != null) { - restRequest.complete(); - } - } - } - - // 成功、失败的统一应答处理 - protected abstract void doSendResponse(HTTP_RESPONSE httpServerResponse, ProduceProcessor produceProcessor, - Response response) throws Exception; - - // 将http request注入到invocation的handler context - protected abstract void setHttpRequestContext(Invocation invocation, RestServerRequestInternal restRequest); -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/RestConst.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/RestConst.java deleted file mode 100644 index f291e41ec58..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/RestConst.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest; - -public final class RestConst { - private RestConst() { - } - - public static final String HTTP_REQUEST_CREATOR = "server-http-request-creator"; - - public static final String REST_CLIENT_REQUEST_PATH = "rest-client-request-path"; - - public static final String SWAGGER_REST_OPERATION = "swaggerRestOperation"; - - public static final String REST = "rest"; - - public static final String SCHEME = "cse"; - - public static final String URI_PREFIX = SCHEME + "://"; -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/RestEngineSchemaListener.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/RestEngineSchemaListener.java deleted file mode 100644 index 70da8d28838..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/RestEngineSchemaListener.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest; - -import java.util.HashMap; -import java.util.Map; - -import javax.inject.Inject; - -import org.springframework.stereotype.Component; - -import io.servicecomb.common.rest.locator.ServicePathManager; -import io.servicecomb.core.BootListener; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.definition.loader.SchemaListener; -import io.servicecomb.serviceregistry.RegistryUtils; - -@Component -public class RestEngineSchemaListener implements SchemaListener, BootListener { - private MicroserviceMetaManager microserviceMetaManager; - - @Inject - public void setMicroserviceMetaManager(MicroserviceMetaManager microserviceMetaManager) { - this.microserviceMetaManager = microserviceMetaManager; - } - - @Override - public void onBootEvent(BootEvent event) { - if (!event.getEventType().equals(EventType.BEFORE_REGISTRY)) { - return; - } - - MicroserviceMeta microserviceMeta = - microserviceMetaManager.getOrCreateMicroserviceMeta(RegistryUtils.getMicroservice()); - ServicePathManager servicePathManager = ServicePathManager.getServicePathManager(microserviceMeta); - if (servicePathManager != null) { - servicePathManager.buildProducerPaths(); - } - } - - @Override - public void onSchemaLoaded(SchemaMeta... schemaMetas) { - // 此时相应的ServicePathManager可能正在被使用,为避免太高的复杂度,使用copy on write逻辑 - Map mgrMap = new HashMap<>(); - for (SchemaMeta schemaMeta : schemaMetas) { - MicroserviceMeta microserviceMeta = schemaMeta.getMicroserviceMeta(); - ServicePathManager mgr = findPathManager(mgrMap, microserviceMeta); - mgr.addSchema(schemaMeta); - } - - for (ServicePathManager mgr : mgrMap.values()) { - // 对具有动态path operation进行排序 - mgr.sortPath(); - - mgr.saveToMicroserviceMeta(); - } - } - - protected ServicePathManager findPathManager(Map mgrMap, - MicroserviceMeta microserviceMeta) { - ServicePathManager mgr = mgrMap.get(microserviceMeta.getName()); - if (mgr != null) { - return mgr; - } - - mgr = ServicePathManager.getServicePathManager(microserviceMeta); - if (mgr == null) { - mgr = new ServicePathManager(microserviceMeta); - } else { - mgr = mgr.cloneServicePathManager(); - } - mgrMap.put(microserviceMeta.getName(), mgr); - return mgr; - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/LocalRestServerRequest.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/LocalRestServerRequest.java deleted file mode 100644 index cfc81100ff5..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/LocalRestServerRequest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec; - -import java.util.List; -import java.util.Map; - -import javax.ws.rs.core.HttpHeaders; - -import io.servicecomb.foundation.common.utils.HttpUtils; - -public class LocalRestServerRequest implements RestServerRequest { - private Map pathParams; - - private Map> queryParams; - - private Map> httpHeaders; - - private Object bodyObject; - - public LocalRestServerRequest(Map pathParams, Map> queryParams, - Map> httpHeaders, Object bodyObject) { - this.pathParams = pathParams; - this.queryParams = queryParams; - this.httpHeaders = httpHeaders; - - this.bodyObject = bodyObject; - } - - @Override - public String getPath() { - throw new Error("no need to impl"); - } - - @Override - public String getMethod() { - throw new Error("not support"); - } - - @Override - public String getContentType() { - return null; - } - - @Override - public String[] getQueryParam(String key) { - List values = queryParams.get(key); - if (values == null) { - return null; - } - - return values.toArray(new String[values.size()]); - } - - @Override - public String getPathParam(String key) { - return pathParams.get(key); - } - - @Override - public String getHeaderParam(String key) { - List headerValues = httpHeaders.get(key); - return (headerValues != null ? headerValues.get(0) : null); - } - - @Override - public Object getFormParam(String key) { - if (bodyObject == null) { - return null; - } - - // 走到form这里来,说明bodyObject必然是map - @SuppressWarnings("unchecked") - Map form = (Map) bodyObject; - return form.get(key); - } - - @Override - public String getCookieParam(String key) { - List cookieList = httpHeaders.get(HttpHeaders.COOKIE); - return HttpUtils.getCookieParamValue(key, cookieList); - } - - @Override - public Object getBody() throws Exception { - return bodyObject; - } - - @Override - public Map getQueryParams() { - throw new Error("no need to impl"); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestClientRequest.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestClientRequest.java deleted file mode 100644 index f3b46b7fbd5..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestClientRequest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec; - -import io.vertx.core.buffer.Buffer; - -/** - * vertx的HttpClientRequest没有getHeader的能力 - * 在写cookie参数时,没办法多次添加cookie,所以只能进行接口包装 - */ -public interface RestClientRequest { - - void write(Buffer bodyBuffer); - - void end() throws Exception; - - void addCookie(String name, String value); - - void putHeader(String name, String value); - - void addForm(String name, Object value); -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestCodec.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestCodec.java deleted file mode 100644 index 262d260f08f..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestCodec.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec; - -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.common.rest.definition.RestParam; -import io.servicecomb.swagger.invocation.exception.ExceptionFactory; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -public final class RestCodec { - private static final Logger LOG = LoggerFactory.getLogger(RestCodec.class); - - private RestCodec() { - } - - public static void argsToRest(Object[] args, RestOperationMeta restOperation, - RestClientRequest clientRequest) throws Exception { - int paramSize = restOperation.getParamList().size(); - if (paramSize == 0) { - return; - } - - if (paramSize != args.length) { - throw new Exception("wrong number of arguments"); - } - - for (int idx = 0; idx < paramSize; idx++) { - RestParam param = restOperation.getParamList().get(idx); - param.getParamProcessor().setValue(clientRequest, args[idx]); - } - } - - public static Object[] restToArgs(RestServerRequest request, - RestOperationMeta restOperation) throws InvocationException { - List paramList = restOperation.getParamList(); - - try { - Object[] paramValues = new Object[paramList.size()]; - for (int idx = 0; idx < paramList.size(); idx++) { - RestParam param = paramList.get(idx); - paramValues[idx] = param.getParamProcessor().getValue(request); - } - - return paramValues; - } catch (Exception e) { - LOG.error("Parameter is not valid, cause " + e.getMessage()); - throw ExceptionFactory.convertProducerException(e, "Parameter is not valid."); - } - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestObjectMapper.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestObjectMapper.java deleted file mode 100644 index 196623d14d9..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestObjectMapper.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.type.TypeFactory; -import com.fasterxml.jackson.databind.util.ISO8601DateFormat; - -public final class RestObjectMapper extends ObjectMapper { - public static final RestObjectMapper INSTANCE = new RestObjectMapper(); - - private static final long serialVersionUID = -8158869347066287575L; - - private static final JavaType STRING_JAVA_TYPE = TypeFactory.defaultInstance().constructType(String.class); - - private RestObjectMapper() { - // swagger中要求date使用ISO8601格式传递,这里与之做了功能绑定,这在cse中是没有问题的 - setDateFormat(new ISO8601DateFormat()); - } - - public String convertToString(Object value) throws Exception { - return convertValue(value, STRING_JAVA_TYPE); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestServerRequest.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestServerRequest.java deleted file mode 100644 index ea642d17693..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestServerRequest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec; - -import java.util.Map; - -/** - * 各种类型的http request接口统一化 - */ -public interface RestServerRequest { - String getPath(); - - String getContentType(); - - String getMethod(); - - String[] getQueryParam(String key); - - Map getQueryParams(); - - String getPathParam(String key); - - String getHeaderParam(String key); - - Object getFormParam(String key); - - String getCookieParam(String key); - - // 可能是inputStream,也可能直接就是目标Object - Object getBody() throws Exception; -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestServerRequestInternal.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestServerRequestInternal.java deleted file mode 100644 index a73aa660f04..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/RestServerRequestInternal.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec; - -import java.util.Map; - -public interface RestServerRequestInternal extends RestServerRequest { - void setPathParamMap(Map pathParamMap); - - void complete(); - - T getHttpRequest(); -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/AbstractParamProcessor.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/AbstractParamProcessor.java deleted file mode 100644 index d47a3c29090..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/AbstractParamProcessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import com.fasterxml.jackson.databind.JavaType; - -public abstract class AbstractParamProcessor implements ParamValueProcessor { - protected String paramPath; - - protected JavaType targetType; - - public AbstractParamProcessor(String paramPath, JavaType targetType) { - this.paramPath = paramPath; - this.targetType = targetType; - } - - public String getParameterPath() { - return paramPath; - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/BodyProcessorCreator.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/BodyProcessorCreator.java deleted file mode 100644 index 7e13a872b3c..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/BodyProcessorCreator.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import java.io.InputStream; -import java.lang.reflect.Type; - -import javax.ws.rs.core.MediaType; - -import org.apache.commons.io.IOUtils; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.common.rest.codec.RestClientRequest; -import io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.common.rest.codec.RestServerRequest; -import io.servicecomb.foundation.vertx.stream.BufferOutputStream; -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.swagger.models.parameters.Parameter; -import io.vertx.core.buffer.Buffer; - -public class BodyProcessorCreator implements ParamValueProcessorCreator { - - public static final String PARAMTYPE = "body"; - - public static class BodyProcessor implements ParamValueProcessor { - protected JavaType targetType; - - public BodyProcessor(JavaType targetType) { - this.targetType = targetType; - } - - @Override - public Object getValue(RestServerRequest request) throws Exception { - // 从payload中获取参数 - Object body = request.getBody(); - if (body == null) { - return null; - } - - if (InputStream.class.isInstance(body)) { - InputStream inputStream = (InputStream) body; - - String contentType = request.getContentType(); - if (contentType != null && contentType.startsWith(MediaType.TEXT_PLAIN)) { - // TODO: we should consider body encoding - return IOUtils.toString(inputStream, "UTF-8"); - } - return RestObjectMapper.INSTANCE.readValue(inputStream, targetType); - } - - return RestObjectMapper.INSTANCE.convertValue(body, targetType); - } - - @Override - public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { - try (BufferOutputStream output = new BufferOutputStream()) { - RestObjectMapper.INSTANCE.writeValue(output, arg); - clientRequest.write(output.getBuffer()); - } - } - - @Override - public String getParameterPath() { - return ""; - } - - @Override - public String getProcessorType() { - return PARAMTYPE; - } - } - - public static class RawJsonBodyProcessor extends BodyProcessor { - - public RawJsonBodyProcessor(JavaType targetType) { - super(targetType); - } - - @Override - public Object getValue(RestServerRequest request) throws Exception { - // 从payload中获取参数 - Object body = request.getBody(); - if (body == null) { - return null; - } - - if (InputStream.class.isInstance(body)) { - InputStream inputStream = (InputStream) body; - // TODO: we should consider body encoding - return IOUtils.toString(inputStream, "UTF-8"); - } - - return RestObjectMapper.INSTANCE.convertValue(body, targetType); - } - - @Override - public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { - clientRequest.write(Buffer.buffer((String) arg)); - } - } - - public BodyProcessorCreator() { - ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); - } - - @Override - public ParamValueProcessor create(Parameter parameter, Type genericParamType) { - JavaType targetType = TypeFactory.defaultInstance().constructType(genericParamType); - boolean rawJson = ClassUtils.isRawJsonType(parameter); - if (genericParamType.getTypeName().equals(String.class.getTypeName()) && rawJson) { - return new RawJsonBodyProcessor(targetType); - } - - return new BodyProcessor(targetType); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/CookieProcessorCreator.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/CookieProcessorCreator.java deleted file mode 100644 index c5cc0202611..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/CookieProcessorCreator.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import java.lang.reflect.Type; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.common.rest.codec.RestClientRequest; -import io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.common.rest.codec.RestServerRequest; -import io.swagger.models.parameters.Parameter; - -public class CookieProcessorCreator implements ParamValueProcessorCreator { - public static final String PARAMTYPE = "cookie"; - - public static class CookieProcessor extends AbstractParamProcessor { - public CookieProcessor(String paramPath, JavaType targetType) { - super(paramPath, targetType); - } - - @Override - public Object getValue(RestServerRequest request) throws Exception { - String param = request.getCookieParam(paramPath); - if (param == null) { - return null; - } - - return convertValue(param, targetType); - } - - @Override - public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { - clientRequest.addCookie(paramPath, RestObjectMapper.INSTANCE.convertToString(arg)); - } - - @Override - public String getProcessorType() { - return PARAMTYPE; - } - } - - public CookieProcessorCreator() { - ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); - } - - @Override - public ParamValueProcessor create(Parameter parameter, Type genericParamType) { - JavaType targetType = TypeFactory.defaultInstance().constructType(genericParamType); - return new CookieProcessor(parameter.getName(), targetType); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/FormProcessorCreator.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/FormProcessorCreator.java deleted file mode 100644 index d30b5696925..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/FormProcessorCreator.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import java.lang.reflect.Type; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.common.rest.codec.RestClientRequest; -import io.servicecomb.common.rest.codec.RestServerRequest; -import io.swagger.models.parameters.Parameter; - -public class FormProcessorCreator implements ParamValueProcessorCreator { - public static final String PARAMTYPE = "formData"; - - public static class FormProcessor extends AbstractParamProcessor { - public FormProcessor(String paramPath, JavaType targetType) { - super(paramPath, targetType); - } - - @Override - public Object getValue(RestServerRequest request) throws Exception { - Object param = request.getFormParam(paramPath); - if (param == null) { - return null; - } - - return convertValue(param, targetType); - } - - @Override - public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { - clientRequest.addForm(paramPath, arg); - } - - @Override - public String getProcessorType() { - return PARAMTYPE; - } - } - - public FormProcessorCreator() { - ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); - } - - @Override - public ParamValueProcessor create(Parameter parameter, Type genericParamType) { - JavaType targetType = TypeFactory.defaultInstance().constructType(genericParamType); - return new FormProcessor(parameter.getName(), targetType); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java deleted file mode 100644 index cbee20cb6e2..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import java.lang.reflect.Type; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.common.rest.codec.RestClientRequest; -import io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.common.rest.codec.RestServerRequest; -import io.swagger.models.parameters.Parameter; - -public class HeaderProcessorCreator implements ParamValueProcessorCreator { - public static final String PARAMTYPE = "header"; - - public static class HeaderProcessor extends AbstractParamProcessor { - public HeaderProcessor(String paramPath, JavaType targetType) { - super(paramPath, targetType); - } - - @Override - public Object getValue(RestServerRequest request) throws Exception { - String param = request.getHeaderParam(paramPath); - if (param == null) { - return null; - } - - return convertValue(param, targetType); - } - - @Override - public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { - clientRequest.putHeader(paramPath, RestObjectMapper.INSTANCE.convertToString(arg)); - } - - @Override - public String getProcessorType() { - return PARAMTYPE; - } - } - - public HeaderProcessorCreator() { - ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); - } - - @Override - public ParamValueProcessor create(Parameter parameter, Type genericParamType) { - JavaType targetType = TypeFactory.defaultInstance().constructType(genericParamType); - return new HeaderProcessor(parameter.getName(), targetType); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/ParamValueProcessor.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/ParamValueProcessor.java deleted file mode 100644 index 2476868b4b9..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/ParamValueProcessor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.common.rest.codec.RestClientRequest; -import io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.common.rest.codec.RestServerRequest; - -public interface ParamValueProcessor { - Object getValue(RestServerRequest request) throws Exception; - - void setValue(RestClientRequest clientRequest, Object arg) throws Exception; - - default Object convertValue(Object value, JavaType targetType) { - return RestObjectMapper.INSTANCE.convertValue(value, targetType); - } - - String getParameterPath(); - - String getProcessorType(); -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/ParamValueProcessorCreator.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/ParamValueProcessorCreator.java deleted file mode 100644 index c68c4f032a5..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/ParamValueProcessorCreator.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import java.lang.reflect.Type; - -import io.swagger.models.parameters.Parameter; - -public interface ParamValueProcessorCreator { - ParamValueProcessor create(Parameter parameter, Type genericParamType); -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/ParamValueProcessorCreatorManager.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/ParamValueProcessorCreatorManager.java deleted file mode 100644 index 737caed40fc..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/ParamValueProcessorCreatorManager.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import io.servicecomb.foundation.common.RegisterManager; - -public final class ParamValueProcessorCreatorManager extends RegisterManager { - private static final String NAME = "param value processor mgr"; - - public static final ParamValueProcessorCreatorManager INSTANCE = new ParamValueProcessorCreatorManager(); - - static { - new PathProcessorCreator(); - new QueryProcessorCreator(); - new FormProcessorCreator(); - new HeaderProcessorCreator(); - new CookieProcessorCreator(); - new BodyProcessorCreator(); - } - - private ParamValueProcessorCreatorManager() { - super(NAME); - } - - public ParamValueProcessorCreator getBodyProcessorCreater() { - return this.ensureFindValue(BodyProcessorCreator.PARAMTYPE); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/PathProcessorCreator.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/PathProcessorCreator.java deleted file mode 100644 index 2f21c75cf08..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/PathProcessorCreator.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import java.lang.reflect.Type; -import java.net.URLDecoder; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.common.rest.codec.RestClientRequest; -import io.servicecomb.common.rest.codec.RestServerRequest; -import io.swagger.models.parameters.Parameter; - -public class PathProcessorCreator implements ParamValueProcessorCreator { - public static final String PARAMTYPE = "path"; - - public static class PathProcessor extends AbstractParamProcessor { - public PathProcessor(String paramPath, JavaType targetType) { - super(paramPath, targetType); - } - - @Override - public Object getValue(RestServerRequest request) throws Exception { - String value = request.getPathParam(paramPath); - if (value == null) { - return null; - } - return convertValue(URLDecoder.decode(value, "UTF-8"), targetType); - } - - @Override - public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { - // path不需要set - } - - @Override - public String getProcessorType() { - return PARAMTYPE; - } - } - - public PathProcessorCreator() { - ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); - } - - @Override - public ParamValueProcessor create(Parameter parameter, Type genericParamType) { - JavaType targetType = TypeFactory.defaultInstance().constructType(genericParamType); - return new PathProcessor(parameter.getName(), targetType); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/QueryProcessorCreator.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/QueryProcessorCreator.java deleted file mode 100644 index ba92cb8348b..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/QueryProcessorCreator.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import java.lang.reflect.Type; -import java.util.Collection; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.common.rest.codec.RestClientRequest; -import io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.common.rest.codec.RestServerRequest; -import io.swagger.models.parameters.Parameter; - -public class QueryProcessorCreator implements ParamValueProcessorCreator { - public static final String PARAMTYPE = "query"; - - public static class QueryProcessor extends AbstractParamProcessor { - // Query参数可能为数组类型 - protected boolean isArrayOrCollection; - - public QueryProcessor(String paramPath, JavaType targetType, boolean isArrayOrCollection) { - super(paramPath, targetType); - this.isArrayOrCollection = isArrayOrCollection; - } - - @Override - public Object getValue(RestServerRequest request) throws Exception { - String[] param = request.getQueryParam(paramPath); - if (param == null) { - return null; - } - - // 处理数组类型query参数 - if (isArrayOrCollection) { - return convertValue(param, targetType); - } - - if (param.length == 0) { - return null; - } - - return convertValue(param[0], targetType); - } - - @Override - public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { - // query不需要set - } - - protected T convertValue(Object arg, Class cls) throws Exception { - return RestObjectMapper.INSTANCE.convertValue(arg, cls); - } - - @Override - public String getProcessorType() { - return PARAMTYPE; - } - } - - public QueryProcessorCreator() { - ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); - } - - @Override - public ParamValueProcessor create(Parameter parameter, Type genericParamType) { - JavaType targetType = TypeFactory.defaultInstance().constructType(genericParamType); - Class rawCls = targetType.getRawClass(); - boolean isArrayOrCollection = rawCls.isArray() || Collection.class.isAssignableFrom(rawCls); - return new QueryProcessor(parameter.getName(), targetType, isArrayOrCollection); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/RestClientRequestImpl.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/RestClientRequestImpl.java deleted file mode 100644 index d6a9873fc61..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/param/RestClientRequestImpl.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import javax.ws.rs.core.MediaType; - -import io.servicecomb.common.rest.codec.RestClientRequest; -import io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.foundation.vertx.stream.BufferOutputStream; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpClientRequest; -import io.vertx.core.http.HttpHeaders; - -public class RestClientRequestImpl implements RestClientRequest { - protected HttpClientRequest request; - - protected Map cookieMap; - - protected Map formMap; - - protected Buffer bodyBuffer; - - public RestClientRequestImpl(HttpClientRequest request) { - this.request = request; - } - - @Override - public void write(Buffer bodyBuffer) { - this.bodyBuffer = bodyBuffer; - } - - @Override - public void end() throws Exception { - writeCookies(); - - genBodyBuffer(); - - if (bodyBuffer == null) { - request.end(); - return; - } - - request.end(bodyBuffer); - } - - private void genBodyBuffer() throws Exception { - request.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - - 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 = RestObjectMapper.INSTANCE.convertToString(entry.getValue()); - 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()); - } - - @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<>(); - } - - formMap.put(name, value); - } - - @Override - public void putHeader(String name, String value) { - request.putHeader(name, value); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/AbstractProduceProcessor.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/AbstractProduceProcessor.java deleted file mode 100644 index 4c02d4909f0..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/AbstractProduceProcessor.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.produce; - -public abstract class AbstractProduceProcessor implements ProduceProcessor { - public AbstractProduceProcessor() { - ProduceProcessorManager.INSTANCE.register(getName(), this); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.java deleted file mode 100644 index dcb63ddf441..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.produce; - -import java.io.InputStream; -import java.io.OutputStream; - -import javax.ws.rs.core.MediaType; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.common.rest.codec.RestObjectMapper; - -public class ProduceJsonProcessor extends AbstractProduceProcessor { - - @Override - public String getName() { - return MediaType.APPLICATION_JSON; - } - - @Override - public void doEncodeResponse(OutputStream output, Object result) throws Exception { - RestObjectMapper.INSTANCE.writeValue(output, result); - } - - @Override - public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { - return RestObjectMapper.INSTANCE.readValue(input, type); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceProcessor.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceProcessor.java deleted file mode 100644 index d7f3e0cf114..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceProcessor.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.produce; - -import java.io.InputStream; -import java.io.OutputStream; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.foundation.vertx.stream.BufferInputStream; -import io.servicecomb.foundation.vertx.stream.BufferOutputStream; -import io.vertx.core.buffer.Buffer; - -public interface ProduceProcessor { - String getName(); - - default void encodeResponse(OutputStream output, Object result) throws Exception { - if (result == null) { - return; - } - - doEncodeResponse(output, result); - } - - void doEncodeResponse(OutputStream output, Object result) throws Exception; - - default Buffer encodeResponse(Object result) throws Exception { - if (null == result) { - return null; - } - - try (BufferOutputStream output = new BufferOutputStream()) { - doEncodeResponse(output, result); - return output.getBuffer(); - } - } - - default Object decodeResponse(InputStream input, JavaType type) throws Exception { - if (input.available() == 0) { - return null; - } - - return doDecodeResponse(input, type); - } - - Object doDecodeResponse(InputStream input, JavaType type) throws Exception; - - default Object decodeResponse(Buffer buffer, JavaType type) throws Exception { - if (buffer.length() == 0) { - return null; - } - - try (BufferInputStream input = new BufferInputStream(buffer.getByteBuf())) { - return doDecodeResponse(input, type); - } - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java deleted file mode 100644 index f7395b1b77c..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.produce; - -import javax.ws.rs.core.MediaType; - -import io.servicecomb.foundation.common.RegisterManager; - -public final class ProduceProcessorManager extends RegisterManager { - private static final String NAME = "produce processor mgr"; - - public static final String DEFAULT_TYPE = MediaType.APPLICATION_JSON; - - public static final ProduceProcessorManager INSTANCE = new ProduceProcessorManager(); - - public static final ProduceProcessor JSON_PROCESSOR = new ProduceJsonProcessor(); - - public static final ProduceProcessor PLAIN_PROCESSOR = new ProduceTextPlainProcessor(); - - public static final ProduceProcessor DEFAULT_PROCESSOR = JSON_PROCESSOR; - - private ProduceProcessorManager() { - super(NAME); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java deleted file mode 100644 index 6c13151fea9..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.produce; - -import java.io.InputStream; -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 extends AbstractProduceProcessor { - @Override - public String getName() { - return MediaType.TEXT_PLAIN; - } - - @Override - public void doEncodeResponse(OutputStream output, Object result) throws Exception { - output.write(String.valueOf(result).getBytes(StandardCharsets.UTF_8)); - } - - @Override - public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { - // plainText类型,肯定是返回string的,想不出有其他类型的场景 - return IOUtils.toString(input, StandardCharsets.UTF_8); - // TODO: 该方法尚需进一步修改 - // Class returnCls = type.getRawClass(); - // if (returnCls.isPrimitive()) { - // // 处理char类型 - // if (returnCls == char.class) { - // return ((String)result).charAt(0); - // } - // // 处理其他如int, long, boolean等类型 - // return RestObjectMapper.INSTANCE.readValue((String)result, type); - // } - // else { - // // 处理如String等其他复杂类型 - // // 对于更多处理不了的复杂类型建议使用"application/json" - // return returnCls.getConstructor(new Class[] {String.class}) - // .newInstance((String)result); - // } - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/RestOperationComparator.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/RestOperationComparator.java deleted file mode 100644 index 9594165f803..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/RestOperationComparator.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.definition; - -import java.io.Serializable; -import java.util.Comparator; - -import io.servicecomb.common.rest.definition.path.PathRegExp; - -/** - * 用于RestOperation的排序 - */ -public class RestOperationComparator implements Serializable, Comparator { - - private static final long serialVersionUID = -2364909265520813678L; - - @Override - public int compare(RestOperationMeta r1, RestOperationMeta r2) { - // 排序规则: - // 1.静态字符多的优先 - // 2.变量组多的优先 - // 3.带正则表达式的优先 - // 如: - // /customers/{id}/{name}/address - // /customers/{id : .+}/address - // /customers/{id}/address - // /customers/{id : .+} - - PathRegExp path1 = r1.getAbsolutePathRegExp(); - PathRegExp path2 = r2.getAbsolutePathRegExp(); - - int staticCompare = path2.getStaticCharCount() - path1.getStaticCharCount(); - if (staticCompare != 0) { - return staticCompare; - } - - int groupCompare = path2.getGroupCount() - path1.getGroupCount(); - if (groupCompare != 0) { - return groupCompare; - } - - return path2.getGroupWithRegExpCount() - path1.getGroupWithRegExpCount(); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/RestOperationMeta.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/RestOperationMeta.java deleted file mode 100644 index ac3c1ff712d..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/RestOperationMeta.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.definition; - -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import javax.ws.rs.core.MediaType; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.common.rest.codec.produce.ProduceProcessor; -import io.servicecomb.common.rest.codec.produce.ProduceProcessorManager; -import io.servicecomb.common.rest.definition.path.PathRegExp; -import io.servicecomb.common.rest.definition.path.URLPathBuilder; -import io.servicecomb.core.definition.OperationMeta; -import io.swagger.models.Operation; -import io.swagger.models.Swagger; -import io.swagger.models.parameters.Parameter; - -public class RestOperationMeta { - private static final Logger LOGGER = LoggerFactory.getLogger(RestOperationMeta.class); - - private static final String ACCEPT_TYPE_SEPARATER = ","; - - private static final String ACCEPT_TYPE_INNER_SEPARATER = ";"; - - private static final String MEDIATYPE_INNER_SLASH = "/"; - - protected OperationMeta operationMeta; - - protected List produces; - - protected List paramList = new ArrayList<>(); - - // key为参数名 - protected Map paramMap = new LinkedHashMap<>(); - - // key为数据类型,比如json之类 - private Map produceProcessorMap = new HashMap<>(); - - // 不一定等于mgr中的default,因为本operation可能不支持mgr中的default - private ProduceProcessor defaultProcessor; - - protected String absolutePath; - - protected PathRegExp absolutePathRegExp; - - // 快速构建URL path - private URLPathBuilder pathBuilder; - - public void init(OperationMeta operationMeta) { - this.operationMeta = operationMeta; - - Swagger swagger = operationMeta.getSchemaMeta().getSwagger(); - Operation operation = operationMeta.getSwaggerOperation(); - this.produces = operation.getProduces(); - if (produces == null) { - this.produces = swagger.getProduces(); - } - - this.createProduceProcessors(); - - Method method = operationMeta.getMethod(); - Type[] genericParamTypes = method.getGenericParameterTypes(); - if (genericParamTypes.length != operation.getParameters().size()) { - throw new Error("Param count is not equal between swagger and method, path=" + absolutePath); - } - - // 初始化所有rest param - for (int idx = 0; idx < genericParamTypes.length; idx++) { - Parameter parameter = operation.getParameters().get(idx); - Type genericParamType = genericParamTypes[idx]; - - RestParam param = new RestParam(idx, parameter, genericParamType); - addParam(param); - } - - setAbsolutePath(concatPath(swagger.getBasePath(), operationMeta.getOperationPath())); - } - - public void setOperationMeta(OperationMeta operationMeta) { - this.operationMeta = operationMeta; - } - - // 输出b/c/形式的url - 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; - } - - public void setAbsolutePath(String absolutePath) { - this.absolutePath = absolutePath; - this.absolutePathRegExp = createPathRegExp(absolutePath); - this.pathBuilder = new URLPathBuilder(absolutePath, paramMap); - } - - public PathRegExp getAbsolutePathRegExp() { - return this.absolutePathRegExp; - } - - public boolean isAbsoluteStaticPath() { - return this.absolutePathRegExp.isStaticPath(); - } - - protected PathRegExp createPathRegExp(String path) { - if (path == null || path.equals("")) { - throw new Error("null rest url is not supported"); - } - try { - return new PathRegExp(path); - } catch (Exception e) { - LOGGER.error(e.getMessage()); - return null; - } - } - - public RestParam getParamByName(String name) { - return paramMap.get(name); - } - - public RestParam getParamByIndex(int index) { - return paramList.get(index); - } - - public OperationMeta getOperationMeta() { - return operationMeta; - } - - // 为operation创建支持的多种produce processor - protected void createProduceProcessors() { - if (null == produces || produces.isEmpty()) { - for (ProduceProcessor processor : ProduceProcessorManager.INSTANCE.values()) { - this.produceProcessorMap.put(processor.getName(), processor); - } - return; - } - for (String produce : produces) { - ProduceProcessor processor = ProduceProcessorManager.INSTANCE.findValue(produce); - if (processor == null) { - LOGGER.error("produce {} is not supported", produce); - continue; - } - this.produceProcessorMap.put(produce, processor); - } - - defaultProcessor = getDefaultOrFirstProcessor(); - } - - public URLPathBuilder getPathBuilder() { - return this.pathBuilder; - } - - public List getParamList() { - return paramList; - } - - private void addParam(RestParam param) { - paramList.add(param); - paramMap.put(param.getParamName(), param); - } - - public ProduceProcessor findProduceProcessor(String type) { - return this.produceProcessorMap.get(type); - } - - // 选择与accept匹配的produce processor或者缺省的 - public ProduceProcessor ensureFindProduceProcessor(String types) { - if (StringUtils.isEmpty(types)) { - return defaultProcessor; - } - - String[] typeArr = splitAcceptTypes(types); - if (containSpecType(typeArr, MediaType.WILDCARD)) { - return defaultProcessor; - } - if (containSpecType(typeArr, ProduceProcessorManager.DEFAULT_TYPE)) { - return ProduceProcessorManager.DEFAULT_PROCESSOR; - } - - for (String type : typeArr) { - ProduceProcessor processor = this.produceProcessorMap.get(type); - if (null != processor) { - return processor; - } - } - return null; - } - - // 只提取出media type,忽略charset和q值等 - protected String[] splitAcceptTypes(String types) { - String[] typeArr = types.split(ACCEPT_TYPE_SEPARATER); - for (int idxX = 0; idxX < typeArr.length; idxX++) { - String[] strItems = typeArr[idxX].split(ACCEPT_TYPE_INNER_SEPARATER); - for (int idxY = 0; idxY < strItems.length; idxY++) { - if (strItems[idxY].contains(MEDIATYPE_INNER_SLASH)) { - typeArr[idxX] = strItems[idxY].trim(); - break; - } - } - } - return typeArr; - } - - // 检查是否包含特定的类型 - protected boolean containSpecType(String[] typeArr, String specType) { - for (String type : typeArr) { - if (specType.equals(type)) { - return true; - } - } - return false; - } - - public ProduceProcessor getDefaultProcessor() { - return this.defaultProcessor; - } - - // 仅用于测试 - protected void setDefaultProcessor(ProduceProcessor defaultProcessor) { - this.defaultProcessor = defaultProcessor; - } - - // 获取缺省的或者第一个processor - private ProduceProcessor getDefaultOrFirstProcessor() { - ProduceProcessor processor = this.produceProcessorMap.get(ProduceProcessorManager.DEFAULT_TYPE); - if (null == processor) { - for (ProduceProcessor pp : this.produceProcessorMap.values()) { - return pp; - } - } - return processor; - } - - public String getHttpMethod() { - return operationMeta.getHttpMethod(); - } - - public List getProduces() { - return produces; - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/RestParam.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/RestParam.java deleted file mode 100644 index 68f4c0b5afb..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/RestParam.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.definition; - -import java.lang.reflect.Type; -import java.util.Collection; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.common.rest.codec.param.ParamValueProcessor; -import io.servicecomb.common.rest.codec.param.ParamValueProcessorCreator; -import io.servicecomb.common.rest.codec.param.ParamValueProcessorCreatorManager; -import io.swagger.models.parameters.Parameter; - -public class RestParam { - private static final JavaType STRING_ARRAY_TYPE = TypeFactory.defaultInstance().constructArrayType(String.class); - - protected ParamValueProcessor paramProcessor; - - protected String paramName; - - // 在args数组中的下标 - protected int paramIndex; - - public RestParam(int paramIndex, Parameter parameter, Type genericParamType) { - this.paramIndex = paramIndex; - this.paramName = parameter.getName(); - - init(parameter, genericParamType); - } - - public ParamValueProcessor getParamProcessor() { - return this.paramProcessor; - } - - public void setParamProcessor(ParamValueProcessor paramProcessor) { - this.paramProcessor = paramProcessor; - } - - public String getParamName() { - return paramName; - } - - protected void init(Parameter parameter, Type genericParamType) { - String paramType = parameter.getIn(); - ParamValueProcessorCreator creater = - ParamValueProcessorCreatorManager.INSTANCE.ensureFindValue(paramType); - - this.setParamProcessor(creater.create(parameter, genericParamType)); - } - - @SuppressWarnings("unchecked") - public T getValue(Object[] args) { - return (T) args[paramIndex]; - } - - public String[] getValueAsStrings(Object[] args) { - Object value = args[paramIndex]; - if (value == null) { - return null; - } - - if (value.getClass().isArray() || Collection.class.isInstance(value)) { - return (String[]) paramProcessor.convertValue(value, STRING_ARRAY_TYPE); - } - - return new String[] {String.valueOf(value)}; - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/AbstractUrlParamWriter.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/AbstractUrlParamWriter.java deleted file mode 100644 index 4cbe34d04f7..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/AbstractUrlParamWriter.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.definition.path; - -import io.servicecomb.common.rest.definition.RestParam; - -public abstract class AbstractUrlParamWriter implements UrlParamWriter { - protected RestParam param; - - public void setParam(RestParam param) { - this.param = param; - } - - protected Object getParamValue(Object[] args) { - return param.getValue(args); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/PathVarParamWriter.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/PathVarParamWriter.java deleted file mode 100644 index 74ad0c255f9..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/PathVarParamWriter.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.definition.path; - -import io.servicecomb.common.rest.definition.RestParam; - -/** - * 处理动态path - */ -public class PathVarParamWriter extends AbstractUrlParamWriter { - public PathVarParamWriter(RestParam param) { - this.param = param; - } - - @Override - public void write(StringBuilder builder, Object[] args) throws Exception { - builder.append((Object) getParamValue(args)); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/QueryVarParamWriter.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/QueryVarParamWriter.java deleted file mode 100644 index ae1b0657c45..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/QueryVarParamWriter.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.definition.path; - -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.Collection; - -import io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.common.rest.definition.RestParam; - -public class QueryVarParamWriter extends AbstractUrlParamWriter { - // ? or & - private char prefix; - - public QueryVarParamWriter(char prefix, RestParam param) { - this.param = param; - this.prefix = prefix; - } - - @Override - public void write(StringBuilder builder, Object[] args) throws Exception { - builder.append(prefix); - - Object value = getParamValue(args); - if (value == null) { - // 连key都不写进去,才能表达null的概念 - return; - } - - if (value.getClass().isArray()) { - writeArray(builder, value); - return; - } - - if (Collection.class.isInstance(value)) { - writeCollection(builder, value); - return; - } - - writeKeyEqual(builder); - builder.append(encodeNotNullValue(value)); - } - - private void writeKeyEqual(StringBuilder builder) { - builder.append(param.getParamName()).append('='); - } - - @SuppressWarnings("unchecked") - private void writeCollection(StringBuilder builder, Object value) throws Exception { - for (Object item : (Collection) value) { - writeItem(builder, item); - } - - if (((Collection) value).size() != 0) { - deleteLastChar(builder); - } - } - - private void writeArray(StringBuilder builder, Object value) throws Exception { - for (Object item : (Object[]) value) { - writeItem(builder, item); - } - - if (((Object[]) value).length != 0) { - deleteLastChar(builder); - } - } - - private void deleteLastChar(StringBuilder builder) { - builder.setLength(builder.length() - 1); - } - - private void writeItem(StringBuilder builder, Object item) throws Exception { - writeKeyEqual(builder); - - // TODO:数组元素为null,当前找不到表达方式,通过issue跟踪,有解决方案后再来处理 - // http://code.huawei.com/CSE/cse-java-chassis/issues/133 - if (item != null) { - builder.append(encodeNotNullValue(item)); - } - - builder.append('&'); - } - - private String encodeNotNullValue(Object value) throws Exception { - String strValue = RestObjectMapper.INSTANCE.convertToString(value); - return URLEncoder.encode(strValue, StandardCharsets.UTF_8.name()); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/StaticUrlParamWriter.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/StaticUrlParamWriter.java deleted file mode 100644 index 68031084916..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/StaticUrlParamWriter.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.definition.path; - -public class StaticUrlParamWriter implements UrlParamWriter { - - private String staticPath; - - public StaticUrlParamWriter(String staticPath) { - this.staticPath = staticPath; - } - - @Override - public void write(StringBuilder builder, Object[] args) { - builder.append(staticPath); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/URLPathBuilder.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/URLPathBuilder.java deleted file mode 100644 index c2d4d9dd68c..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/URLPathBuilder.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.definition.path; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import io.servicecomb.common.rest.codec.param.QueryProcessorCreator; -import io.servicecomb.common.rest.definition.RestParam; - -/** - * 初始化阶段创建URLPathBuilder,用于加速调用阶段的path创建 - */ -public class URLPathBuilder { - - private List pathParamWriterList = new ArrayList<>(); - - private List queryParamWriterList = new ArrayList<>(); - - private static final String SLASH = "/"; - - public URLPathBuilder(String rawPath, Map paramMap) { - initPathWriterList(rawPath, paramMap); - initQueryWriterList(paramMap); - } - - private void initQueryWriterList(Map paramMap) { - for (RestParam param : paramMap.values()) { - - if (!QueryProcessorCreator.PARAMTYPE.equals(param.getParamProcessor().getProcessorType())) { - continue; - } - - char prefix = '&'; - if (queryParamWriterList.isEmpty()) { - prefix = '?'; - } - - UrlParamWriter dynamicWriter = new QueryVarParamWriter(prefix, param); - queryParamWriterList.add(dynamicWriter); - } - } - - 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; - } - - String tmpPath = ""; - for (int idx = 0; idx < rawPath.length(); idx++) { - char currentChar = rawPath.charAt(idx); - if (currentChar == '{') { - if (tmpPath.length() != 0) { - this.pathParamWriterList.add(new StaticUrlParamWriter(tmpPath)); - tmpPath = ""; - } - } else if (currentChar == '}') { - if (tmpPath.length() != 0) { - RestParam param = paramMap.get(tmpPath); - this.pathParamWriterList.add(new PathVarParamWriter(param)); - tmpPath = ""; - } - } else { - tmpPath += currentChar; - } - } - if (tmpPath.length() != 0) { - this.pathParamWriterList.add(new StaticUrlParamWriter(tmpPath)); - } - } - - public String createRequestPath(Object[] args) throws Exception { - StringBuilder builder = new StringBuilder(); - - genPathString(builder, args); - genQueryString(builder, args); - - return builder.toString(); - } - - public String createPathString(Object[] args) throws Exception { - StringBuilder builder = new StringBuilder(); - genPathString(builder, args); - return builder.toString(); - } - - private void genPathString(StringBuilder builder, Object[] args) throws Exception { - for (UrlParamWriter writer : this.pathParamWriterList) { - writer.write(builder, args); - } - } - - private void genQueryString(StringBuilder builder, Object[] args) throws Exception { - for (UrlParamWriter writer : queryParamWriterList) { - writer.write(builder, args); - } - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/UrlParamWriter.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/UrlParamWriter.java deleted file mode 100644 index 838e58274ea..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/UrlParamWriter.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.definition.path; - -public interface UrlParamWriter { - void write(StringBuilder builder, Object[] args) throws Exception; -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/MicroservicePaths.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/MicroservicePaths.java deleted file mode 100644 index 398984b661c..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/MicroservicePaths.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.locator; - -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 java.util.Map.Entry; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.common.rest.definition.RestOperationComparator; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.foundation.common.exceptions.ServiceCombException; - -public class MicroservicePaths { - private static final Logger LOGGER = LoggerFactory.getLogger(MicroservicePaths.class); - - // 运行阶段,静态path,一次直接查找到目标,不必遍历查找 - // 以path为key - protected Map staticPathOperations = new HashMap<>(); - - // 运行阶段,以path优先级,从高到低排列的operation列表 - protected List dynamicPathOperationsList = new ArrayList<>(); - - public void cloneTo(MicroservicePaths other) { - other.staticPathOperations.putAll(staticPathOperations); - other.dynamicPathOperationsList.addAll(dynamicPathOperationsList); - } - - public void sortPath() { - RestOperationComparator comparator = new RestOperationComparator(); - Collections.sort(this.dynamicPathOperationsList, comparator); - } - - public void addResource(RestOperationMeta swaggerRestOperation) { - if (swaggerRestOperation.isAbsoluteStaticPath()) { - // 静态path - addStaticPathResource(swaggerRestOperation); - return; - } - - dynamicPathOperationsList.add(swaggerRestOperation); - } - - protected void addStaticPathResource(RestOperationMeta operation) { - String httpMethod = operation.getHttpMethod(); - String path = operation.getAbsolutePath(); - OperationGroup group = staticPathOperations.get(path); - if (group == null) { - group = new OperationGroup(); - group.register(httpMethod, operation); - staticPathOperations.put(path, group); - return; - } - - if (group.findValue(httpMethod) == null) { - group.register(httpMethod, operation); - return; - } - - throw new ServiceCombException( - String.format("operation with url %s, method %s is duplicated.", path, httpMethod)); - } - - public Map getStaticPathOperationMap() { - return staticPathOperations; - } - - public List getDynamicPathOperationList() { - return dynamicPathOperationsList; - } - - public void printPaths() { - for (Entry entry : staticPathOperations.entrySet()) { - OperationGroup operationGroup = entry.getValue(); - printPath(operationGroup.values()); - } - - printPath(getDynamicPathOperationList()); - } - - protected void printPath(Collection operations) { - for (RestOperationMeta operation : operations) { - LOGGER.info("Swagger mapped \"{[{}], method=[{}], produces={}}\" onto {}", - operation.getAbsolutePath(), - operation.getHttpMethod(), - operation.getProduces(), - operation.getOperationMeta().getMethod()); - } - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/OperationGroup.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/OperationGroup.java deleted file mode 100644 index 9f8514835e7..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/OperationGroup.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.locator; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.foundation.common.RegisterManager; - -/** - * 存放具有相同path,不同httpmethod的operation - */ -public class OperationGroup extends RegisterManager { - private static final String NAME = "operation group manager"; - - public OperationGroup() { - super(NAME); - } -} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/ServicePathManager.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/ServicePathManager.java deleted file mode 100644 index 687981ef172..00000000000 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/ServicePathManager.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.locator; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.serviceregistry.api.Const; - -/** - * 对静态路径和动态路径的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; - - // equal to swagger - protected MicroservicePaths swaggerPaths = new MicroservicePaths(); - - // we support swagger basePath is not include contextPath and urlPattern - // so for producer, we must concat contextPath and urlPattern - // only valid for microservice of this process - protected MicroservicePaths producerPaths; - - // 已经有哪些schemaId的path信息加进来了 - // 在producer场景中,业务before producer provider事件中将契约注册进来,此时会触发事件,携带注册范围的信息 - // 启动流程的最后阶段,同样会触发一次事件,此时是全量的信息 - // 所以,可能会重复 - protected Set schemaIdSet = new HashSet<>(); - - public static ServicePathManager getServicePathManager(MicroserviceMeta microserviceMeta) { - return microserviceMeta.getExtData(REST_PATH_MANAGER); - } - - public void saveToMicroserviceMeta() { - microserviceMeta.putExtData(REST_PATH_MANAGER, this); - } - - public ServicePathManager(MicroserviceMeta microserviceMeta) { - this.microserviceMeta = microserviceMeta; - } - - public MicroserviceMeta getMicroserviceMeta() { - return microserviceMeta; - } - - public boolean isSchemaExists(String schemaId) { - return schemaIdSet.contains(schemaId); - } - - public void addSchema(SchemaMeta schemaMeta) { - if (isSchemaExists(schemaMeta.getSchemaId())) { - return; - } - - schemaIdSet.add(schemaMeta.getSchemaId()); - for (OperationMeta operationMeta : schemaMeta.getOperations()) { - RestOperationMeta restOperationMeta = new RestOperationMeta(); - restOperationMeta.init(operationMeta); - operationMeta.putExtData(RestConst.SWAGGER_REST_OPERATION, restOperationMeta); - addResource(restOperationMeta); - } - - LOGGER.info("add schema to service paths. {}:{}", - schemaMeta.getMicroserviceName(), - schemaMeta.getSchemaId()); - } - - public ServicePathManager cloneServicePathManager() { - ServicePathManager mgr = new ServicePathManager(microserviceMeta); - swaggerPaths.cloneTo(mgr.swaggerPaths); - mgr.schemaIdSet.addAll(schemaIdSet); - return mgr; - } - - public OperationLocator consumerLocateOperation(String path, String httpMethod) { - String standPath = OperationLocator.getStandardPath(path); - OperationLocator locator = new OperationLocator(); - locator.locate(microserviceMeta.getName(), standPath, httpMethod, swaggerPaths); - - return locator; - } - - public OperationLocator producerLocateOperation(String path, String httpMethod) { - String standPath = OperationLocator.getStandardPath(path); - OperationLocator locator = new OperationLocator(); - locator.locate(microserviceMeta.getName(), standPath, httpMethod, producerPaths); - - return locator; - } - - public void addResource(RestOperationMeta swaggerRestOperation) { - swaggerPaths.addResource(swaggerRestOperation); - } - - public void sortPath() { - swaggerPaths.sortPath(); - } - - public void buildProducerPaths() { - String urlPrefix = System.getProperty(Const.URL_PREFIX); - if (StringUtils.isEmpty(urlPrefix)) { - producerPaths = swaggerPaths; - producerPaths.printPaths(); - return; - } - - producerPaths = new MicroservicePaths(); - for (OperationGroup operationGroup : swaggerPaths.getStaticPathOperationMap().values()) { - addProducerPaths(urlPrefix, operationGroup.values()); - } - - addProducerPaths(urlPrefix, swaggerPaths.getDynamicPathOperationList()); - producerPaths.printPaths(); - } - - private void addProducerPaths(String urlPrefix, Collection restOperationMetas) { - for (RestOperationMeta swaggerRestOperation : restOperationMetas) { - RestOperationMeta producerRestOperation = swaggerRestOperation; - if (!swaggerRestOperation.getAbsolutePath().startsWith(urlPrefix)) { - producerRestOperation = new RestOperationMeta(); - producerRestOperation.init(swaggerRestOperation.getOperationMeta()); - producerRestOperation.setAbsolutePath(urlPrefix + swaggerRestOperation.getAbsolutePath()); - } - producerPaths.addResource(producerRestOperation); - } - } -} 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 new file mode 100644 index 00000000000..a642265ae0f --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/CommonRestConfiguration.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.common.rest; + +import java.util.List; + +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.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.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 scbQueryCodecCsv() { + return new QueryCodecCsv(); + } + + @Bean + public QueryCodecSsv scbQueryCodecSsv() { + return new QueryCodecSsv(); + } + + @Bean + public QueryCodecPipes scbQueryCodecPipes() { + return new QueryCodecPipes(); + } + + @Bean + public QueryCodecMulti scbQueryCodecMulti() { + return new QueryCodecMulti(); + } + + @Bean + public QueryCodecsUtils scbQueryCodecsUtils(QueryCodecs queryCodecs) { + return new QueryCodecsUtils(queryCodecs); + } + + @Bean + public RestServerCodecFilter scbRestServerCodecFilter() { + return new RestServerCodecFilter(); + } + + @Bean + public WebSocketServerCodecFilter scbWebSocketServerCodecFilter() { + return new WebSocketServerCodecFilter(); + } + + @Bean + 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 new file mode 100644 index 00000000000..d9b0df27c92 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/HttpTransportContext.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.common.rest; + +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; +import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; +import org.apache.servicecomb.swagger.invocation.context.TransportContext; + +public class HttpTransportContext implements TransportContext { + private final HttpServletRequestEx requestEx; + + private final HttpServletResponseEx responseEx; + + public HttpTransportContext(HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { + this.requestEx = requestEx; + this.responseEx = responseEx; + } + + public HttpServletRequestEx getRequestEx() { + return requestEx; + } + + public HttpServletResponseEx getResponseEx() { + return responseEx; + } +} 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 new file mode 100644 index 00000000000..bc598f973f6 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestConst.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest; + +import org.apache.commons.lang3.SystemUtils; + +public final class RestConst { + private RestConst() { + } + + public static final String REST_CLIENT_REQUEST_PATH = "rest-client-request-path"; + + public static final String SWAGGER_REST_OPERATION = "swaggerRestOperation"; + + public static final String REST = "rest"; + + public static final String SCHEME = "cse"; + + public static final String SCHEME_NEW = "servicecomb"; + + public static final String URI_PREFIX = SCHEME + "://"; + + public static final String URI_PREFIX_NEW = SCHEME_NEW + "://"; + + // in HttpServletRequest attribute + public static final String PATH_PARAMETERS = "servicecomb-paths"; + + // in HttpServletRequest attribute + public static final String BODY_PARAMETER = "servicecomb-body"; + + //in invocation response + public static final String INVOCATION_HANDLER_RESPONSE = "servicecomb-invocation-handler-response"; + + //in invocation response + public static final String INVOCATION_HANDLER_PROCESSOR = "servicecomb-invocation-handler-processor"; + + //in invocation response + public static final String INVOCATION_HANDLER_REQUESTCLIENT = "servicecomb-invocation-handler-requestclient"; + + public static final String REST_PRODUCER_INVOCATION = "servicecomb-rest-producer-invocation"; + + public static final String REST_INVOCATION_CONTEXT = "servicecomb-rest-invocation-context"; + + public static final String REST_REQUEST = "servicecomb-rest-request"; + + public static final String CONSUMER_HEADER = "servicecomb-rest-consumer-header"; + + public static final String READ_STREAM_PART = "servicecomb-readStreamPart"; + + public static final String UPLOAD_DIR = "servicecomb.uploads.directory"; + + public static final String UPLOAD_DEFAULT_DIR = SystemUtils.JAVA_IO_TMPDIR; + + // limit of one upload file, only available for servlet rest transport + public static final String UPLOAD_MAX_FILE_SIZE = "servicecomb.uploads.maxFileSize"; + + // limit of upload request body + public static final String UPLOAD_MAX_SIZE = "servicecomb.uploads.maxSize"; + + // the size threshold after which files will be written to disk + // only available for servlet rest transport + public static final String UPLOAD_FILE_SIZE_THRESHOLD = "servicecomb.uploads.fileSizeThreshold"; + + public static final String PROVIDER_SCAN_REST_CONTROLLER = "servicecomb.provider.rest.scanRestController"; + + public static final String PRINT_CODEC_ERROR_MESSGAGE = "servicecomb.codec.printErrorMessage"; + + 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 new file mode 100644 index 00000000000..178d52f614d --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestEngineSchemaListener.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.common.rest; + +import org.apache.servicecomb.common.rest.locator.ServicePathManager; +import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.core.SCBEngine.CreateMicroserviceMetaEvent; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.foundation.common.event.EnableExceptionPropagation; + +import com.google.common.eventbus.Subscribe; + +public class RestEngineSchemaListener implements BootListener { + @Override + public int getOrder() { + return -10000; + } + + @Override + public void onBeforeRegistry(BootEvent event) { + createServicePathManager(event.getScbEngine().getProducerMicroserviceMeta()) + .buildProducerPaths(); + event.getScbEngine().getEventBus().register(this); + } + + @EnableExceptionPropagation + @Subscribe + public void onCreateMicroserviceMetaEvent(CreateMicroserviceMetaEvent event) { + MicroserviceMeta microserviceMeta = event.getMicroserviceMeta(); + createServicePathManager(microserviceMeta); + } + + private ServicePathManager createServicePathManager(MicroserviceMeta microserviceMeta) { + // already connect ServicePathManager and MicroserviceMeta instance + // no need to save ServicePathManager instance again + return new ServicePathManager(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 new file mode 100644 index 00000000000..5a6c09d3912 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.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 io.vertx.core.json.Json; + +public abstract class RestProducerInvocationCreator implements InvocationCreator { + private static final Logger LOGGER = LoggerFactory.getLogger(RestVertxProducerInvocationCreator.class); + + protected MicroserviceMeta microserviceMeta; + + protected final Endpoint endpoint; + + protected final HttpServletRequestEx requestEx; + + protected final HttpServletResponseEx responseEx; + + protected RestOperationMeta restOperationMeta; + + protected ProduceProcessor produceProcessor; + + public RestProducerInvocationCreator(MicroserviceMeta microserviceMeta, Endpoint endpoint, + HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { + this.microserviceMeta = microserviceMeta; + this.endpoint = endpoint; + this.requestEx = requestEx; + this.responseEx = responseEx; + } + + @Override + public CompletableFuture createAsync() { + initRestOperation(); + + Invocation invocation = createInstance(); + initInvocationContext(invocation); + addParameterContext(invocation); + initTransportContext(invocation); + + invocation.addLocalContext(RestConst.REST_REQUEST, requestEx); + + 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 = requestEx.getHeader(CoreConst.CSE_CONTEXT); + if (StringUtils.isEmpty(strCseContext)) { + return; + } + + @SuppressWarnings("unchecked") + Map invocationContext = Json.decodeValue(strCseContext, Map.class); + invocation.mergeContext(invocationContext); + } + + protected void addParameterContext(Invocation invocation) { + String headerContextMapper = LegacyPropertyFactory + .getStringProperty(RestConst.HEADER_CONTEXT_MAPPER); + String queryContextMapper = LegacyPropertyFactory + .getStringProperty(RestConst.QUERY_CONTEXT_MAPPER); + + Map headerContextMappers; + if (headerContextMapper != null) { + headerContextMappers = YAMLUtil.yaml2Properties(headerContextMapper); + } else { + headerContextMappers = new HashMap<>(); + } + + Map queryContextMappers; + if (queryContextMapper != null) { + queryContextMappers = YAMLUtil.yaml2Properties(queryContextMapper); + } else { + queryContextMappers = new HashMap<>(); + } + + headerContextMappers.forEach((k, v) -> { + if (v instanceof String && requestEx.getHeader(k) != null) { + invocation.addContext((String) v, requestEx.getHeader(k)); + } + }); + queryContextMappers.forEach((k, v) -> { + if (v instanceof String && requestEx.getParameter(k) != null) { + invocation.addContext((String) v, requestEx.getParameter(k)); + } + }); + } + + protected abstract void initTransportContext(Invocation invocation); + + protected void initRestOperation() { + OperationLocator locator = locateOperation(microserviceMeta); + requestEx.setAttribute(RestConst.PATH_PARAMETERS, locator.getPathVarMap()); + 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(requestEx.getRequestURI(), requestEx.getMethod()); + } +} 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 new file mode 100644 index 00000000000..727346c82d5 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.common.rest.codec.produce.ProduceProcessor; +import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager; +import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter; +import org.apache.servicecomb.core.Invocation; +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.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 RestProducerInvocationFlow extends ProducerInvocationFlow { + private static final Logger LOGGER = LoggerFactory.getLogger(RestProducerInvocationFlow.class); + + private static final ProduceProcessor DEFAULT_PRODUCE_PROCESSOR = ProduceProcessorManager.INSTANCE + .findDefaultProcessor(); + + public RestProducerInvocationFlow(InvocationCreator invocationCreator, + HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { + super(invocationCreator, requestEx, responseEx); + } + + @Override + protected Invocation sendCreateInvocationException(Throwable throwable) { + try { + Response response = Exceptions.toProducerResponse(null, throwable); + 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); + } + + endResponse(null); + return null; + } + + @Override + protected void endResponse(Invocation invocation, Response response) { + invocation.getInvocationStageTrace().startProviderSendResponse(); + + endResponse(invocation); + } + + private void endResponse(Invocation invocation) { + try { + responseEx.endResponse(); + } catch (Throwable flushException) { + LOGGER.error("Failed to flush rest response, operation:{}, request uri:{}", + 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:{}", + 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 new file mode 100644 index 00000000000..83f14c64eca --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.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.common.rest; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; +import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; + +import io.vertx.ext.web.RoutingContext; + +public class RestVertxProducerInvocationCreator extends RestProducerInvocationCreator { + private final RoutingContext routingContext; + + public RestVertxProducerInvocationCreator(RoutingContext routingContext, + MicroserviceMeta microserviceMeta, Endpoint endpoint, + 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); + 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 new file mode 100644 index 00000000000..ff564234124 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/UploadConfig.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; + +import org.springframework.core.env.Environment; + +import jakarta.servlet.MultipartConfigElement; + +public class UploadConfig { + private final Environment environment; + + public UploadConfig(Environment environment) { + this.environment = environment; + } + + /** + * null means not support upload + */ + public String getLocation() { + return environment.getProperty(RestConst.UPLOAD_DIR, RestConst.UPLOAD_DEFAULT_DIR); + } + + /** + * limit of one upload file, only available for servlet rest transport + */ + public long getMaxFileSize() { + return environment.getProperty(RestConst.UPLOAD_MAX_FILE_SIZE, long.class, -1L); + } + + /** + * limit of upload request body + */ + public long getMaxSize() { + return environment.getProperty(RestConst.UPLOAD_MAX_SIZE, long.class, -1L); + } + + + /** + * the size threshold after which files will be written to disk, only available for servlet rest transport + */ + public int getFileSizeThreshold() { + return environment.getProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, int.class, 0); + } + + public MultipartConfigElement toMultipartConfigElement() { + return new MultipartConfigElement( + 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 new file mode 100644 index 00000000000..55576022edd --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/VertxHttpTransportContext.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.common.rest; + +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; +import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; +import org.apache.servicecomb.swagger.invocation.context.VertxTransportContext; + +import io.vertx.core.Context; +import io.vertx.core.Vertx; +import io.vertx.ext.web.RoutingContext; + +public class VertxHttpTransportContext extends HttpTransportContext implements VertxTransportContext { + private final RoutingContext routingContext; + + private final Context vertxContext; + + public VertxHttpTransportContext(RoutingContext routingContext, HttpServletRequestEx requestEx, + HttpServletResponseEx responseEx) { + super(requestEx, responseEx); + + this.routingContext = routingContext; + this.vertxContext = Vertx.currentContext(); + } + + public RoutingContext getRoutingContext() { + return routingContext; + } + + @Override + public Context getVertxContext() { + return vertxContext; + } +} 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 new file mode 100644 index 00000000000..74967694a90 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.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.common.rest.codec; + +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的能力 + * 在写cookie参数时,没办法多次添加cookie,所以只能进行接口包装 + */ +public interface RestClientRequest { + void write(Buffer bodyBuffer); + + Future end(); + + void addCookie(String name, String value); + + void putHeader(String name, String value); + + MultiMap getHeaders(); + + void addForm(String name, Object value); + + 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 new file mode 100644 index 00000000000..260ac0d4cb1 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestCodec.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +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 jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Response.Status; + +public final class RestCodec { + private RestCodec() { + } + + public static void argsToRest(Map args, RestOperationMeta restOperation, + RestClientRequest clientRequest) throws Exception { + int paramSize = restOperation.getParamList().size(); + if (paramSize == 0) { + return; + } + + for (int idx = 0; idx < paramSize; idx++) { + RestParam param = restOperation.getParamList().get(idx); + param.getParamProcessor().setValue(clientRequest, args.get(param.getParamName())); + } + } + + public static Map restToArgs(HttpServletRequest request, + RestOperationMeta restOperation) throws InvocationException { + List paramList = restOperation.getParamList(); + + Map paramValues = new HashMap<>(); + for (RestParam param : paramList) { + try { + paramValues.put(param.getParamName(), param.getParamProcessor().getValue(request)); + } catch (Exception e) { + String message = String + .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(), + e.getMessage()); + throw new InvocationException(Status.BAD_REQUEST, new CommonExceptionData(message), e); + } + } + + return paramValues; + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestObjectMapperFactory.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestObjectMapperFactory.java new file mode 100644 index 00000000000..d54800a07cb --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestObjectMapperFactory.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.common.rest.codec; + +import java.util.List; + +import org.apache.servicecomb.foundation.common.utils.AbstractRestObjectMapper; +import org.apache.servicecomb.foundation.common.utils.RestObjectMapper; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; + +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Manage RestObjectMapper instances. Give users an option to specify custom mappers. + */ +public class RestObjectMapperFactory { + private static AbstractRestObjectMapper defaultMapper = new RestObjectMapper(); + + private static final AbstractRestObjectMapper viewMapper = new RestObjectMapper(); + + private static AbstractRestObjectMapper consumerWriterMapper = defaultMapper; + + static { + registerModules(defaultMapper); + registerModules(viewMapper); + } + + private static void registerModules(ObjectMapper mapper) { + // not use mapper.findAndRegisterModules() + // because we need to sort modules, so that customers can override our default module + List modules = SPIServiceUtils.getOrLoadSortedService(Module.class); + mapper.registerModules(modules.toArray(new Module[0])); + } + + public static AbstractRestObjectMapper getConsumerWriterMapper() { + return consumerWriterMapper; + } + + public static AbstractRestObjectMapper getRestObjectMapper() { + return defaultMapper; + } + + public static AbstractRestObjectMapper getRestViewMapper() { + return viewMapper; + } + + public static void setConsumerWriterMapper(AbstractRestObjectMapper customMapper) { + registerModules(customMapper); + consumerWriterMapper = customMapper; + } + + public static void setDefaultRestObjectMapper(AbstractRestObjectMapper customMapper) { + registerModules(customMapper); + defaultMapper = customMapper; + } +} 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/AbstractParamProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/AbstractParamProcessor.java new file mode 100644 index 00000000000..2513388db4a --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/AbstractParamProcessor.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.common.rest.codec.param; + +import com.fasterxml.jackson.databind.JavaType; +import com.google.common.base.Defaults; + +public abstract class AbstractParamProcessor implements ParamValueProcessor { + protected String paramPath; + + // for consumer, targetType should be null + protected JavaType targetType; + + protected Object defaultValue; + + protected boolean required = false; + + public Object getDefaultValue() { + return defaultValue; + } + + public AbstractParamProcessor(String paramPath, JavaType targetType, Object defaultValue, boolean required) { + this.paramPath = paramPath; + this.targetType = targetType; + this.defaultValue = defaultValue; + this.required = required; + if (defaultValue == null && + targetType != null && targetType.getRawClass().isPrimitive()) { + this.defaultValue = Defaults.defaultValue(targetType.getRawClass()); + } + } + + @Override + public String getParameterPath() { + return paramPath; + } + + public JavaType getTargetType() { + return targetType; + } + + public boolean isRequired() { + return required; + } +} 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 new file mode 100644 index 00000000000..e789038c467 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java @@ -0,0 +1,414 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +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.generator.SwaggerConst; +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.ObjectReader; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.fasterxml.jackson.databind.type.SimpleType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +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 { + + 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); + + 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; + + protected boolean isRequired; + + protected OpenAPI openAPI; + + 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((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.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 + public Class getSerialViewClass() { + return serialViewClass; + } + + @Override + public Object getValue(HttpServletRequest request) throws Exception { + Object result = getValueImpl(request); + if (result == null && this.isRequired) { + throw new InvocationException(Status.BAD_REQUEST, "Body parameter is required."); + } + return result; + } + + private Object getValueImpl(HttpServletRequest request) throws IOException { + Object body = request.getAttribute(RestConst.BODY_PARAMETER); + if (body != null) { + return convertValue(body, targetType); + } + + // edge support convert from form-data or x-www-form-urlencoded to json automatically + 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); + } + + // for standard HttpServletRequest, getInputStream will never return null + // but for mocked HttpServletRequest, maybe get a null + // like org.apache.servicecomb.provider.springmvc.reference.ClientToHttpServletRequest + InputStream inputStream = request.getInputStream(); + if (inputStream == null) { + return null; + } + + 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(); + } + + 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."); + } + if (supportedContentTypes.contains(clientEncodingDefault())) { + return clientEncodingDefault(); + } + return supportedContentTypes.get(0); + } + ContentType contentType = ContentType.parse(type); + return contentType.getMimeType(); + } + + @Override + public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { + 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(contentType, arg); + clientRequest.write(buffer); + } + } + + /** + * Serialize body object into body buffer, according to the Content-Type. + */ + private Buffer createBodyBuffer(String contentType, Object arg) throws IOException { + if (MediaType.APPLICATION_JSON.equals(contentType)) { + 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)); + } + + // 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(); + } + } + + @Override + public String getParameterPath() { + return ""; + } + + @Override + public String getProcessorType() { + return PARAM_TYPE; + } + } + + 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 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 + public Object getValue(HttpServletRequest request) throws Exception { + Object body = request.getAttribute(RestConst.BODY_PARAMETER); + if (body != null) { + return convertValue(body, targetType); + } + + InputStream inputStream = request.getInputStream(); + if (inputStream == null) { + return null; + } + + return IOUtils.toString(inputStream, StandardCharsets.UTF_8); + } + + @Override + public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { + if (arg instanceof String) { + clientRequest.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); + clientRequest.write(Buffer.buffer((String) arg)); + return; + } + + throw new IllegalArgumentException("@RawJsonRequestBody only supports string type."); + } + + @Override + public String getParameterPath() { + return ""; + } + + @Override + public String getProcessorType() { + return PARAM_TYPE; + } + } + + public BodyProcessorCreator() { + ParamValueProcessorCreatorManager.INSTANCE.register(PARAM_TYPE, this); + } + + @Override + 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.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; + } + + 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 new file mode 100644 index 00000000000..9426cc2e232 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/CookieProcessorCreator.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.common.rest.codec.param; + +import java.lang.reflect.Type; +import java.util.Objects; + +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.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 static final String PARAMTYPE = "cookie"; + + public static class CookieProcessor extends AbstractParamProcessor { + public CookieProcessor(String paramPath, JavaType targetType, Object defaultValue, boolean required) { + super(paramPath, targetType, defaultValue, required); + } + + @Override + public Object getValue(HttpServletRequest request) { + Cookie[] cookies = request.getCookies(); + Object value = null; + if (cookies == null || cookies.length == 0) { + value = checkRequiredAndDefaultValue(); + return convertValue(value, targetType); + } + + for (Cookie cookie : cookies) { + if (Objects.equals(paramPath, cookie.getName())) { + value = cookie.getValue(); + break; + } + } + if (value == null) { + value = checkRequiredAndDefaultValue(); + } + return convertValue(value, targetType); + } + + private Object checkRequiredAndDefaultValue() { + if (isRequired()) { + throw new InvocationException(Status.BAD_REQUEST, "Parameter is required."); + } + return getDefaultValue(); + } + + @Override + public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { + clientRequest.addCookie(paramPath, + RestObjectMapperFactory.getConsumerWriterMapper().convertToString(arg)); + } + + @Override + public String getProcessorType() { + return PARAMTYPE; + } + } + + public CookieProcessorCreator() { + ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); + } + + @Override + public ParamValueProcessor create(OperationMeta operationMeta, + String parameterName, Parameter parameter, Type genericParamType) { + JavaType targetType = + genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); + 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 new file mode 100644 index 00000000000..740f57e039f --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.reflect.Type; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +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 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; + +@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(String paraName, RequestBody formParameter, String mediaType, JavaType targetType) { + super(paraName, targetType, + formParameter.getContent().get(mediaType).getSchema().getDefault(), + formParameter.getRequired() != null && formParameter.getRequired()); + + this.repeatedType = formParameter.getContent().get(mediaType) + .getSchema().getProperties().get(paraName) instanceof ArraySchema; + } + + @Override + public Object getValue(HttpServletRequest request) { + Map forms = (Map) request.getAttribute(RestConst.BODY_PARAMETER); + if (forms != null && !forms.isEmpty()) { + return convertValue(forms.get(paramPath), targetType); + } + + if (repeatedType) { + //Even if the paramPath does not exist, it won't be null at now + return convertValue(request.getParameterValues(paramPath), targetType); + } + + Object value = request.getParameter(paramPath); + if (value == null) { + value = checkRequiredAndDefaultValue(); + } + return convertValue(value, targetType); + } + + private Object checkRequiredAndDefaultValue() { + if (isRequired()) { + throw new InvocationException(Status.BAD_REQUEST, "Parameter is required."); + } + return getDefaultValue(); + } + + @Override + public void setValue(RestClientRequest clientRequest, Object arg) { + clientRequest.addForm(paramPath, arg); + } + + @Override + public String getProcessorType() { + return PARAMTYPE; + } + } + + public FormProcessorCreator() { + ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); + } + + @Override + public ParamValueProcessor create(OperationMeta operationMeta, + String paramName, RequestBody parameter, Type genericParamType) { + JavaType targetType = + genericParamType == null ? null : TypeFactory.defaultInstance().constructType(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(paramName, parameter, mediaType, targetType); + } + + 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 false; + } + + public static class PartProcessor extends AbstractParamProcessor { + private static final Type partListType = ParameterizedTypeUtil.make(List.class, Part.class); + + // key is target type + private static final Map partsToTargetConverters = SPIServiceUtils.getSortedService( + Converter.class) + .stream() + .filter(c -> partListType.equals(c.getSrcType())) + .collect(Collectors.toMap(Converter::getTargetType, Function.identity())); + + // key is target type + private static final Map partToTargetConverters = SPIServiceUtils.getSortedService(Converter.class) + .stream() + .filter(c -> c.getSrcType() instanceof Class && Part.class.isAssignableFrom((Class) c.getSrcType())) + .collect(Collectors.toMap(Converter::getTargetType, Function.identity())); + + private final boolean repeatedType; + + private Converter converter; + + 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.repeatedType = formParameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE) + .getSchema().getProperties().get(paramName) instanceof ArraySchema; + initConverter(genericParamType); + } + + private void initConverter(Type genericParamType) { + if (repeatedType) { + initRepeatedConverter(genericParamType); + return; + } + + initNormalConverter(genericParamType); + } + + private void initNormalConverter(Type genericParamType) { + if (genericParamType instanceof JavaType) { + genericParamType = ((JavaType) genericParamType).getRawClass(); + } + converter = partToTargetConverters.get(genericParamType); + } + + private void initRepeatedConverter(Type genericParamType) { + if (genericParamType instanceof JavaType) { + genericParamType = ParameterizedTypeUtil.make(((JavaType) genericParamType).getRawClass(), + ((JavaType) genericParamType).getContentType()); + } + converter = partsToTargetConverters.get(genericParamType); + } + + @Override + public Object getValue(HttpServletRequest request) throws Exception { + if (repeatedType) { + // get all parts + List parts = request.getParts() + .stream() + .filter(part -> part.getName().equals(paramPath)) + .collect(Collectors.toList()); + return convertValue(converter, parts); + } + + return convertValue(converter, request.getPart(paramPath)); + } + + public Object convertValue(Converter converter, Object value) { + if (value == null || converter == null) { + return value; + } + + return converter.convert(value); + } + + @Override + public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { + clientRequest.attach(paramPath, arg); + } + + @Override + public String getProcessorType() { + return PARAMTYPE; + } + } +} 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 new file mode 100644 index 00000000000..3bfb2675046 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.reflect.Type; + +import org.apache.servicecomb.common.rest.codec.RestClientRequest; +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 com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +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 = LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.parameter.header.ignoreRequiredCheck", false); + + private final HeaderCodec headerCodec; + + public HeaderProcessor(HeaderParameter headerParameter, JavaType targetType) { + 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) { + return headerCodec.decode(this, request); + } + + public Object checkRequiredAndDefaultValue() { + if (!ignoreRequiredCheck && isRequired()) { + throw new InvocationException(Status.BAD_REQUEST, "Parameter is required."); + } + return getDefaultValue(); + } + + @Override + public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { + headerCodec.encode(clientRequest, paramPath, arg); + } + + @Override + public String getProcessorType() { + return PARAMTYPE; + } + } + + public HeaderProcessorCreator() { + ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); + } + + @Override + 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 new file mode 100644 index 00000000000..5d5dfc32cf0 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessor.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.param; + +import jakarta.servlet.http.HttpServletRequest; + +import org.apache.servicecomb.common.rest.codec.RestClientRequest; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; + +import com.fasterxml.jackson.databind.JavaType; + +public interface ParamValueProcessor { + Object getValue(HttpServletRequest request) throws Exception; + + void setValue(RestClientRequest clientRequest, Object arg) throws Exception; + + default Object convertValue(Object value, JavaType targetType) { + if (value == null || targetType == null) { + return value; + } + if (getSerialViewClass() != null) { + return RestObjectMapperFactory.getRestViewMapper().setConfig( + RestObjectMapperFactory.getRestViewMapper().getDeserializationConfig().withView(getSerialViewClass())) + .convertValue(value, targetType); + } + return RestObjectMapperFactory.getRestObjectMapper() + .convertValue(value, targetType); + } + + String getParameterPath(); + + String getProcessorType(); + + default Class getSerialViewClass() { + return null; + } +} 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 new file mode 100644 index 00000000000..1c3378dc6bb --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessorCreator.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.common.rest.codec.param; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.core.definition.OperationMeta; + +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/ParamValueProcessorCreatorManager.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessorCreatorManager.java new file mode 100644 index 00000000000..b46ead4c407 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/ParamValueProcessorCreatorManager.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.common.rest.codec.param; + +import org.apache.servicecomb.foundation.common.RegisterManager; + +public final class ParamValueProcessorCreatorManager extends RegisterManager { + private static final String NAME = "param value processor mgr"; + + public static final ParamValueProcessorCreatorManager INSTANCE = new ParamValueProcessorCreatorManager(); + + static { + new PathProcessorCreator(); + new QueryProcessorCreator(); + new FormProcessorCreator(); + new HeaderProcessorCreator(); + new CookieProcessorCreator(); + new BodyProcessorCreator(); + } + + private ParamValueProcessorCreatorManager() { + super(NAME); + } +} 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 new file mode 100644 index 00000000000..861b7490fea --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/PathProcessorCreator.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.common.rest.codec.param; + +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +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.v3.oas.models.parameters.Parameter; +import jakarta.servlet.http.HttpServletRequest; + +public class PathProcessorCreator implements ParamValueProcessorCreator { + public static final String PARAMTYPE = "path"; + + public static class PathProcessor extends AbstractParamProcessor { + public PathProcessor(String paramPath, JavaType targetType, Object defaultValue, boolean required) { + super(paramPath, targetType, defaultValue, required); + } + + @Override + public Object getValue(HttpServletRequest request) { + @SuppressWarnings("unchecked") + Map pathVarMap = (Map) request.getAttribute(RestConst.PATH_PARAMETERS); + if (pathVarMap == null) { + return null; + } + + String value = pathVarMap.get(paramPath); + if (value == null) { + return null; + } + return convertValue(StringUtils.uriDecode(value, StandardCharsets.UTF_8), targetType); + } + + @Override + public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { + // path不需要set + } + + @Override + public String getProcessorType() { + return PARAMTYPE; + } + } + + public PathProcessorCreator() { + ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); + } + + @Override + public ParamValueProcessor create(OperationMeta operationMeta, + String parameterName, Parameter parameter, Type genericParamType) { + JavaType targetType = + genericParamType == null ? null : TypeFactory.defaultInstance().constructType(genericParamType); + 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 new file mode 100644 index 00000000000..8fef056867c --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/QueryProcessorCreator.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.reflect.Type; +import java.util.Map; + +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 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; + +@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 = 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 = 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 = 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.getSchema().getDefault(), + queryParameter.getRequired() != null && queryParameter.getRequired()); + + this.repeatedType = queryParameter.getSchema() instanceof ArraySchema; + this.queryCodec = QueryCodecsUtils.find(queryParameter.getStyle(), queryParameter.getExplode()); + } + + @Override + public Object getValue(HttpServletRequest request) { + return queryCodec.decode(this, 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 && "".equals(value)) { + value = null; + } + + return value != null ? value : checkRequiredAndDefaultValue(); + } + + private Object checkRequiredAndDefaultValue() { + if (!ignoreRequiredCheck && isRequired()) { + throw new InvocationException(Status.BAD_REQUEST, + String.format("Parameter %s is required.", paramPath)); + } + Object defaultValue = getDefaultValue(); + if (!ignoreDefaultValue && defaultValue != null) { + return defaultValue; + } + + return null; + } + + @Override + public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { + // query不需要set + } + + @Override + public String getProcessorType() { + return PARAMTYPE; + } + + public QueryCodec getQueryCodec() { + return queryCodec; + } + + public boolean isRepeatedType() { + return repeatedType; + } + + public Object convertValue(Object value) { + return convertValue(value, targetType); + } + } + + public QueryProcessorCreator() { + ParamValueProcessorCreatorManager.INSTANCE.register(PARAMTYPE, this); + } + + @Override + 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/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 new file mode 100644 index 00000000000..e14451389e5 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceJsonProcessor.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.common.rest.codec.produce; + +import java.io.InputStream; +import java.io.OutputStream; + +import jakarta.ws.rs.core.MediaType; + +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; + +import com.fasterxml.jackson.databind.JavaType; + +public class ProduceJsonProcessor implements ProduceProcessor { + + private Class serializationView; + + @Override + public String getSerializationView() { + return serializationView == null ? ProduceProcessor.super.getSerializationView() + : serializationView.getName(); + } + + @Override + public void setSerializationView(Class serializationView) { + if (serializationView == null) { + return; + } + this.serializationView = serializationView; + } + + @Override + public String getName() { + return MediaType.APPLICATION_JSON; + } + + @Override + public void doEncodeResponse(OutputStream output, Object result) throws Exception { + if (serializationView == null) { + RestObjectMapperFactory.getRestObjectMapper().writeValue(output, result); + return; + } + RestObjectMapperFactory.getRestObjectMapper().writerWithView(serializationView).writeValue(output, result); + } + + @Override + public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { + if (serializationView == null) { + return RestObjectMapperFactory.getRestObjectMapper().readValue(input, type); + } + return RestObjectMapperFactory.getRestObjectMapper().readerWithView(serializationView) + .forType(type).readValue(input); + } + + @Override + public int getOrder() { + return 0; + } +} 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 new file mode 100644 index 00000000000..574c1e1b630 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.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.common.rest.codec.produce; + +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; +import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; + +import com.fasterxml.jackson.databind.JavaType; + +import io.vertx.core.buffer.Buffer; + +public interface ProduceProcessor { + String getName(); + + int getOrder(); + + default String getSerializationView() { + return ProduceProcessorManager.DEFAULT_SERIAL_CLASS; + } + + default void setSerializationView(Class serializationView) { + // do nothing default + } + + default void encodeResponse(OutputStream output, Object result) throws Exception { + if (result == null) { + return; + } + + doEncodeResponse(output, result); + } + + void doEncodeResponse(OutputStream output, Object result) throws Exception; + + default Buffer encodeResponse(Object result) throws Exception { + if (null == result) { + return null; + } + + try (BufferOutputStream output = new BufferOutputStream()) { + doEncodeResponse(output, result); + return output.getBuffer(); + } + } + + default Object decodeResponse(InputStream input, JavaType type) throws Exception { + return doDecodeResponse(input, type); + } + + Object doDecodeResponse(InputStream input, JavaType type) throws Exception; + + default Object decodeResponse(Buffer buffer, JavaType type) throws Exception { + if (buffer.length() == 0) { + return null; + } + + 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 new file mode 100644 index 00000000000..3fdcfec135f --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.HashMap; +import java.util.List; +import java.util.Map; + +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 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); + + private static final List produceProcessor = + SPIServiceUtils.getSortedService(ProduceProcessor.class); + + private static final String NAME = "produce processor mgr"; + + public static final String DEFAULT_SERIAL_CLASS = "servicecomb_default_class"; + + public static final ProduceProcessorManager INSTANCE = new ProduceProcessorManager(); + + public static final String PARAM_DEFAULT_RESPONSE_ENCODING = "servicecomb.rest.parameter.default-response-encoding"; + + private static String defaultResponseEncoding; + + private final Map jsonProcessorMap; + + private final Map plainProcessorMap; + + private final Map defaultProcessorMap; + + private ProduceProcessorManager() { + super(NAME); + produceProcessor.forEach(processor -> { + Map prodProcessorMap = getObjMap() + .computeIfAbsent(processor.getName(), key -> new HashMap<>()); + prodProcessorMap.putIfAbsent(processor.getSerializationView(), processor); + }); + jsonProcessorMap = ensureFindValue(MediaType.APPLICATION_JSON); + plainProcessorMap = ensureFindValue(MediaType.TEXT_PLAIN); + defaultProcessorMap = jsonProcessorMap; + } + + private static ProduceProcessor cloneNewProduceProcessor(Class serialViewClass, + Map produceViewMap) { + ProduceProcessor newInstance; + try { + newInstance = produceViewMap.get(DEFAULT_SERIAL_CLASS).getClass().getDeclaredConstructor().newInstance(); + newInstance.setSerializationView(serialViewClass); + return newInstance; + } catch (Throwable e) { + // ignore exception + LOGGER.warn("Failed to create produceProcessor with {}", serialViewClass.getName(), e); + } + return produceViewMap.get(DEFAULT_SERIAL_CLASS); + } + + private static String defaultResponseEncoding() { + if (defaultResponseEncoding == null) { + defaultResponseEncoding = LegacyPropertyFactory + .getStringProperty(PARAM_DEFAULT_RESPONSE_ENCODING, MediaType.APPLICATION_JSON); + } + return defaultResponseEncoding; + } + + public ProduceProcessor findJsonProcessorByViewClass(Class serialViewClass) { + if (serialViewClass == null) { + return jsonProcessorMap.get(DEFAULT_SERIAL_CLASS); + } + return jsonProcessorMap.computeIfAbsent(serialViewClass.getName(), + viewKey -> cloneNewProduceProcessor(serialViewClass, jsonProcessorMap)); + } + + public ProduceProcessor findPlainProcessorByViewClass(Class serialViewClass) { + if (serialViewClass == null) { + return plainProcessorMap.get(DEFAULT_SERIAL_CLASS); + } + return plainProcessorMap.computeIfAbsent(serialViewClass.getName(), + viewKey -> cloneNewProduceProcessor(serialViewClass, plainProcessorMap)); + } + + public ProduceProcessor findDefaultJsonProcessor() { + return jsonProcessorMap.get(DEFAULT_SERIAL_CLASS); + } + + public ProduceProcessor findDefaultProcessor() { + return defaultProcessorMap.get(DEFAULT_SERIAL_CLASS); + } + + 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 new file mode 100644 index 00000000000..21d73688dc4 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.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.produce; + +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; + +import com.fasterxml.jackson.databind.JavaType; + +import jakarta.ws.rs.core.MediaType; + +public class ProduceTextPlainProcessor extends ProduceJsonProcessor { + @Override + public String getName() { + return MediaType.TEXT_PLAIN; + } + + @Override + public void doEncodeResponse(OutputStream output, Object result) throws Exception { + 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 { + 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 new file mode 100644 index 00000000000..0ba395bb43f --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/AbstractQueryCodec.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Arrays; +import java.util.Collection; +import java.util.Collections; + +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; + +public abstract class AbstractQueryCodec implements QueryCodec { + private final String codecName; + + public AbstractQueryCodec(String codecName) { + this.codecName = codecName; + } + + @Override + public String getCodecName() { + return codecName; + } + + @Override + @SuppressWarnings("unchecked") + 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; + } + + if (value instanceof Collection) { + encode(builder, name, (Collection) value); + return; + } + + encode(builder, name, Collections.singletonList(value)); + } + + 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 new file mode 100644 index 00000000000..b9bfd1b7130 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodec.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.common.rest.codec.query; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +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(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); + } + + @Override + default int getOrder() { + return 0; + } + + String getCodecName(); + + 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 new file mode 100644 index 00000000000..dd745c54b73 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecCsv.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.query; + +/** + * ?query=x1,x2 + */ +public class QueryCodecCsv extends QueryCodecWithDelimiter { + public static final String CODEC_NAME = "form:0"; + + public static final String DELIMITER = ","; + + public QueryCodecCsv() { + super(CODEC_NAME, DELIMITER, DELIMITER); + } +} 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 new file mode 100644 index 00000000000..4c2f4287916 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMulti.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Collection; +import java.util.Map; + +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 = "form:1"; + + public QueryCodecMulti() { + super(CODEC_NAME); + } + + @Override + public void encode(URLPathStringBuilder builder, String name, Collection values) throws Exception { + for (Object value : values) { + if (value == null) { + continue; + } + + String strValue = QueryCodec.convertToString(value); + builder.appendQuery(name, QueryCodec.encodeValue(strValue)); + } + } + + @Override + 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); + } + + Object value = processor.getAndCheckParameter(request); + return processor.convertValue(value); + } +} 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 new file mode 100644 index 00000000000..68032f004f4 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecPipes.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.common.rest.codec.query; + +/** + * ?query=x1|x2 + */ +public class QueryCodecPipes extends QueryCodecWithDelimiter { + public static final String CODEC_NAME = "pipeDelimited:0"; + + public static final String JOIN_DELIMITER = "|"; + + public static final String SPLIT_DELIMITER = "\\|"; + + public QueryCodecPipes() { + super(CODEC_NAME, JOIN_DELIMITER, SPLIT_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 new file mode 100644 index 00000000000..514e931d3a0 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecSsv.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.query; + +/** + * ?query=x1%20x2 + */ +public class QueryCodecSsv extends QueryCodecWithDelimiter { + public static final String CODEC_NAME = "spaceDelimited:0"; + + public static final String DELIMITER = " "; + + public QueryCodecSsv() { + 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 new file mode 100644 index 00000000000..f3452bf371e --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecWithDelimiter.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.common.rest.codec.query; + +import java.util.Collection; +import java.util.StringJoiner; + +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:
+ * 1. a
+ * 2. b,c
+ * will encode to be a%2Cb%2Cc, this is ambiguous + */ +public class QueryCodecWithDelimiter extends AbstractQueryCodec { + private final String joinDelimiter; + + private final String splitDelimiter; + + public QueryCodecWithDelimiter(String codecName, String joinDelimiter, String splitDelimiter) { + super(codecName); + this.joinDelimiter = joinDelimiter; + this.splitDelimiter = splitDelimiter; + } + + @Override + public void encode(URLPathStringBuilder builder, String name, Collection values) throws Exception { + String joined = join(values); + if (joined == null) { + return; + } + + builder.appendQuery(name, joined); + } + + /** + *
+   *   SwaggerIde:
+   *   1. encode query value by uri rule, not url rule
+   *      part of the difference:
+   *               uri  url
+   *        space  %20  +
+   *        [      [    %5B
+   *      some difference will cause tomcat parse url failed
+   *      so we encode query value by url rule
+   *   2. encode each element
+   *        for pipes, SwaggerIde will encode [a, b] to a|b
+   *        but this will cause problem when run with tomcat
+   *        so we encode the joined value, not encode each element
+   * 
+ * @param values values to be joined + * @return joined value + */ + protected String join(Collection values) throws Exception { + StringJoiner joiner = new StringJoiner(joinDelimiter); + boolean hasValue = false; + for (Object value : values) { + if (value != null) { + String strValue = QueryCodec.convertToString(value); + joiner.add(strValue); + hasValue = true; + } + } + + return hasValue ? QueryCodec.encodeValue(joiner.toString()) : null; + } + + @Override + public Object decode(QueryProcessor processor, HttpServletRequest request) { + Object value = processor.getAndCheckParameter(request); + value = value != null ? value.toString().split(splitDelimiter, -1) : new String[0]; + return processor.convertValue(value); + } +} 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 new file mode 100644 index 00000000000..41c5098ef52 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecs.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.common.rest.codec.query; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class QueryCodecs { + private static final Logger LOGGER = LoggerFactory.getLogger(QueryCodecs.class); + + static QueryCodecs createForTest() { + return new QueryCodecs(Arrays.asList( + new QueryCodecMulti(), + new QueryCodecCsv(), + new QueryCodecSsv(), + new QueryCodecPipes() + )); + } + + private final Map codecs = new HashMap<>(); + + private final QueryCodec defaultCodec; + + public QueryCodecs(List orderedCodecs) { + orderedCodecs.forEach(this::register); + defaultCodec = codecs.get(QueryCodecMulti.CODEC_NAME); + } + + private void register(QueryCodec codec) { + QueryCodec exists = codecs.put(codec.getCodecName(), codec); + if (exists != null) { + LOGGER.info("override QueryCodec, exists={}, new={}.", + exists.getClass().getName(), codec.getClass().getName()); + } + } + + public QueryCodec find(String name) { + if (name == null) { + return defaultCodec; + } + + QueryCodec codec = codecs.get(name); + if (codec == null) { + throw new IllegalStateException("not support QueryCodec, name=" + name); + } + return codec; + } +} 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 new file mode 100644 index 00000000000..4b54210c7a0 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecsUtils.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.common.rest.codec.query; + +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 + private static QueryCodecs queryCodecs = QueryCodecs.createForTest(); + + public QueryCodecsUtils(QueryCodecs queryCodecs) { + QueryCodecsUtils.queryCodecs = queryCodecs; + } + + 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/RestMetaUtils.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestMetaUtils.java new file mode 100644 index 00000000000..3626d1e59f0 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestMetaUtils.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.common.rest.definition; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.core.definition.OperationMeta; + +public final class RestMetaUtils { + private RestMetaUtils() { + } + + public static RestOperationMeta getRestOperationMeta(OperationMeta operationMeta) { + return operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationComparator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationComparator.java new file mode 100644 index 00000000000..ee3ead460d7 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationComparator.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.common.rest.definition; + +import java.io.Serializable; +import java.util.Comparator; + +import org.apache.servicecomb.common.rest.definition.path.PathRegExp; + +/** + * 用于RestOperation的排序 + */ +public class RestOperationComparator implements Serializable, Comparator { + + private static final long serialVersionUID = -2364909265520813678L; + + @Override + public int compare(RestOperationMeta r1, RestOperationMeta r2) { + // 排序规则: + // 1.静态字符多的优先 + // 2.变量组多的优先 + // 3.带正则表达式的优先 + // 如: + // /customers/{id}/{name}/address + // /customers/{id : .+}/address + // /customers/{id}/address + // /customers/{id : .+} + + PathRegExp path1 = r1.getAbsolutePathRegExp(); + PathRegExp path2 = r2.getAbsolutePathRegExp(); + + int staticCompare = path2.getStaticCharCount() - path1.getStaticCharCount(); + if (staticCompare != 0) { + return staticCompare; + } + + int groupCompare = path2.getGroupCount() - path1.getGroupCount(); + if (groupCompare != 0) { + return groupCompare; + } + + return path2.getGroupWithRegExpCount() - path1.getGroupWithRegExpCount(); + } +} 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 new file mode 100644 index 00000000000..580175ec680 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.definition; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.common.rest.codec.param.FormProcessorCreator.PartProcessor; +import org.apache.servicecomb.common.rest.definition.path.PathRegExp; +import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder; +import org.apache.servicecomb.common.rest.locator.OperationLocator; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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 boolean formData; + + // make sure if response is file + protected boolean downloadFile; + + protected boolean serverSendEvents; + + protected List paramList = new ArrayList<>(); + + // key为参数名 + protected Map paramMap = new LinkedHashMap<>(); + + protected List fileKeys = new ArrayList<>(); + + protected String absolutePath; + + protected PathRegExp absolutePathRegExp; + + // 快速构建URL path + private URLPathBuilder pathBuilder; + + public void init(OperationMeta operationMeta) { + this.operationMeta = operationMeta; + + OpenAPI swagger = operationMeta.getSchemaMeta().getSwagger(); + Operation operation = operationMeta.getSwaggerOperation(); + + this.downloadFile = checkDownloadFileFlag(); + 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 (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); + } + } + + setAbsolutePath(SwaggerUtils.concatAbsolutePath(swagger, operationMeta.getOperationPath())); + } + + 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 operation.getRequestBody().getContent().get(SwaggerConst.FILE_MEDIA_TYPE).getSchema(); + } + + public boolean isDownloadFile() { + return downloadFile; + } + + 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; + } + + public boolean isFormData() { + return formData; + } + + public void setOperationMeta(OperationMeta operationMeta) { + this.operationMeta = operationMeta; + } + + public String getAbsolutePath() { + return this.absolutePath; + } + + public void setAbsolutePath(String absolutePath) { + this.absolutePath = absolutePath; + this.absolutePathRegExp = createPathRegExp(absolutePath); + this.pathBuilder = new URLPathBuilder(absolutePath, paramMap); + } + + public PathRegExp getAbsolutePathRegExp() { + return this.absolutePathRegExp; + } + + public boolean isAbsoluteStaticPath() { + return this.absolutePathRegExp.isStaticPath(); + } + + protected PathRegExp createPathRegExp(String path) { + if (path == null || path.equals("")) { + throw new Error("null rest url is not supported"); + } + try { + return new PathRegExp(OperationLocator.getStandardPath(path)); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + return null; + } + } + + public RestParam getParamByName(String name) { + return paramMap.get(name); + } + + public OperationMeta getOperationMeta() { + return operationMeta; + } + + public URLPathBuilder getPathBuilder() { + return this.pathBuilder; + } + + public List getParamList() { + return paramList; + } + + private void addParam(RestParam param) { + if (param.getParamProcessor() instanceof PartProcessor) { + fileKeys.add(param.getParamName()); + } + paramList.add(param); + paramMap.put(param.getParamName(), param); + } + + public String getHttpMethod() { + return operationMeta.getHttpMethod(); + } + + public List getFileKeys() { + return fileKeys; + } +} 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 new file mode 100644 index 00000000000..a2b44674c6f --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestParam.java @@ -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. + */ + +package org.apache.servicecomb.common.rest.definition; + +import java.lang.reflect.Type; +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.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); + + protected ParamValueProcessor paramProcessor; + + protected String paramName; + + public RestParam(OperationMeta operationMeta, Parameter parameter, Type genericParamType) { + this.paramName = parameter.getName(); + + 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; + } + + public void setParamProcessor(ParamValueProcessor paramProcessor) { + this.paramProcessor = paramProcessor; + } + + public String getParamName() { + return paramName; + } + + protected void init(OperationMeta operationMeta, Parameter parameter, Type genericParamType) { + String paramType = parameter.getIn(); + ParamValueProcessorCreator creator = + ParamValueProcessorCreatorManager.INSTANCE.ensureFindValue(paramType); + + 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)); + } + + public T getValue(Map args) { + return (T) args.get(paramName); + } + + public String[] getValueAsStrings(Map args) { + Object value = args.get(paramName); + if (value == null) { + return null; + } + + if (value.getClass().isArray() || value instanceof Collection) { + return (String[]) paramProcessor.convertValue(value, STRING_ARRAY_TYPE); + } + + return new String[] {String.valueOf(value)}; + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/AbstractUrlParamWriter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/AbstractUrlParamWriter.java new file mode 100644 index 00000000000..d4eca87d86c --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/AbstractUrlParamWriter.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.common.rest.definition.path; + +import java.util.Map; + +import com.google.common.annotations.VisibleForTesting; +import org.apache.servicecomb.common.rest.definition.RestParam; + +public abstract class AbstractUrlParamWriter implements UrlParamWriter { + protected RestParam param; + + @VisibleForTesting + public Object getParamValue(Map args) { + if (param == null) { + // Wrong server definition + // @GetMapping(path = "/getLocalDateTime/{paramX}") + // public LocalDateTime getLocalDateTimePath(@PathParam("paramY") LocalDateTime date) { + throw new IllegalArgumentException("Path parameter name not valid in provider. Check if provider " + + "path pattern has the parameter name."); + } + return args.get(param.getParamName()); + } +} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/PathRegExp.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathRegExp.java similarity index 91% rename from common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/PathRegExp.java rename to common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathRegExp.java index 113956201bc..34f04b57b65 100644 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/definition/path/PathRegExp.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathRegExp.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +15,7 @@ * limitations under the License. */ -package io.servicecomb.common.rest.definition.path; +package org.apache.servicecomb.common.rest.definition.path; import java.util.ArrayList; import java.util.List; @@ -54,7 +55,7 @@ public class PathRegExp { protected final Pattern pattern; - protected final List varNames = new ArrayList(); + protected final List varNames = new ArrayList<>(); public static String ensureEndWithSlash(String path) { if (path.endsWith(SLASH)) { diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriter.java new file mode 100644 index 00000000000..4cd96411fad --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriter.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.common.rest.definition.path; + +import java.util.Map; + +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.common.rest.definition.RestParam; +import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; +import org.apache.servicecomb.foundation.common.http.HttpUtils; + +/** + * Dynamically processing path + */ +public class PathVarParamWriter extends AbstractUrlParamWriter { + public PathVarParamWriter(RestParam param) { + this.param = param; + } + + @Override + public void write(URLPathStringBuilder builder, Map args) throws Exception { + if (getParamValue(args) == null) { + throw new IllegalArgumentException("path parameter can not be null."); + } + String encodedPathParam = encodeNotNullValue(getParamValue(args)); + builder.appendPath(encodedPathParam); + } + + private String encodeNotNullValue(Object value) throws Exception { + String strValue = RestObjectMapperFactory.getRestObjectMapper().convertToString(value); + return HttpUtils.encodePathParam(strValue); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriter.java new file mode 100644 index 00000000000..699bc07d66b --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriter.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.common.rest.definition.path; + +import java.util.Map; + +import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; +import org.apache.servicecomb.common.rest.codec.query.QueryCodec; +import org.apache.servicecomb.common.rest.definition.RestParam; +import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; + +public class QueryVarParamWriter extends AbstractUrlParamWriter { + private final QueryCodec queryCodec; + + public QueryVarParamWriter(RestParam param) { + this.param = param; + this.queryCodec = ((QueryProcessor) param.getParamProcessor()).getQueryCodec(); + } + + @Override + public void write(URLPathStringBuilder builder, Map args) throws Exception { + Object value = getParamValue(args); + queryCodec.encode(builder, param.getParamName(), value); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/StaticUrlParamWriter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/StaticUrlParamWriter.java new file mode 100644 index 00000000000..ff9e9658f7b --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/StaticUrlParamWriter.java @@ -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. + */ + +package org.apache.servicecomb.common.rest.definition.path; + +import java.util.Map; + +import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; + +public class StaticUrlParamWriter implements UrlParamWriter { + + private final String staticPath; + + public StaticUrlParamWriter(String staticPath) { + this.staticPath = staticPath; + } + + @Override + public void write(URLPathStringBuilder builder, Map args) { + builder.appendPath(staticPath); + } +} 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 new file mode 100644 index 00000000000..a59c904ff41 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilder.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.definition.path; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator; +import org.apache.servicecomb.common.rest.definition.RestParam; + +/** + * 初始化阶段创建URLPathBuilder,用于加速调用阶段的path创建 + */ +public class URLPathBuilder { + + private final List pathParamWriterList = new ArrayList<>(); + + private final List queryParamWriterList = new ArrayList<>(); + + private static final String SLASH = "/"; + + public URLPathBuilder(String rawPath, Map paramMap) { + initPathWriterList(rawPath, paramMap); + initQueryWriterList(paramMap); + } + + private void initQueryWriterList(Map paramMap) { + for (RestParam param : paramMap.values()) { + if (!QueryProcessorCreator.PARAMTYPE.equals(param.getParamProcessor().getProcessorType())) { + continue; + } + + UrlParamWriter dynamicWriter = new QueryVarParamWriter(param); + queryParamWriterList.add(dynamicWriter); + } + } + + private void initPathWriterList(String rawPath, Map paramMap) { + // 首部加上'/' + if (!rawPath.startsWith(SLASH)) { + rawPath = SLASH + rawPath; + } + + StringBuilder tmpPath = new StringBuilder(); + for (int idx = 0; idx < rawPath.length(); idx++) { + char currentChar = rawPath.charAt(idx); + if (currentChar == '{') { + if (tmpPath.length() != 0) { + this.pathParamWriterList.add(new StaticUrlParamWriter(tmpPath.toString())); + tmpPath.setLength(0); + } + } else if (currentChar == '}') { + if (tmpPath.length() == 0) { + continue; + } + String tmpPathStr = tmpPath.toString(); + String pathParamName = tmpPathStr; + if (tmpPathStr.contains(":")) { + pathParamName = tmpPathStr.split(":", 2)[0].trim(); + } + RestParam param = paramMap.get(pathParamName); + this.pathParamWriterList.add(new PathVarParamWriter(param)); + tmpPath.setLength(0); + } else { + tmpPath.append(currentChar); + } + } + if (tmpPath.length() != 0) { + this.pathParamWriterList.add(new StaticUrlParamWriter(tmpPath.toString())); + } + } + + public String createRequestPath(Map args) throws Exception { + URLPathStringBuilder builder = new URLPathStringBuilder(); + + genPathString(builder, args); + genQueryString(builder, args); + + return builder.build(); + } + + public String createPathString(Map args) throws Exception { + URLPathStringBuilder builder = new URLPathStringBuilder(); + genPathString(builder, args); + return builder.build(); + } + + private void genPathString(URLPathStringBuilder builder, Map args) throws Exception { + for (UrlParamWriter writer : this.pathParamWriterList) { + writer.write(builder, args); + } + } + + private void genQueryString(URLPathStringBuilder builder, Map args) throws Exception { + for (UrlParamWriter writer : queryParamWriterList) { + writer.write(builder, args); + } + } + + public static class URLPathStringBuilder { + private final StringBuilder stringBuilder = new StringBuilder(); + + private boolean queryPrefixNotWrite = true; + + public URLPathStringBuilder appendPath(String s) { + stringBuilder.append(s); + return this; + } + + public URLPathStringBuilder appendQuery(String name, String encodedValue) { + if (queryPrefixNotWrite) { + stringBuilder.append('?'); + queryPrefixNotWrite = false; + } else { + stringBuilder.append('&'); + } + + stringBuilder.append(name).append("=").append(encodedValue); + return this; + } + + public String build() { + return stringBuilder.toString(); + } + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/UrlParamWriter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/UrlParamWriter.java new file mode 100644 index 00000000000..21ed73a7340 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/path/UrlParamWriter.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.common.rest.definition.path; + +import java.util.Map; + +import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; + +public interface UrlParamWriter { + void write(URLPathStringBuilder builder, Map args) throws Exception; +} 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 new file mode 100644 index 00000000000..1d21e9c8bbd --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 com.google.common.net.HttpHeaders.CONTENT_LENGTH; +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 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.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.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.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.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 static final String NAME = "rest-server-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.RESTFUL.equals(transport); + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + return CompletableFuture.completedFuture(invocation) + .thenAccept(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 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); + invocation.getInvocationStageTrace().finishProviderDecodeRequest(); + } + + protected CompletableFuture encodeResponse(Invocation invocation, Response response) { + invocation.onEncodeResponseStart(response); + HttpTransportContext transportContext = invocation.getTransportContext(); + HttpServletResponseEx responseEx = transportContext.getResponseEx(); + // TODO: response support JsonView + ProduceProcessor produceProcessor = ProduceProcessorManager.INSTANCE + .createProduceProcessor(invocation.getOperationMeta(), response.getStatusCode(), + invocation.getRequestEx().getHeader(HttpHeaders.ACCEPT), null); + + return encodeResponse(invocation, response, produceProcessor, responseEx) + .whenComplete((r, e) -> invocation.onEncodeResponseFinish()); + } + + private static boolean isFailedResponse(Response response) { + return response.getResult() instanceof InvocationException; + } + + 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); + + if (isFailedResponse(response)) { + responseEx.setContentType(produceProcessor.getName()); + return writeResponse(responseEx, produceProcessor, ((InvocationException) response.getResult()).getErrorData(), + response, true); + } + + if (isDownloadFileResponseType(invocation, response)) { + return writePart(responseEx, response.getResult(), response); + } + + 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; + } + + /** + * Check whether this response is a downloaded file response, + * according to the schema recorded in {@link org.apache.servicecomb.swagger.invocation.response.ResponsesMeta} + * and response status code. + * @return true if this response is a downloaded file, otherwise false. + */ + public static boolean isDownloadFileResponseType(Invocation invocation, Response response) { + return Part.class.isAssignableFrom( + invocation.findResponseType(response.getStatusCode()).getRawClass()); + } + + 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()); + } + } + + 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/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 new file mode 100644 index 00000000000..3df5c699403 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/MicroservicePaths.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.locator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.servicecomb.common.rest.definition.RestOperationComparator; +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MicroservicePaths { + private static final Logger LOGGER = LoggerFactory.getLogger(MicroservicePaths.class); + + // 运行阶段,静态path,一次直接查找到目标,不必遍历查找 + // 以path为key + protected Map staticPathOperations = new HashMap<>(); + + // 运行阶段,以path优先级,从高到低排列的operation列表 + protected List dynamicPathOperationsList = new ArrayList<>(); + + public void sortPath() { + RestOperationComparator comparator = new RestOperationComparator(); + this.dynamicPathOperationsList.sort(comparator); + } + + public void addResource(RestOperationMeta swaggerRestOperation) { + if (swaggerRestOperation.isAbsoluteStaticPath()) { + // 静态path + addStaticPathResource(swaggerRestOperation); + return; + } + + dynamicPathOperationsList.add(swaggerRestOperation); + } + + protected void addStaticPathResource(RestOperationMeta operation) { + String httpMethod = operation.getHttpMethod(); + String path = OperationLocator.getStandardPath(operation.getAbsolutePath()); + OperationGroup group = staticPathOperations.get(path); + if (group == null) { + group = new OperationGroup(); + group.register(httpMethod, operation); + staticPathOperations.put(path, group); + return; + } + + if (group.findValue(httpMethod) == null) { + group.register(httpMethod, operation); + return; + } + + throw new ServiceCombException( + String.format("operation with url %s, method %s is duplicated.", path, httpMethod)); + } + + public Map getStaticPathOperationMap() { + return staticPathOperations; + } + + public List getDynamicPathOperationList() { + return dynamicPathOperationsList; + } + + public void printPaths() { + for (Entry entry : staticPathOperations.entrySet()) { + OperationGroup operationGroup = entry.getValue(); + printPath(operationGroup.values()); + } + + printPath(getDynamicPathOperationList()); + } + + protected void printPath(Collection operations) { + for (RestOperationMeta operation : operations) { + SwaggerProducerOperation producerOperation = operation.getOperationMeta().getSwaggerProducerOperation(); + + LOGGER.debug("Swagger mapped \"{[{}], method=[{}]}\" onto {}", + operation.getAbsolutePath(), + operation.getHttpMethod(), + producerOperation.getProducerMethod()); + } + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationGroup.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationGroup.java new file mode 100644 index 00000000000..2e96fb33dc2 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationGroup.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.common.rest.locator; + +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +import org.apache.servicecomb.foundation.common.RegisterManager; + +/** + * 存放具有相同path,不同httpmethod的operation + */ +public class OperationGroup extends RegisterManager { + private static final String NAME = "operation group manager"; + + public OperationGroup() { + super(NAME); + } +} diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/OperationLocator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationLocator.java similarity index 76% rename from common/common-rest/src/main/java/io/servicecomb/common/rest/locator/OperationLocator.java rename to common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationLocator.java index 50572e2a31e..4b94cee9e36 100644 --- a/common/common-rest/src/main/java/io/servicecomb/common/rest/locator/OperationLocator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/OperationLocator.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,19 +15,18 @@ * limitations under the License. */ -package io.servicecomb.common.rest.locator; +package org.apache.servicecomb.common.rest.locator; import java.util.Collection; 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 io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.swagger.invocation.exception.InvocationException; +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 new file mode 100644 index 00000000000..d59d7019bc9 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/locator/ServicePathManager.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.locator; + +import java.util.Collection; + +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.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.registry.definition.DefinitionConst; + + +/** + * 对静态路径和动态路径的operation进行预先处理,加速operation的查询定位 + */ +public class ServicePathManager { + private static final String REST_PATH_MANAGER = "RestServicePathManager"; + + protected MicroserviceMeta microserviceMeta; + + // equal to swagger + protected MicroservicePaths swaggerPaths = new MicroservicePaths(); + + // we support swagger basePath is not include contextPath and urlPattern + // so for producer, we must concat contextPath and urlPattern + // only valid for microservice of this process + protected MicroservicePaths producerPaths; + + public static ServicePathManager getServicePathManager(MicroserviceMeta microserviceMeta) { + return microserviceMeta.getExtData(REST_PATH_MANAGER); + } + + public ServicePathManager(MicroserviceMeta microserviceMeta) { + this.microserviceMeta = microserviceMeta; + + for (SchemaMeta schemaMeta : microserviceMeta.getSchemaMetas().values()) { + addSchema(schemaMeta); + } + sortPath(); + + microserviceMeta.putExtData(REST_PATH_MANAGER, this); + } + + private void addSchema(SchemaMeta schemaMeta) { + for (OperationMeta operationMeta : schemaMeta.getOperations().values()) { + RestOperationMeta restOperationMeta = new RestOperationMeta(); + restOperationMeta.init(operationMeta); + operationMeta.putExtData(RestConst.SWAGGER_REST_OPERATION, restOperationMeta); + addResource(restOperationMeta); + } + } + + public OperationLocator consumerLocateOperation(String path, String httpMethod) { + String standPath = OperationLocator.getStandardPath(path); + OperationLocator locator = new OperationLocator(); + locator.locate(microserviceMeta.getMicroserviceName(), standPath, httpMethod, swaggerPaths); + + return locator; + } + + public OperationLocator producerLocateOperation(String path, String httpMethod) { + String standPath = OperationLocator.getStandardPath(path); + OperationLocator locator = new OperationLocator(); + locator.locate(microserviceMeta.getMicroserviceName(), standPath, httpMethod, producerPaths); + + return locator; + } + + public void addResource(RestOperationMeta swaggerRestOperation) { + swaggerPaths.addResource(swaggerRestOperation); + } + + public void sortPath() { + swaggerPaths.sortPath(); + } + + public void buildProducerPaths() { + String urlPrefix = ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX); + if (StringUtils.isEmpty(urlPrefix)) { + producerPaths = swaggerPaths; + producerPaths.printPaths(); + return; + } + + producerPaths = new MicroservicePaths(); + for (OperationGroup operationGroup : swaggerPaths.getStaticPathOperationMap().values()) { + addProducerPaths(urlPrefix, operationGroup.values()); + } + + addProducerPaths(urlPrefix, swaggerPaths.getDynamicPathOperationList()); + producerPaths.printPaths(); + } + + private void addProducerPaths(String urlPrefix, Collection restOperationMetas) { + for (RestOperationMeta swaggerRestOperation : restOperationMetas) { + RestOperationMeta producerRestOperation = swaggerRestOperation; + if (!swaggerRestOperation.getAbsolutePath().startsWith(urlPrefix)) { + producerRestOperation = new RestOperationMeta(); + producerRestOperation.init(swaggerRestOperation.getOperationMeta()); + producerRestOperation.setAbsolutePath(urlPrefix + swaggerRestOperation.getAbsolutePath()); + } + producerPaths.addResource(producerRestOperation); + } + } +} 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 new file mode 100644 index 00000000000..3884d85a86c --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/resource/ClassPathStaticResourceHandler.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.common.rest.resource; + +import java.io.IOException; +import java.net.URL; + +import jakarta.servlet.http.Part; + +import org.apache.servicecomb.foundation.common.part.InputStreamPart; + +public class ClassPathStaticResourceHandler extends StaticResourceHandler { + protected Part findResource(String path) throws IOException { + URL url = this.getClass().getClassLoader().getResource(path); + if (url == null) { + return null; + } + + return new InputStreamPart(null, url.openStream()).setSubmittedFileName(path); + } +} 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 new file mode 100644 index 00000000000..a01813fbd70 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/resource/StaticResourceHandler.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.common.rest.resource; + +import java.io.IOException; +import java.net.URI; + +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; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +// TODO: LRU cache for small resource in jar? +public abstract class StaticResourceHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(StaticResourceHandler.class); + + private String webRoot = "webroot/"; + + public void setWebRoot(String webRoot) { + this.webRoot = webRoot; + } + + protected abstract Part findResource(String path) throws IOException; + + public Response handle(String path) { + path = URI.create(webRoot + path).normalize().getPath(); + if (!path.startsWith(webRoot)) { + // maybe request of attack, just return 404 + return Response.failResp(new InvocationException(Status.NOT_FOUND, Status.NOT_FOUND.getReasonPhrase())); + } + + Part part; + try { + part = findResource(path); + } catch (Throwable e) { + LOGGER.error("failed to process static resource, path={}", path, e); + return Response + .failResp(new InvocationException(Status.INTERNAL_SERVER_ERROR, "failed to process static resource.")); + } + + if (part == null) { + return Response.failResp(new InvocationException(Status.NOT_FOUND, Status.NOT_FOUND.getReasonPhrase())); + } + + return handler(part); + } + + public Response handler(Part part) { + // todo: cache control + Response response = Response.ok(part); + response.setHeader(HttpHeaders.CONTENT_TYPE, part.getContentType()); + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "inline"); + return response; + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationItem.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationItem.java new file mode 100644 index 00000000000..d884e49e453 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationItem.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.common.rest.route; + +import java.util.regex.Pattern; + +public class URLMappedConfigurationItem { + private String microserviceName; + + private String versionRule; + + private int prefixSegmentCount; + + private Pattern pattern; + + private String stringPattern; + + public String getMicroserviceName() { + return microserviceName; + } + + public void setMicroserviceName(String microserviceName) { + this.microserviceName = microserviceName; + } + + public String getVersionRule() { + return versionRule; + } + + public void setVersionRule(String versionRule) { + this.versionRule = versionRule; + } + + public int getPrefixSegmentCount() { + return prefixSegmentCount; + } + + public void setPrefixSegmentCount(int prefixSegmentCount) { + this.prefixSegmentCount = prefixSegmentCount; + } + + public Pattern getPattern() { + return pattern; + } + + public void setPattern(Pattern pattern) { + this.pattern = pattern; + } + + public String getStringPattern() { + return stringPattern; + } + + public void setStringPattern(String stringPattern) { + this.stringPattern = stringPattern; + } +} 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/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/Utils.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/Utils.java new file mode 100644 index 00000000000..0e1f64291c9 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/Utils.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.route; + +/** + * Commonly used methods in this package. + */ +public final class Utils { + private Utils() { + + } + + /** + * Get the actual path without prefix + * @param path full path + * @param pathIndex the index of / that after prefix + * @return actual path + */ + public static String findActualPath(String path, int pathIndex) { + if (pathIndex <= 0) { + return path; + } + + int fromIndex = 0; + int counter = pathIndex; + char[] chars = path.toCharArray(); + for (int i = 0; i < chars.length; i++) { + if (chars[i] == '/') { + if (--counter < 0) { + fromIndex = i; + break; + } + } + } + return fromIndex > 0 ? path.substring(fromIndex) : ""; + } +} 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 new file mode 100644 index 00000000000..65a6c38a587 --- /dev/null +++ b/common/common-rest/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module @@ -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.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 new file mode 100644 index 00000000000..c13dc4dac85 --- /dev/null +++ b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor @@ -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.common.rest.codec.produce.ProduceJsonProcessor +org.apache.servicecomb.common.rest.codec.produce.ProduceTextPlainProcessor 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 new file mode 100644 index 00000000000..94be435a96b --- /dev/null +++ b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider @@ -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.filter.RestFilterProvider 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/io/servicecomb/common/rest/TestAbstractRestServer.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/TestAbstractRestServer.java deleted file mode 100644 index bdea29677f9..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/TestAbstractRestServer.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest; - -import static org.mockito.Mockito.when; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.codec.RestServerRequestInternal; -import io.servicecomb.common.rest.codec.produce.ProduceProcessor; -import io.servicecomb.core.Const; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.Transport; -import io.servicecomb.foundation.common.utils.JsonUtils; -import io.servicecomb.swagger.invocation.Response; - -public class TestAbstractRestServer { - private Invocation invocation; - - private RestServerRequestInternal restRequest; - - private AbstractRestServer restServer; - - @Before - public void before() throws Exception { - invocation = Mockito.mock(Invocation.class); - restRequest = Mockito.mock(RestServerRequestInternal.class); - restServer = new AbstractRestServer() { - @Override - protected void doSendResponse(Response httpServerResponse, ProduceProcessor produceProcessor, - Response response) throws Exception { - } - - @Override - protected void setHttpRequestContext(Invocation invocation, RestServerRequestInternal restRequest) { - } - }; - restServer.setTransport(Mockito.mock(Transport.class)); - } - - @After - public void after() { - - invocation = null; - restRequest = null; - restServer = null; - } - - @Test - public void testSetContext() throws Exception { - boolean status = true; - try { - Map contextMap = new HashMap<>(); - contextMap.put("abc", "abc"); - String strContext = JsonUtils.writeValueAsString(contextMap); - when(restRequest.getHeaderParam(Const.CSE_CONTEXT)).thenReturn(strContext); - restServer.setContext(invocation, restRequest); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testFindRestOperationException() { - boolean status = true; - try { - when(restRequest.getPath()).thenReturn("/a/b/c"); - when(restRequest.getMethod()).thenReturn("POST"); - restServer.findRestOperation(restRequest); - } catch (Throwable e) { - status = false; - } - Assert.assertFalse(status); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/TestDefPath.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/TestDefPath.java deleted file mode 100644 index 20835b673e3..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/TestDefPath.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.common.rest.definition.path.PathRegExp; - -public class TestDefPath { - - @Test - public void testPathRegExp() throws Exception { - PathRegExp oPathRegExp = new PathRegExp("//{test}//"); - Assert.assertEquals(1, oPathRegExp.getGroupCount()); - Assert.assertEquals(0, oPathRegExp.getGroupWithRegExpCount()); - PathRegExp oSecondPathRegExp = new PathRegExp("{[^/:]+?}"); - Assert.assertEquals(1, oSecondPathRegExp.getGroupCount()); - Assert.assertEquals(1, oSecondPathRegExp.getGroupWithRegExpCount()); - Assert.assertEquals("test/", PathRegExp.ensureEndWithSlash("test/")); - Assert.assertEquals("test/", PathRegExp.ensureEndWithSlash("test")); - Assert.assertEquals(null, oSecondPathRegExp.match("{test/test}", null)); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/TestRestConst.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/TestRestConst.java deleted file mode 100644 index 96302c55d81..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/TestRestConst.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest; - -import org.junit.Assert; -import org.junit.Test; - -public class TestRestConst { - - @Test - public void testConst() { - Assert.assertEquals("server-http-request-creator", RestConst.HTTP_REQUEST_CREATOR); - Assert.assertEquals("rest-client-request-path", RestConst.REST_CLIENT_REQUEST_PATH); - Assert.assertEquals("swaggerRestOperation", RestConst.SWAGGER_REST_OPERATION); - Assert.assertEquals("rest", RestConst.REST); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/TestRestEngineSchemaListener.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/TestRestEngineSchemaListener.java deleted file mode 100644 index 4f55094622f..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/TestRestEngineSchemaListener.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; -import org.springframework.context.ApplicationContext; - -import io.servicecomb.common.rest.locator.ServicePathManager; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.servicecomb.swagger.generator.core.SwaggerGeneratorContext; -import io.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorContext; -import io.swagger.models.Swagger; - -public class TestRestEngineSchemaListener { - private final SwaggerGeneratorContext context = new PojoSwaggerGeneratorContext(); - - private static class TestRestEngineSchemaListenerSchemaImpl { - @SuppressWarnings("unused") - public int add(int x, int y) { - return 0; - } - } - - @Test - public void test() { - BeanUtils.setContext(Mockito.mock(ApplicationContext.class)); - - MicroserviceMeta mm = new MicroserviceMeta("app:ms"); - List smList = new ArrayList<>(); - - SwaggerGenerator generator = new SwaggerGenerator(context, TestRestEngineSchemaListenerSchemaImpl.class); - Swagger swagger = generator.generate(); - swagger.setBasePath(""); - SchemaMeta sm1 = new SchemaMeta(swagger, mm, "sid1"); - smList.add(sm1); - - RestEngineSchemaListener listener = new RestEngineSchemaListener(); - SchemaMeta[] smArr = smList.toArray(new SchemaMeta[smList.size()]); - listener.onSchemaLoaded(smArr); - // 重复调用,不应该出异常 - listener.onSchemaLoaded(smArr); - - ServicePathManager spm = ServicePathManager.getServicePathManager(mm); - Assert.assertEquals(mm, spm.getMicroserviceMeta()); - - Assert.assertSame(sm1, - spm.consumerLocateOperation("/add/", "POST").getOperation().getOperationMeta().getSchemaMeta()); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/TestParam.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/TestParam.java deleted file mode 100644 index 2304f626b4c..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/TestParam.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec; - -import java.io.ByteArrayInputStream; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import com.fasterxml.jackson.databind.util.ISO8601Utils; - -import io.servicecomb.common.rest.codec.param.BodyProcessorCreator; -import io.servicecomb.common.rest.codec.param.CookieProcessorCreator; -import io.servicecomb.common.rest.codec.param.FormProcessorCreator; -import io.servicecomb.common.rest.codec.param.HeaderProcessorCreator; -import io.servicecomb.common.rest.codec.param.ParamValueProcessor; -import io.servicecomb.common.rest.codec.param.ParamValueProcessorCreator; -import io.servicecomb.common.rest.codec.param.ParamValueProcessorCreatorManager; -import io.servicecomb.common.rest.codec.param.PathProcessorCreator; -import io.servicecomb.common.rest.codec.param.QueryProcessorCreator; -import io.servicecomb.common.rest.codec.param.RestClientRequestImpl; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.foundation.common.utils.ReflectUtils; -import io.swagger.models.parameters.BodyParameter; -import io.swagger.models.parameters.CookieParameter; -import io.swagger.models.parameters.FormParameter; -import io.swagger.models.parameters.HeaderParameter; -import io.swagger.models.parameters.PathParameter; -import io.swagger.models.parameters.QueryParameter; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpClientRequest; -import io.vertx.core.http.HttpHeaders; -import mockit.Mock; -import mockit.MockUp; - -public class TestParam { - - private Map serverPathParams = new HashMap<>(); - - private Map> serverQueryParams = new HashMap<>(); - - private Map> serverHttpHeaders = new HashMap<>(); - - private RestServerRequest serverRequest = - new LocalRestServerRequest(serverPathParams, serverQueryParams, serverHttpHeaders, null); - - private Map> clientHttpHeaders = new HashMap<>(); - - private Buffer clientBodyBuffer; - - private HttpClientRequest request; - - private RestClientRequest clientRequest = new RestClientRequestImpl(request); - - @Before - public void setup() { - request = new MockUp() { - @Mock - public HttpClientRequest putHeader(CharSequence name, CharSequence value) { - this.putHeader(name.toString(), value.toString()); - - return request; - } - - @Mock - public HttpClientRequest putHeader(String name, String value) { - List list = clientHttpHeaders.get(name); - if (list == null) { - list = new ArrayList<>(); - clientHttpHeaders.put(name, list); - } - list.add(value); - - return request; - } - - @Mock - public void end(Buffer chunk) { - clientBodyBuffer = chunk; - } - }.getMockInstance(); - - clientRequest = new RestClientRequestImpl(request); - } - - @Test - public void testCookie() throws Exception { - ParamValueProcessorCreator creator = - ParamValueProcessorCreatorManager.INSTANCE.findValue(CookieProcessorCreator.PARAMTYPE); - CookieParameter cp = new CookieParameter(); - cp.setName("c1"); - ParamValueProcessor processor = creator.create(cp, String.class); - - Date date = new Date(); - processor.setValue(clientRequest, date); - clientRequest.end(); - List cookies = clientHttpHeaders.get(HttpHeaders.COOKIE.toString()); - Assert.assertEquals("c1=" + ISO8601Utils.format(date) + "; ", cookies.get(0)); - - serverHttpHeaders.put(HttpHeaders.COOKIE.toString(), cookies); - Object newDate = processor.getValue(serverRequest); - Assert.assertEquals(ISO8601Utils.format(date), newDate); - } - - @Test - public void testPath() throws Exception { - ParamValueProcessorCreator creator = - ParamValueProcessorCreatorManager.INSTANCE.findValue(PathProcessorCreator.PARAMTYPE); - PathParameter pp = new PathParameter(); - pp.setName("p1"); - ParamValueProcessor processor = creator.create(pp, String.class); - - serverPathParams.put("p1", "path"); - Object value = processor.getValue(serverRequest); - Assert.assertEquals("path", value); - } - - @Test - public void testQuery() throws Exception { - ParamValueProcessorCreator creator = - ParamValueProcessorCreatorManager.INSTANCE.findValue(QueryProcessorCreator.PARAMTYPE); - QueryParameter qp = new QueryParameter(); - qp.setName("q1"); - - ParamValueProcessor processor = creator.create(qp, Date.class); - Date value = (Date) processor.getValue(serverRequest); - Assert.assertEquals(null, value); - - Date date = new Date(); - String strDate = ISO8601Utils.format(date); - serverQueryParams.put("q1", Arrays.asList(strDate)); - - value = (Date) processor.getValue(serverRequest); - Assert.assertEquals(date.toString(), value.toString()); - - processor = creator.create(qp, Date[].class); - Date[] values = (Date[]) processor.getValue(serverRequest); - Assert.assertEquals(1, values.length); - Assert.assertEquals(date.toString(), values[0].toString()); - } - - @Test - public void testHeader() throws Exception { - ParamValueProcessorCreator creator = - ParamValueProcessorCreatorManager.INSTANCE.findValue(HeaderProcessorCreator.PARAMTYPE); - HeaderParameter hp = new HeaderParameter(); - hp.setName("h1"); - - ParamValueProcessor processor = creator.create(hp, Date.class); - - Date date = new Date(); - processor.setValue(clientRequest, date); - clientRequest.end(); - List headValues = clientHttpHeaders.get("h1"); - Assert.assertEquals(ISO8601Utils.format(date), headValues.get(0)); - - serverHttpHeaders.put("h1", headValues); - Object newDate = processor.getValue(serverRequest); - Assert.assertEquals(date.toString(), newDate.toString()); - } - - @Test - public void testForm() throws Exception { - ParamValueProcessorCreator creator = - ParamValueProcessorCreatorManager.INSTANCE.findValue(FormProcessorCreator.PARAMTYPE); - FormParameter fp = new FormParameter(); - fp.setName("f1"); - ParamValueProcessor processor = creator.create(fp, Date.class); - - Date date = new Date(); - processor.setValue(clientRequest, date); - clientRequest.end(); - Assert.assertEquals("f1=" + ISO8601Utils.format(date) + "&", clientBodyBuffer.toString()); - } - - static class Body { - public Date date = new Date(); - } - - @Test - public void testBody() throws Exception { - ParamValueProcessorCreator creator = - ParamValueProcessorCreatorManager.INSTANCE.findValue(BodyProcessorCreator.PARAMTYPE); - BodyParameter bp = new BodyParameter(); - ParamValueProcessor processor = creator.create(bp, Body.class); - - Body body = new Body(); - processor.setValue(clientRequest, body); - clientRequest.end(); - - String expect = RestObjectMapper.INSTANCE.writeValueAsString(body); - Assert.assertEquals(expect, clientBodyBuffer.toString()); - - ByteArrayInputStream is = new ByteArrayInputStream(expect.getBytes(StandardCharsets.UTF_8)); - ReflectUtils.setField(serverRequest, "bodyObject", is); - is.close(); - Body result = (Body) processor.getValue(serverRequest); - Assert.assertEquals(body.date.toString(), result.date.toString()); - } - - @Test - public void testRestCodec() { - boolean status = false; - try { - RestCodec.argsToRest((new String[] {"test"}), - Mockito.mock(RestOperationMeta.class), - Mockito.mock(RestClientRequest.class)); - RestCodec.restToArgs(Mockito.mock(RestServerRequest.class), Mockito.mock(RestOperationMeta.class)); - } catch (Exception e) { - status = true; - } - Assert.assertFalse(status); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/TestRestCodec.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/TestRestCodec.java deleted file mode 100644 index e555ce31cd2..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/TestRestCodec.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec; - -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.ws.rs.core.Response.Status; - -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.codec.param.ParamValueProcessor; -import io.servicecomb.common.rest.codec.param.RestClientRequestImpl; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.common.rest.definition.RestParam; -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.servicecomb.swagger.invocation.exception.InvocationException; -import io.swagger.models.parameters.HeaderParameter; -import io.swagger.models.parameters.Parameter; -import mockit.Expectations; -import mockit.Mocked; - -public class TestRestCodec { - - private static RestOperationMeta restOperation; - - private static Map header = new HashMap<>(); - - private static RestClientRequest clientRequest = new RestClientRequestImpl(null) { - public void putHeader(String name, String value) { - header.put(name, value); - } - }; - - private static List paramList = null; - - @BeforeClass - public static void beforeClass() { - restOperation = Mockito.mock(RestOperationMeta.class); - // clientRequest = Mockito.mock(RestClientRequest.class); - paramList = new ArrayList<>(); - - Parameter hp = new HeaderParameter(); - hp.setName("header"); - paramList.add(new RestParam(0, hp, int.class)); - when(restOperation.getParamList()).thenReturn(paramList); - } - - @AfterClass - public static void afterClass() { - restOperation = null; - clientRequest = null; - paramList.clear(); - } - - @Test - public void testArgsToRest() { - try { - RestCodec.argsToRest(new String[] {"abc"}, restOperation, clientRequest); - Assert.assertEquals("abc", header.get("header")); - } catch (Exception e) { - Assert.assertTrue(false); - } - } - - @Test - public void testRestToArgs(@Mocked RestServerRequest request, - @Mocked RestOperationMeta restOperation, @Mocked RestParam restParam, - @Mocked ParamValueProcessor processer) throws Exception { - List params = new ArrayList<>(); - params.add(restParam); - String s = "my"; - - new Expectations() { - { - restOperation.getParamList(); - result = params; - restParam.getParamProcessor(); - result = processer; - processer.getValue(request); - result = s; - } - }; - - Object[] xx = RestCodec.restToArgs(request, restOperation); - Assert.assertEquals(xx[0], s); - } - - @Test - public void testRestToArgsExcetpion(@Mocked RestServerRequest request, - @Mocked RestOperationMeta restOperation, @Mocked RestParam restParam, - @Mocked ParamValueProcessor processer) throws Exception { - List params = new ArrayList<>(); - params.add(restParam); - - new Expectations() { - { - restOperation.getParamList(); - result = params; - restParam.getParamProcessor(); - result = processer; - processer.getValue(request); - result = new Exception("bad request parame"); - } - }; - - boolean success = false; - try { - RestCodec.restToArgs(request, restOperation); - success = true; - } catch (InvocationException e) { - Assert.assertEquals(590, e.getStatusCode()); - Assert.assertEquals("Parameter is not valid.", ((CommonExceptionData) e.getErrorData()).getMessage()); - } - Assert.assertEquals(success, false); - } - - @Test - public void testRestToArgsInstanceExcetpion(@Mocked RestServerRequest request, - @Mocked RestOperationMeta restOperation, @Mocked RestParam restParam, - @Mocked ParamValueProcessor processer) throws Exception { - List params = new ArrayList<>(); - params.add(restParam); - InvocationException exception = new InvocationException(Status.BAD_REQUEST, "Parameter is not valid."); - - new Expectations() { - { - restOperation.getParamList(); - result = params; - restParam.getParamProcessor(); - result = processer; - processer.getValue(request); - result = exception; - } - }; - - boolean success = false; - try { - RestCodec.restToArgs(request, restOperation); - success = true; - } catch (InvocationException e) { - Assert.assertEquals(e.getStatusCode(), Status.BAD_REQUEST.getStatusCode()); - } - Assert.assertEquals(success, false); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestBodyProcessor.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestBodyProcessor.java deleted file mode 100644 index de0142d4549..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestBodyProcessor.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import static org.mockito.Mockito.when; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.codec.RestServerRequest; -import io.servicecomb.foundation.vertx.stream.BufferInputStream; -import io.servicecomb.swagger.generator.core.SwaggerConst; -import io.swagger.models.parameters.BodyParameter; -import io.vertx.core.buffer.Buffer; - -public class TestBodyProcessor { - private static RestServerRequest request; - - private static ParamValueProcessor bodyProcessor; - - @Before - public void beforeTest() { - request = Mockito.mock(RestServerRequest.class); - } - - @Test - public void testString() throws Exception { - BodyProcessorCreator bodyCreator = - (BodyProcessorCreator) ParamValueProcessorCreatorManager.INSTANCE.getBodyProcessorCreater(); - BodyParameter bp = new BodyParameter(); - bp.setVendorExtension(SwaggerConst.EXT_RAW_JSON_TYPE, false); - bodyProcessor = bodyCreator.create(bp, String.class); - - Buffer buffer = Buffer.buffer("\"abc\""); - when(request.getBody()).thenReturn(new BufferInputStream(buffer.getByteBuf())); - - String result = (String) bodyProcessor.getValue(request); - Assert.assertEquals("abc", result); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestBodyProcessorCreator.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestBodyProcessorCreator.java deleted file mode 100644 index 9b89d109c2b..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestBodyProcessorCreator.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import java.util.List; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.servicecomb.common.rest.codec.param.BodyProcessorCreator.RawJsonBodyProcessor; -import io.servicecomb.swagger.generator.core.SwaggerConst; -import io.swagger.models.parameters.BodyParameter; - -public class TestBodyProcessorCreator { - private static BodyProcessorCreator bodyCreator; - - @Before - public void beforeTest() { - bodyCreator = (BodyProcessorCreator) ParamValueProcessorCreatorManager.INSTANCE.getBodyProcessorCreater(); - } - - @Test - public void testBodyProcessorCreator() throws Exception { - BodyParameter bp = new BodyParameter(); - bp.setVendorExtension(SwaggerConst.EXT_RAW_JSON_TYPE, true); - - ParamValueProcessor processor = bodyCreator.create(bp, String.class); - Assert.assertTrue(RawJsonBodyProcessor.class.isInstance(processor)); - - bp.setVendorExtension(SwaggerConst.EXT_RAW_JSON_TYPE, false); - processor = bodyCreator.create(bp, String.class); - Assert.assertFalse(RawJsonBodyProcessor.class.isInstance(processor)); - - bp.setVendorExtension(SwaggerConst.EXT_RAW_JSON_TYPE, true); - processor = bodyCreator.create(bp, List.class); - Assert.assertFalse(RawJsonBodyProcessor.class.isInstance(processor)); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestRawJsonBodyProcessor.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestRawJsonBodyProcessor.java deleted file mode 100644 index 3e0adaf5f91..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/param/TestRawJsonBodyProcessor.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.param; - -import static org.mockito.Mockito.when; - -import java.util.Map; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.common.rest.codec.RestServerRequest; -import io.servicecomb.foundation.vertx.stream.BufferInputStream; -import io.servicecomb.swagger.generator.core.SwaggerConst; -import io.swagger.models.parameters.BodyParameter; -import io.vertx.core.buffer.Buffer; - -public class TestRawJsonBodyProcessor { - private static RestServerRequest request; - - private static ParamValueProcessor bodyProcessor; - - @Before - public void beforeTest() { - request = Mockito.mock(RestServerRequest.class); - BodyProcessorCreator bodyCreator = - (BodyProcessorCreator) ParamValueProcessorCreatorManager.INSTANCE.getBodyProcessorCreater(); - BodyParameter bp = new BodyParameter(); - bp.setVendorExtension(SwaggerConst.EXT_RAW_JSON_TYPE, true); - bodyProcessor = bodyCreator.create(bp, String.class); - } - - @Test - public void testJsonInt() throws Exception { - Buffer buffer = Buffer.buffer("123"); - when(request.getBody()).thenReturn(new BufferInputStream(buffer.getByteBuf())); - - String result = (String) bodyProcessor.getValue(request); - Assert.assertEquals("123", result); - } - - @Test - public void testJsonString() throws Exception { - Buffer buffer = Buffer.buffer("\"abc\""); - when(request.getBody()).thenReturn(new BufferInputStream(buffer.getByteBuf())); - - String result = (String) bodyProcessor.getValue(request); - Assert.assertEquals("\"abc\"", result); - } - - @Test - public void testJsonMap() throws Exception { - Buffer buffer = Buffer.buffer("{\"abc\":\"def\"}"); - when(request.getBody()).thenReturn(new BufferInputStream(buffer.getByteBuf())); - - String result = (String) bodyProcessor.getValue(request); - Assert.assertEquals("{\"abc\":\"def\"}", result); - - @SuppressWarnings("unchecked") - Map resMap = RestObjectMapper.INSTANCE.readValue(result.getBytes(), - Map.class); - Assert.assertEquals("def", resMap.get("abc")); - } - - @Test - public void testJsonList() throws Exception { - Buffer buffer = Buffer.buffer("[\"abc\"]"); - when(request.getBody()).thenReturn(new BufferInputStream(buffer.getByteBuf())); - - String result = (String) bodyProcessor.getValue(request); - Assert.assertEquals("[\"abc\"]", result); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/produce/TestProduceJsonProcessor.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/produce/TestProduceJsonProcessor.java deleted file mode 100644 index 7f0bffe2ef9..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/produce/TestProduceJsonProcessor.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.produce; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.nio.charset.StandardCharsets; - -import org.junit.Assert; -import org.junit.Test; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.vertx.core.buffer.Buffer; - -public class TestProduceJsonProcessor { - ProduceProcessor pp = ProduceProcessorManager.JSON_PROCESSOR; - - JavaType stringType = TypeFactory.defaultInstance().constructType(String.class); - - @Test - public void testEncodeResponseNull() throws Exception { - Buffer buffer = pp.encodeResponse(null); - Assert.assertNull(buffer); - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - pp.encodeResponse(os, null); - Assert.assertEquals(0, os.size()); - } - - @Test - public void testdecodeResponseNull() throws Exception { - JavaType resultType = TypeFactory.unknownType(); - Object result = pp.decodeResponse(Buffer.buffer(), resultType); - Assert.assertNull(result); - - ByteArrayInputStream is = new ByteArrayInputStream(new byte[] {}); - result = pp.decodeResponse(is, resultType); - Assert.assertNull(result); - } - - @Test - public void testBufferNormal() throws Exception { - String value = "abc"; - Buffer buffer = pp.encodeResponse(value); - Assert.assertEquals("\"abc\"", buffer.toString(StandardCharsets.UTF_8)); - - Object result = pp.decodeResponse(buffer, stringType); - Assert.assertEquals(value, result); - } - - @Test - public void testStreamNormal() throws Exception { - String value = "abc"; - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - pp.encodeResponse(os, value); - Assert.assertEquals("\"abc\"", os.toString(StandardCharsets.UTF_8.name())); - - ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); - Object result = pp.decodeResponse(is, stringType); - Assert.assertEquals(value, result); - - os.close(); - is.close(); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/produce/TestProduceProcessorManager.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/produce/TestProduceProcessorManager.java deleted file mode 100644 index e957a998217..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/produce/TestProduceProcessorManager.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.produce; - -import org.junit.Assert; -import org.junit.Test; - -public class TestProduceProcessorManager { - @Test - public void testDefault() { - Assert.assertSame(ProduceProcessorManager.JSON_PROCESSOR, ProduceProcessorManager.DEFAULT_PROCESSOR); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/produce/TestProduceTextPlainProcessor.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/produce/TestProduceTextPlainProcessor.java deleted file mode 100644 index ce83548e740..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/codec/produce/TestProduceTextPlainProcessor.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.codec.produce; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.nio.charset.StandardCharsets; - -import org.junit.Assert; -import org.junit.Test; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.vertx.core.buffer.Buffer; - -public class TestProduceTextPlainProcessor { - ProduceProcessor pp = ProduceProcessorManager.PLAIN_PROCESSOR; - - JavaType stringType = TypeFactory.defaultInstance().constructType(String.class); - - @Test - public void testEncodeResponseNull() throws Exception { - Buffer buffer = pp.encodeResponse(null); - Assert.assertNull(buffer); - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - pp.encodeResponse(os, null); - Assert.assertEquals(0, os.size()); - } - - @Test - public void testdecodeResponseNull() throws Exception { - JavaType resultType = TypeFactory.unknownType(); - Object result = pp.decodeResponse(Buffer.buffer(), resultType); - Assert.assertNull(result); - - ByteArrayInputStream is = new ByteArrayInputStream(new byte[] {}); - result = pp.decodeResponse(is, resultType); - Assert.assertNull(result); - } - - @Test - public void testBufferNormal() throws Exception { - String value = "abc"; - Buffer buffer = pp.encodeResponse(value); - Assert.assertEquals(value, buffer.toString(StandardCharsets.UTF_8)); - - Object result = pp.decodeResponse(buffer, stringType); - Assert.assertEquals(value, result); - } - - @Test - public void testStreamNormal() throws Exception { - String value = "abc"; - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - pp.encodeResponse(os, value); - Assert.assertEquals(value, os.toString(StandardCharsets.UTF_8.name())); - - ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); - Object result = pp.decodeResponse(is, stringType); - Assert.assertEquals(value, result); - - os.close(); - is.close(); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/TestPath.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/TestPath.java deleted file mode 100644 index 27972908594..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/TestPath.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.definition; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.servicecomb.common.rest.definition.path.PathRegExp; -import io.servicecomb.common.rest.definition.path.QueryVarParamWriter; -import io.servicecomb.common.rest.definition.path.URLPathBuilder; -import io.swagger.models.parameters.Parameter; -import io.swagger.models.parameters.PathParameter; -import io.swagger.models.parameters.QueryParameter; -import mockit.Mock; -import mockit.MockUp; - -public class TestPath { - - @Before - public void setUp() throws Exception { - } - - @After - public void tearDown() throws Exception { - } - - @Test - public void testPathRegExp() throws Exception { - PathRegExp oPathRegExp = new PathRegExp("//{test}//"); - Assert.assertEquals(1, oPathRegExp.getGroupCount()); - Assert.assertEquals(0, oPathRegExp.getGroupWithRegExpCount()); - PathRegExp oSecondPathRegExp = new PathRegExp("{[^/:]+?}"); - Assert.assertEquals(1, oSecondPathRegExp.getGroupCount()); - Assert.assertEquals(1, oSecondPathRegExp.getGroupWithRegExpCount()); - Assert.assertEquals("test/", PathRegExp.ensureEndWithSlash("test/")); - Assert.assertEquals("test/", PathRegExp.ensureEndWithSlash("test")); - Assert.assertEquals(null, oSecondPathRegExp.match("{test/test}", null)); - Assert.assertEquals("(]+?)/(.*)", (oSecondPathRegExp.toString())); - Assert.assertEquals(false, oSecondPathRegExp.isStaticPath()); - Assert.assertEquals(0, oSecondPathRegExp.getStaticCharCount()); - Assert.assertNotEquals(null, (oPathRegExp.match("//{test}//", new HashMap()))); - // Error Scenarios - oPathRegExp = new PathRegExp("//{test \t}//"); - // Error Scenarios for double {{ - try { - oPathRegExp = new PathRegExp("//{test{"); - } catch (Exception e) { - Assert.assertEquals(true, e.getMessage().contains("A variable must not contain an extra")); - } - // Error Scenarios for illegal }} - try { - oPathRegExp = new PathRegExp("//}"); - } catch (Exception e) { - Assert.assertEquals(true, e.getMessage().contains("is only allowed as")); - } - // Error Scenarios for illegal ; - try { - oPathRegExp = new PathRegExp("//;"); - } catch (Exception e) { - Assert.assertEquals(true, e.getMessage().contains("matrix parameters are not allowed in")); - } - - // Error Scenarios for NO } ; - try { - oPathRegExp = new PathRegExp("//{test"); - } catch (Exception e) { - Assert.assertEquals(true, e.getMessage().contains("No '}' found after")); - } - } - - @Test - public void testUrlPathBuilder() throws Exception { - Map paramMap = new HashMap<>(); - - Parameter pathParameter = new PathParameter(); - pathParameter.setName("id"); - RestParam oRestParam = new RestParam(0, pathParameter, int.class); - paramMap.put(oRestParam.getParamName(), oRestParam); - - Parameter queryParameter = new QueryParameter(); - queryParameter.setName("q"); - oRestParam = new RestParam(1, queryParameter, String.class); - paramMap.put(oRestParam.getParamName(), oRestParam); - - URLPathBuilder oURLPathBuilder = new URLPathBuilder("/root/{id}", paramMap); - Object[] args = new Object[] {100, "query"}; - Assert.assertEquals("/root/100?q=query", oURLPathBuilder.createRequestPath(args)); - Assert.assertEquals("/root/100", oURLPathBuilder.createPathString(args)); - } - - @Test - public void testQueryVarParamWriter() throws Exception { - boolean status = true; - new MockUp() { - @Mock - public String getParamName() { - return "queryVar"; - } - }; - new MockUp() { - @Mock - protected Object getParamValue(Object[] args) { - return args[0]; - } - }; - - Parameter parameter = new QueryParameter(); - QueryVarParamWriter writer = new QueryVarParamWriter('&', new RestParam(0, parameter, String.class)); - try { - verify(writer, "T", "&queryVar=T"); - verify(writer, null, "&"); - verify(writer, new String[] {"a", "b"}, "&queryVar=a&queryVar=b"); - verify(writer, new String[] {"a", null, "b"}, "&queryVar=a&queryVar=&queryVar=b"); - verify(writer, Arrays.asList("Lars", "Simon"), "&queryVar=Lars&queryVar=Simon"); - verify(writer, "测试", "&queryVar=%E6%B5%8B%E8%AF%95"); - verify(writer, "a b", "&queryVar=a+b"); - verify(writer, "a+b", "&queryVar=a%2Bb"); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - private void verify(QueryVarParamWriter writer, Object arg, String expect) throws Exception { - StringBuilder sb = new StringBuilder(); - Object[] args = new Object[] {arg}; - writer.write(sb, args); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/TestRestOperationComparator.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/TestRestOperationComparator.java deleted file mode 100644 index 9a20eddc0ba..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/TestRestOperationComparator.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.definition; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.common.rest.locator.MicroservicePaths; - -public class TestRestOperationComparator { - @Test - public void testStaticCharCount() { - RestOperationMeta less = new RestOperationMeta(); - less.setAbsolutePath("/a/{id}"); - - RestOperationMeta more = new RestOperationMeta(); - more.setAbsolutePath("/abc/{id}"); - - MicroservicePaths paths = new MicroservicePaths(); - paths.addResource(less); - paths.addResource(more); - paths.sortPath(); - - Assert.assertSame(more, paths.getDynamicPathOperationList().get(0)); - Assert.assertSame(less, paths.getDynamicPathOperationList().get(1)); - } - - @Test - public void testVarGroupCount() { - RestOperationMeta less = new RestOperationMeta(); - less.setAbsolutePath("/ab/{id}"); - - RestOperationMeta more = new RestOperationMeta(); - more.setAbsolutePath("/a/{test}/{id}"); - - MicroservicePaths paths = new MicroservicePaths(); - paths.addResource(less); - paths.addResource(more); - paths.sortPath(); - - Assert.assertSame(more, paths.getDynamicPathOperationList().get(0)); - Assert.assertSame(less, paths.getDynamicPathOperationList().get(1)); - } - - @Test - public void testGroupWithRegExpCount() { - RestOperationMeta less = new RestOperationMeta(); - less.setAbsolutePath("/a/{test}/{id}"); - - RestOperationMeta more = new RestOperationMeta(); - more.setAbsolutePath("/a/{test : .+}/{id}"); - - MicroservicePaths paths = new MicroservicePaths(); - paths.addResource(less); - paths.addResource(more); - paths.sortPath(); - - Assert.assertSame(more, paths.getDynamicPathOperationList().get(0)); - Assert.assertSame(less, paths.getDynamicPathOperationList().get(1)); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/TestRestOperationMeta.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/TestRestOperationMeta.java deleted file mode 100644 index 54b77884fb9..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/TestRestOperationMeta.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.definition; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import javax.ws.rs.core.MediaType; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.codec.produce.ProduceProcessorManager; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.swagger.models.Operation; -import io.swagger.models.Swagger; - -public class TestRestOperationMeta { - - private final Swagger swagger = Mockito.mock(Swagger.class); - - private final SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); - - private final Operation operation = Mockito.mock(Operation.class); - - private final OperationMeta meta = mock(OperationMeta.class); - - private final RestOperationMeta operationMeta = new RestOperationMeta(); - - @Before - public void setUp() throws Exception { - when(meta.getSchemaMeta()).thenReturn(schemaMeta); - when(meta.getSwaggerOperation()).thenReturn(operation); - when(meta.getMethod()).thenReturn(SomeRestController.class.getMethod("sayHi")); - - when(schemaMeta.getSwagger()).thenReturn(swagger); - } - - @Test - public void testSplitAcceptTypes() { - String types = "application/json;charset=utf-8,*/*;q=0.9"; - Assert.assertArrayEquals(new String[] {"application/json", "*/*"}, operationMeta.splitAcceptTypes(types)); - } - - @Test - public void testContainSpecType() { - Assert.assertTrue(operationMeta.containSpecType(new String[] {MediaType.WILDCARD}, MediaType.WILDCARD)); - Assert.assertFalse( - operationMeta.containSpecType(new String[] {MediaType.APPLICATION_JSON}, MediaType.TEXT_PLAIN)); - } - - @Test - public void testEnsureFindProduceProcessor() { - operationMeta.setDefaultProcessor(ProduceProcessorManager.JSON_PROCESSOR); - - Assert.assertEquals(operationMeta.getDefaultProcessor(), operationMeta.ensureFindProduceProcessor("")); - - Assert.assertEquals(operationMeta.getDefaultProcessor(), - operationMeta.ensureFindProduceProcessor(MediaType.WILDCARD)); - - operationMeta.ensureFindProduceProcessor("####"); - operationMeta.createProduceProcessors(); - String types = "application/json;charset=utf-8,text/plain;q=0.8"; - Assert.assertEquals(ProduceProcessorManager.JSON_PROCESSOR, operationMeta.ensureFindProduceProcessor(types)); - - Assert.assertEquals(null, operationMeta.getParamByName("test")); - Assert.assertEquals(null, operationMeta.getPathBuilder()); - Assert.assertEquals(null, operationMeta.findProduceProcessor("test")); - } - - @Test - public void generatesAbsolutePathWithRootBasePath() { - when(swagger.getBasePath()).thenReturn("/"); - when(meta.getOperationPath()).thenReturn("/sayHi/"); - - operationMeta.init(meta); - - assertThat(operationMeta.getAbsolutePath(), is("/sayHi/")); - } - - @Test - public void generatesAbsolutePathWithNonRootBasePath() { - when(swagger.getBasePath()).thenReturn("/rest"); - when(meta.getOperationPath()).thenReturn("/sayHi"); - - operationMeta.init(meta); - - assertThat(operationMeta.getAbsolutePath(), is("/rest/sayHi/")); - } - - @Test - public void generatesAbsolutePathWithNullPath() { - when(swagger.getBasePath()).thenReturn(null); - when(meta.getOperationPath()).thenReturn(null); - - operationMeta.init(meta); - - assertThat(operationMeta.getAbsolutePath(), is("/")); - } - - @Test - public void generatesAbsolutePathWithEmptyPath() { - when(swagger.getBasePath()).thenReturn(""); - when(meta.getOperationPath()).thenReturn(""); - - operationMeta.init(meta); - - assertThat(operationMeta.getAbsolutePath(), is("/")); - } - - @Test - public void consecutiveSlashesAreRemoved() { - when(swagger.getBasePath()).thenReturn("//rest//"); - when(meta.getOperationPath()).thenReturn("//sayHi//"); - - operationMeta.init(meta); - - assertThat(operationMeta.getAbsolutePath(), is("/rest/sayHi/")); - } - - private static class SomeRestController { - @SuppressWarnings("unused") - public String sayHi() { - return "Hi"; - } - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/UnitTestRestUtils.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/UnitTestRestUtils.java deleted file mode 100644 index ab04ff0f130..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/definition/UnitTestRestUtils.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.definition; - -import java.util.Arrays; - -import javax.ws.rs.core.MediaType; - -import io.servicecomb.core.definition.OperationMeta; -import mockit.Deencapsulation; - -public class UnitTestRestUtils { - public static RestOperationMeta createRestOperatonMeta(String httpMethod, String path) { - OperationMeta om = new OperationMeta(); - om.setHttpMethod(httpMethod); - - RestOperationMeta rom = new RestOperationMeta(); - rom.setOperationMeta(om); - rom.setAbsolutePath(path); - Deencapsulation.setField(rom, "produces", Arrays.asList(MediaType.APPLICATION_JSON)); - return rom; - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/locator/TestMicroservicePaths.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/locator/TestMicroservicePaths.java deleted file mode 100644 index 894bf6d7692..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/locator/TestMicroservicePaths.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.locator; - -import java.io.StringWriter; -import java.io.Writer; - -import org.apache.log4j.Logger; -import org.apache.log4j.SimpleLayout; -import org.apache.log4j.WriterAppender; -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.common.rest.definition.UnitTestRestUtils; -import io.servicecomb.foundation.common.exceptions.ServiceCombException; - -public class TestMicroservicePaths { - @Test - public void testAddResourceStaticNewGroup() { - RestOperationMeta staticRes = UnitTestRestUtils.createRestOperatonMeta("POST", "/static"); - - MicroservicePaths paths = new MicroservicePaths(); - paths.addResource(staticRes); - - Assert.assertSame(staticRes, paths.getStaticPathOperationMap().get("/static").findValue("POST")); - } - - @Test - public void testAddResourceStaticAddToGroup() { - RestOperationMeta staticResPost = UnitTestRestUtils.createRestOperatonMeta("POST", "/static"); - RestOperationMeta staticResGet = UnitTestRestUtils.createRestOperatonMeta("GET", "/static"); - - MicroservicePaths paths = new MicroservicePaths(); - paths.addResource(staticResPost); - paths.addResource(staticResGet); - - Assert.assertSame(staticResPost, paths.getStaticPathOperationMap().get("/static").findValue("POST")); - Assert.assertSame(staticResGet, paths.getStaticPathOperationMap().get("/static").findValue("GET")); - } - - @Test - public void testAddResourceStaticDuplicatedHttpMethod() { - RestOperationMeta staticResPost = UnitTestRestUtils.createRestOperatonMeta("POST", "/static"); - - MicroservicePaths paths = new MicroservicePaths(); - paths.addResource(staticResPost); - - try { - paths.addResource(staticResPost); - Assert.fail("must throw exception"); - } catch (ServiceCombException e) { - Assert.assertEquals("operation with url /static, method POST is duplicated.", e.getMessage()); - } - } - - @Test - public void testAddResourceDynamic() { - RestOperationMeta dynamicRes = UnitTestRestUtils.createRestOperatonMeta("POST", "/dynamic/{id}"); - - MicroservicePaths paths = new MicroservicePaths(); - paths.addResource(dynamicRes); - - Assert.assertSame(dynamicRes, paths.getDynamicPathOperationList().get(0)); - } - - @Test - public void testCloneTo() { - RestOperationMeta staticRes = UnitTestRestUtils.createRestOperatonMeta("POST", "/static"); - RestOperationMeta dynamicRes = UnitTestRestUtils.createRestOperatonMeta("POST", "/dynamic/{id}"); - - MicroservicePaths paths = new MicroservicePaths(); - paths.addResource(staticRes); - paths.addResource(dynamicRes); - - MicroservicePaths other = new MicroservicePaths(); - paths.cloneTo(other); - - Assert.assertEquals(paths.getStaticPathOperationMap(), other.getStaticPathOperationMap()); - Assert.assertEquals(paths.getDynamicPathOperationList(), other.getDynamicPathOperationList()); - } - - @Test - public void testSortPath() { - // only test base rule - // completely rule test by TestRestOperationComparator - RestOperationMeta dynamicResLessStatic = UnitTestRestUtils.createRestOperatonMeta("POST", "/a/{id}"); - RestOperationMeta dynamicResMoreStatic = UnitTestRestUtils.createRestOperatonMeta("POST", "/abc/{id}"); - - MicroservicePaths paths = new MicroservicePaths(); - paths.addResource(dynamicResLessStatic); - paths.addResource(dynamicResMoreStatic); - paths.sortPath(); - - Assert.assertSame(dynamicResMoreStatic, paths.getDynamicPathOperationList().get(0)); - Assert.assertSame(dynamicResLessStatic, paths.getDynamicPathOperationList().get(1)); - } - - @Test - public void testPrintPaths() { - RestOperationMeta staticRes = UnitTestRestUtils.createRestOperatonMeta("POST", "/static"); - RestOperationMeta dynamicRes = UnitTestRestUtils.createRestOperatonMeta("POST", "/dynamic/{id}"); - - MicroservicePaths paths = new MicroservicePaths(); - paths.addResource(staticRes); - paths.addResource(dynamicRes); - - WriterAppender appender = new WriterAppender(); - Writer writer = new StringWriter(); - appender.setWriter(writer); - appender.setLayout(new SimpleLayout()); - Logger.getRootLogger().addAppender(appender); - - paths.printPaths(); - - String[] lines = writer.toString().split("\n"); - Assert.assertEquals("INFO - Swagger mapped \"{[/static], method=[POST], produces=[application/json]}\" onto null", - lines[0].trim()); - Assert.assertEquals( - "INFO - Swagger mapped \"{[/dynamic/{id}], method=[POST], produces=[application/json]}\" onto null", - lines[1].trim()); - - Logger.getRootLogger().removeAppender(appender); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/locator/TestOperationLocator.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/locator/TestOperationLocator.java deleted file mode 100644 index 56e3512e5c5..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/locator/TestOperationLocator.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.locator; - -import javax.ws.rs.core.Response.Status; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.common.rest.definition.UnitTestRestUtils; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -public class TestOperationLocator { - MicroservicePaths paths = new MicroservicePaths(); - - OperationLocator locator = new OperationLocator(); - - private RestOperationMeta addRestOperationMeta(String httpMethod, String path) { - RestOperationMeta rom = UnitTestRestUtils.createRestOperatonMeta(httpMethod, path); - paths.addResource(rom); - return rom; - } - - @Test - public void testLocateNotFound() { - try { - locator.locate("ms", "/notExist", "GET", paths); - Assert.fail("must throw exception"); - } catch (InvocationException e) { - Assert.assertEquals(Status.NOT_FOUND, e.getStatus()); - } - } - - @Test - public void testLocateNotFoundDynamicRemained() { - addRestOperationMeta("GET", "/dynamic/{id}"); - try { - locator.locate("ms", "/dynamic/1/2", "GET", paths); - Assert.fail("must throw exception"); - } catch (InvocationException e) { - Assert.assertEquals(Status.NOT_FOUND, e.getStatus()); - } - } - - @Test - public void testLocateStaticMethodNotAllowed() { - addRestOperationMeta("GET", "/static"); - - try { - locator.locate("ms", "/static", "POST", paths); - Assert.fail("must throw exception"); - } catch (InvocationException e) { - Assert.assertEquals(Status.METHOD_NOT_ALLOWED, e.getStatus()); - } - } - - @Test - public void testLocateDynamicMethodNotAllowed() { - addRestOperationMeta("GET", "/dynamic/{id}"); - try { - locator.locate("ms", "/dynamic/1/", "POST", paths); - Assert.fail("must throw exception"); - } catch (InvocationException e) { - Assert.assertEquals(Status.METHOD_NOT_ALLOWED, e.getStatus()); - } - } - - @Test - public void testLocateStaticFound() { - RestOperationMeta rom = addRestOperationMeta("GET", "/static"); - locator.locate("ms", "/static", "GET", paths); - - Assert.assertSame(rom, locator.getOperation()); - } - - @Test - public void testLocateDynamicFound() { - RestOperationMeta rom = addRestOperationMeta("GET", "/dynamic/{id}"); - locator.locate("ms", "/dynamic/1/", "GET", paths); - - Assert.assertSame(rom, locator.getOperation()); - Assert.assertEquals("1", locator.getPathVarMap().get("id")); - } -} diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/locator/TestServicePathManager.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/locator/TestServicePathManager.java deleted file mode 100644 index f15d74335fb..00000000000 --- a/common/common-rest/src/test/java/io/servicecomb/common/rest/locator/TestServicePathManager.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.common.rest.locator; - -import java.util.Map; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.springframework.context.ApplicationContext; - -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; -import io.swagger.models.Path; -import io.swagger.models.Swagger; -import mockit.Mocked; - -public class TestServicePathManager { - private static class TestServicePathManagerSchemaImpl { - @SuppressWarnings("unused") - public void static1() { - } - - @SuppressWarnings("unused") - public void static2() { - } - - @SuppressWarnings("unused") - public void dynamic1() { - } - - @SuppressWarnings("unused") - public void dynamic2() { - } - } - - private ServicePathManager spm; - - @Mocked - ApplicationContext applicationContext; - - @Before - public void setup() { - BeanUtils.setContext(applicationContext); - - MicroserviceMeta mm = new MicroserviceMeta("app:ms"); - Swagger swagger = UnitTestSwaggerUtils.generateSwagger(TestServicePathManagerSchemaImpl.class).getSwagger(); - Map paths = swagger.getPaths(); - - swagger.setBasePath(""); - Path path = paths.remove("/static1"); - paths.put("/root/rest/static1", path); - - path = paths.remove("/dynamic1"); - paths.put("/dynamic1/{id}", path); - - path = paths.remove("/dynamic2"); - paths.put("/dynamic2/{id}", path); - - SchemaMeta schemaMeta = new SchemaMeta(swagger, mm, "sid"); - - spm = new ServicePathManager(mm); - spm.addSchema(schemaMeta); - spm.sortPath(); - } - - @After - public void teardown() { - BeanUtils.setContext(null); - } - - @Test - public void testBuildProducerPathsNoPrefix() { - System.clearProperty(Const.URL_PREFIX); - - spm.buildProducerPaths(); - Assert.assertSame(spm.producerPaths, spm.swaggerPaths); - } - - @Test - public void testBuildProducerPathsHasPrefix() { - System.setProperty(Const.URL_PREFIX, "/root/rest"); - - spm.buildProducerPaths(); - - // all locate should be success - spm.producerLocateOperation("/root/rest/static1/", "POST"); - spm.producerLocateOperation("/root/rest/static2/", "POST"); - spm.producerLocateOperation("/root/rest/dynamic1/1/", "POST"); - spm.producerLocateOperation("/root/rest/dynamic2/1/", "POST"); - - System.clearProperty(Const.URL_PREFIX); - } -} 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 new file mode 100644 index 00000000000..50b3e8329d5 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.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.common.rest; + +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 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.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.SCBStatus; +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.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; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.junit.jupiter.api.AfterAll; +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.Environment; + +import io.vertx.core.json.Json; +import io.vertx.ext.web.RoutingContext; + +public class RestProducerInvocationCreatorTest { + + final RoutingContext routingContext = Mockito.mock(RoutingContext.class); + + final MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); + + final ServicePathManager servicePathManager = Mockito.mock(ServicePathManager.class); + + final RestOperationMeta restOperationMeta = Mockito.mock(RestOperationMeta.class); + + final Endpoint endpoint = Mockito.mock(Endpoint.class); + + final HttpServletRequestEx requestEx = Mockito.mock(HttpServletRequestEx.class); + + final HttpServletResponseEx responseEx = Mockito.mock(HttpServletResponseEx.class); + + final OperationLocator locator = Mockito.mock(OperationLocator.class); + + final InvocationRuntimeType invocationRuntimeType = Mockito.mock(InvocationRuntimeType.class); + + final OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + + final SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); + + RestProducerInvocationCreator creator; + + static SCBEngine engine; + + static Environment environment; + + @BeforeAll + public static void beforeClass() { + 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(); + } + + @BeforeEach + public void setUp() { + creator = new RestVertxProducerInvocationCreator(routingContext, microserviceMeta, endpoint, + requestEx, responseEx); + creator = Mockito.spy(creator); + } + + @Test + public void should_failed_when_not_defined_any_schema() { + try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { + mockedStatic.when(() -> ServicePathManager.getServicePathManager(null)).thenReturn(servicePathManager); + + InvocationException throwable = (InvocationException) catchThrowable(() -> creator.createAsync().join()); + CommonExceptionData data = (CommonExceptionData) throwable.getErrorData(); + + assertThat(throwable.getStatusCode()).isEqualTo(NOT_FOUND.getStatusCode()); + assertThat(Json.encode(data)).isIn("{\"code\":\"SCB.00000002\",\"message\":\"Not Found\"}", + "{\"message\":\"Not Found\",\"code\":\"SCB.00000002\"}"); + } + } + + @Test + public void should_save_requestEx_in_invocation_context() { + try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { + 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); + + Invocation invocation = creator.createAsync().join(); + + Object request = invocation.getLocalContext(RestConst.REST_REQUEST); + assertThat(request).isSameAs(requestEx); + } + } + + @Test + public void should_save_path_var_map_in_requestEx() { + try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { + 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); + + creator.createAsync().join(); + + Mockito.verify(requestEx, Mockito.times(1)).setAttribute(Mockito.eq(RestConst.PATH_PARAMETERS), Mockito.any()); + } + } + + @Test + 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); + 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.when(requestEx.getHeader(CoreConst.CSE_CONTEXT)).thenReturn("{\"k\":\"v\"}"); + + Invocation invocation = creator.createAsync().join(); + + assertThat(invocation.getContext("k")).isEqualTo("v"); + } + } + + @Test + 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); + 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.when(requestEx.getHeader(CoreConst.CSE_CONTEXT)).thenReturn("{\"k\":\"v\"}"); + + Invocation invocation = creator.createAsync().join(); + + assertThat(invocation.getContext("k")).isNull(); + } + } +} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/TestDefPath.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/TestDefPath.java new file mode 100644 index 00000000000..e6b1f4bcf76 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/TestDefPath.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.common.rest; + +import org.apache.servicecomb.common.rest.definition.path.PathRegExp; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestDefPath { + + @Test + public void testPathRegExp() throws Exception { + PathRegExp oPathRegExp = new PathRegExp("//{test}//"); + Assertions.assertEquals(1, oPathRegExp.getGroupCount()); + Assertions.assertEquals(0, oPathRegExp.getGroupWithRegExpCount()); + PathRegExp oSecondPathRegExp = new PathRegExp("{[^/:]+?}"); + Assertions.assertEquals(1, oSecondPathRegExp.getGroupCount()); + Assertions.assertEquals(1, oSecondPathRegExp.getGroupWithRegExpCount()); + Assertions.assertEquals("test/", PathRegExp.ensureEndWithSlash("test/")); + Assertions.assertEquals("test/", PathRegExp.ensureEndWithSlash("test")); + Assertions.assertNull(oSecondPathRegExp.match("{test/test}", null)); + } +} 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 new file mode 100644 index 00000000000..fa619acb725 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/TestRestEngineSchemaListener.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.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.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.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; + + static ServicePathManager spm; + + @BeforeAll + public static 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); + 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()); + } + + @AfterAll + public static void teardown() { + scbEngine.destroy(); + } + + @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()); + } + + @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()); + } + + @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()); + } + + @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()); + } + + @Test + public void testLocateStaticFound() { + Assertions.assertNotNull(spm.producerLocateOperation("/staticEx", "GET")); + } + + @Test + public void testLocateDynamicFound() { + OperationLocator locator = spm.producerLocateOperation("/dynamic/1", "GET"); + Assertions.assertEquals("1", locator.getPathVarMap().get("id")); + } +} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/PojoModel.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/PojoModel.java new file mode 100644 index 00000000000..982e6314ad7 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/PojoModel.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.common.rest.codec; + +public class PojoModel { + private String name; + + private String desc; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } +} 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 new file mode 100644 index 00000000000..c134cb46558 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.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.common.rest.codec; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.common.rest.codec.param.ParamValueProcessor; +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; +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 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 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"); + 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); + } + + @AfterAll + public static void afterClass() { + restOperation = null; + paramList.clear(); + } + + @Test + public void testRestToArgs() throws Exception { + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + RestOperationMeta restOperation = Mockito.mock(RestOperationMeta.class); + RestParam restParam = Mockito.mock(RestParam.class); + ParamValueProcessor processor = Mockito.mock(ParamValueProcessor.class); + List params = new ArrayList<>(); + params.add(restParam); + String s = "my"; + Mockito.when(restOperation.getParamList()).thenReturn(params); + Mockito.when(restParam.getParamProcessor()).thenReturn(processor); + Mockito.when(processor.getValue(request)).thenReturn(s); + Mockito.when(restParam.getParamName()).thenReturn("test"); + + Map xx = RestCodec.restToArgs(request, restOperation); + Assertions.assertEquals(xx.get("test"), s); + } + + @Test + public void testRestToArgsException() throws Exception { + ParamValueProcessor processor = Mockito.mock(ParamValueProcessor.class); + Mockito.when(processor.getValue(Mockito.any())).thenThrow(new Exception("bad request parame")); + + RestOperationMeta restOperation = Mockito.mock(RestOperationMeta.class); + RestParam restParam = Mockito.mock(RestParam.class); + Mockito.when(restParam.getParamProcessor()).thenReturn(processor); + + List params = new ArrayList<>(); + params.add(restParam); + Mockito.when(restOperation.getParamList()).thenReturn(params); + + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + Mockito.when(restOperation.getOperationMeta()).thenReturn(operationMeta); + + boolean success = false; + try { + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + RestCodec.restToArgs(request, restOperation); + success = true; + } catch (InvocationException e) { + Assertions.assertEquals(400, e.getStatusCode()); + Assertions.assertTrue(e.getMessage().contains("Parameter is not valid")); + } + Assertions.assertFalse(success); + } + + @Test + public void testRestToArgsInstanceException() throws Exception { + InvocationException exception = new InvocationException(Status.BAD_REQUEST, "Parameter is not valid."); + ParamValueProcessor processor = Mockito.mock(ParamValueProcessor.class); + Mockito.when(processor.getValue(Mockito.any())).thenThrow(exception); + + RestOperationMeta restOperation = Mockito.mock(RestOperationMeta.class); + RestParam restParam = Mockito.mock(RestParam.class); + Mockito.when(restParam.getParamProcessor()).thenReturn(processor); + + List params = new ArrayList<>(); + params.add(restParam); + Mockito.when(restOperation.getParamList()).thenReturn(params); + + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + Mockito.when(restOperation.getOperationMeta()).thenReturn(operationMeta); + + boolean success = false; + try { + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + RestCodec.restToArgs(request, restOperation); + 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 new file mode 100644 index 00000000000..57da9440e4f --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestObjectMapper.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.io.ByteArrayInputStream; +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; + +import com.fasterxml.jackson.core.JsonParser.Feature; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.vertx.core.json.JsonObject; +import org.junit.jupiter.api.Test; + +public class TestRestObjectMapper { + + @Test + 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); + String dateStr = mapper.writeValueAsString(date); + Assertions.assertEquals(dateStr, "\"2017-07-21T17:32:28.000+00:00\""); + + date = mapper.readValue("\"2017-07-21T17:32:28.320+0100\"".getBytes(), Date.class); + dateStr = mapper.writeValueAsString(date); + Assertions.assertEquals(dateStr, "\"2017-07-21T16:32:28.320+00:00\""); // one hour later + } + + @Test + public void testAutoCloseSource() { + Assertions.assertFalse(RestObjectMapperFactory.getRestObjectMapper().getFactory().isEnabled(Feature.AUTO_CLOSE_SOURCE)); + } + + @Test + public void testDeserializationFeature() { + Assertions.assertFalse( + RestObjectMapperFactory.getRestObjectMapper().isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); + } + + @Test + public void testDefaultViewInclusionFeature() { + Assertions.assertFalse(RestObjectMapperFactory.getRestObjectMapper().isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)); + } + + @Test + public void testJsonObjectWork() { + JsonObject obj = new JsonObject(); + obj.put("name", "a"); + obj.put("desc", "b"); + PojoModel model = RestObjectMapperFactory.getRestObjectMapper() + .convertValue(obj, TypeFactory.defaultInstance().constructType(PojoModel.class)); + Assertions.assertEquals("a", model.getName()); + Assertions.assertEquals("b", model.getDesc()); + + RestObjectMapperFactory.setDefaultRestObjectMapper(new RestObjectMapper()); + model = RestObjectMapperFactory.getRestObjectMapper() + .convertValue(obj, TypeFactory.defaultInstance().constructType(PojoModel.class)); + Assertions.assertEquals("a", model.getName()); + Assertions.assertEquals("b", model.getDesc()); + + InputStream inputStream = new ByteArrayInputStream(new byte[0]); + try { + RestObjectMapperFactory.getRestObjectMapper().readValue(inputStream, PojoModel.class); + Assertions.fail(); + } catch (MismatchedInputException e) { + // right place, nothing to do. + } catch (Exception e) { + 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 new file mode 100644 index 00000000000..361a3844d4c --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/fix/TestDoSFix.java @@ -0,0 +1,245 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.fix; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.util.concurrent.Callable; + +import org.apache.servicecomb.foundation.common.utils.RestObjectMapper; +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; +import org.junit.jupiter.api.Assertions; + +import com.fasterxml.jackson.core.exc.InputCoercionException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import com.google.common.base.Strings; +import org.junit.jupiter.api.Test; + +public class TestDoSFix { + static final ObjectMapper mapper = new RestObjectMapper(); + + static final String invalidNum = Strings.repeat("9", 1000); + + static final String invalidStr = "\"" + invalidNum + "\""; + + static final String invalidArrNum = "[" + invalidNum + "]"; + + static final String invalidArrStr = "[\"" + invalidNum + "\"]"; + + public static class Model { + public Color color; + + public char cValue; + + public Character cObjValue; + + public byte bValue; + + public Byte bObjValue; + + public short sValue; + + public Short sObjValue; + + public int iValue; + + public Integer iObjValue; + + public long lValue; + + public Long lObjValue; + + public float fValue; + + public Float fObjValue; + + public double dValue; + + public Double dObjValue; + } + + void fastFail(Callable callable, Class eCls) { + long start = System.currentTimeMillis(); + try { + Object ret = callable.call(); + Assertions.fail("expect failed, but success to be " + ret); + } catch (AssertionError e) { + throw e; + } catch (Throwable e) { + if (eCls != e.getClass()) { + e.printStackTrace(); + } + Assertions.assertEquals(eCls, e.getClass()); + } + + long time = System.currentTimeMillis() - start; + Assertions.assertTrue(time < 1000, "did not fix DoS problem, time:" + time); + } + + void fastFail(String input, Class cls, Class eCls) { + fastFail(() -> mapper.readValue(input, cls), eCls); + + fastFail(() -> mapper.readValue(new ByteArrayInputStream(input.getBytes()), cls), eCls); + } + + void batFastFail(Class cls, Class e1, Class e2) { + fastFail(invalidNum, cls, e1); + fastFail(invalidStr, cls, e2); + fastFail(invalidArrNum, cls, e1); + fastFail(invalidArrStr, cls, e2); + } + + void batFastFail(Class cls) { + batFastFail(cls, InputCoercionException.class, InvalidFormatException.class); + } + + void batFastFail(String fieldName, Class e1, Class e2) { + fastFail("{\"" + fieldName + "\":" + invalidNum + "}", Model.class, e1); + fastFail("{\"" + fieldName + "\":\"" + invalidNum + "\"}", Model.class, e2); + fastFail("{\"" + fieldName + "\":[" + invalidNum + "]}", Model.class, e1); + fastFail("{\"" + fieldName + "\":[\"" + invalidNum + "\"]}", Model.class, e2); + } + + void batFastFail(String fieldName) { + batFastFail(fieldName, JsonMappingException.class, InvalidFormatException.class); + } + + @Test + public void testEnum() { + batFastFail(Color.class); + batFastFail("color"); + } + + @Test + public void testChar() { + batFastFail(char.class, InputCoercionException.class, InvalidFormatException.class); + batFastFail(Character.class, InputCoercionException.class, InvalidFormatException.class); + + batFastFail("cValue", JsonMappingException.class, InvalidFormatException.class); + batFastFail("cObjValue", JsonMappingException.class, InvalidFormatException.class); + } + + @Test + public void testByte() { + batFastFail(byte.class); + batFastFail(Byte.class); + + batFastFail("bValue"); + batFastFail("bObjValue"); + } + + @Test + public void testShort() { + batFastFail(short.class); + batFastFail(Short.class); + + batFastFail("sValue"); + batFastFail("sObjValue"); + } + + @Test + public void testInt() { + batFastFail(int.class); + batFastFail(Integer.class); + + batFastFail("iValue"); + batFastFail("iObjValue"); + } + + @Test + public void testLong() { + batFastFail(long.class); + batFastFail(Long.class); + + batFastFail("lValue"); + batFastFail("lObjValue"); + } + + Object fastSucc(Callable callable) { + long start = System.currentTimeMillis(); + try { + Object ret = callable.call(); + Assertions.assertTrue(System.currentTimeMillis() - start < 1000); + return ret; + } catch (Throwable e) { + throw new IllegalStateException(e); + } + } + + Object fastSucc(String input, Class cls) { + return fastSucc(() -> mapper.readValue(input, cls)); + } + + Object fastSucc(InputStream input, Class cls) { + return fastSucc(() -> { + input.reset(); + return mapper.readValue(input, cls); + }); + } + + void batFastSucc(Class cls, Object expected) { + Assertions.assertEquals(expected, fastSucc(invalidNum, cls)); + Assertions.assertEquals(expected, fastSucc(new ByteArrayInputStream(invalidNum.getBytes()), cls)); + + Assertions.assertEquals(expected, fastSucc(invalidStr, cls)); + Assertions.assertEquals(expected, fastSucc(new ByteArrayInputStream(invalidStr.getBytes()), cls)); + + Assertions.assertEquals(expected, fastSucc(invalidArrNum, cls)); + Assertions.assertEquals(expected, fastSucc(new ByteArrayInputStream(invalidArrNum.getBytes()), cls)); + + Assertions.assertEquals(expected, fastSucc(invalidArrStr, cls)); + Assertions.assertEquals(expected, fastSucc(new ByteArrayInputStream(invalidArrStr.getBytes()), cls)); + } + + void checkField(Model model, String fieldName, Object expected) { + try { + Field field = Model.class.getField(fieldName); + Object value = field.get(model); + Assertions.assertEquals(expected, value); + } catch (Throwable e) { + throw new IllegalStateException(e); + } + } + + void batFastSucc(String fieldName, Object expected) { + checkField((Model) fastSucc("{\"" + fieldName + "\":" + invalidNum + "}", Model.class), fieldName, expected); + checkField((Model) fastSucc("{\"" + fieldName + "\":\"" + invalidNum + "\"}", Model.class), fieldName, expected); + checkField((Model) fastSucc("{\"" + fieldName + "\":[" + invalidNum + "]}", Model.class), fieldName, expected); + checkField((Model) fastSucc("{\"" + fieldName + "\":[\"" + invalidNum + "\"]}", Model.class), fieldName, expected); + } + + @Test + public void testFloat() { + batFastSucc(float.class, Float.POSITIVE_INFINITY); + batFastSucc(Float.class, Float.POSITIVE_INFINITY); + + batFastSucc("fValue", Float.POSITIVE_INFINITY); + batFastSucc("fObjValue", Float.POSITIVE_INFINITY); + } + + @Test + public void testDouble() { + batFastSucc(double.class, Double.POSITIVE_INFINITY); + batFastSucc(Double.class, Double.POSITIVE_INFINITY); + + batFastSucc("dValue", Double.POSITIVE_INFINITY); + batFastSucc("dObjValue", Double.POSITIVE_INFINITY); + } +} 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 new file mode 100644 index 00000000000..ab71e3a9e99 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java @@ -0,0 +1,294 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +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.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); + + MultiMap headers; + + final RestClientRequest clientRequest = Mockito.mock(RestClientRequest.class); + + ParamValueProcessor processor; + + final Buffer inputBodyByteBuf = Buffer.buffer(); + + final BufferInputStream inputStream = new BufferInputStream(inputBodyByteBuf); + + Buffer outputBodyBuffer; + + String value; + + private void createProcessor(Class type) { + 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); + } + + private void createClientRequest() { + Mockito.doAnswer(invocation -> { + headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); + return null; + }).when(clientRequest).putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); + + Mockito.when(clientRequest.getHeaders()).thenReturn(headers); + } + + private void initInputStream() throws IOException { + Mockito.when(request.getInputStream()).thenReturn(inputStream); + } + + private void setupGetValue(Class type) throws IOException { + createProcessor(type); + initInputStream(); + } + + @BeforeEach + public void before() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) + .thenReturn(false); + headers = HeadersMultiMap.httpHeaders(); + value = "value"; + } + + @Test + public void testGetValueHaveAttr() throws Exception { + int body = 10; + createProcessor(String.class); + Mockito.when(request.getAttribute(RestConst.BODY_PARAMETER)).thenReturn(body); + + Object result = processor.getValue(request); + Assertions.assertEquals("10", result); + } + + @Test + public void testGetValueNoAttrNoStream() throws Exception { + createProcessor(String.class); + Mockito.when(request.getInputStream()).thenReturn(null); + Assertions.assertThrows(InvocationException.class, () -> processor.getValue(request)); + } + + @Test + public void testGetValueTextPlain() throws Exception { + setupGetValue(String.class); + inputBodyByteBuf.appendString("abc", StandardCharsets.UTF_8.toString()); + + Mockito.when(request.getContentType()).thenReturn(MediaType.TEXT_PLAIN); + + Assertions.assertEquals("abc", processor.getValue(request)); + } + + @Test + public void testGetValueContextTypeJson() throws Exception { + setupGetValue(Integer.class); + inputBodyByteBuf.appendString("\"1\"", StandardCharsets.UTF_8.toString()); + + Mockito.when(request.getContentType()).thenReturn(MediaType.APPLICATION_JSON); + + Assertions.assertEquals(1, processor.getValue(request)); + } + + @Test + public void testGetValueDefaultJson() throws Exception { + setupGetValue(Integer.class); + inputBodyByteBuf.appendString("\"1\"", StandardCharsets.UTF_8.toString()); + + Assertions.assertEquals(1, processor.getValue(request)); + } + + @Test + public void testSetValue() throws Exception { + createClientRequest(); + createProcessor(String.class); + + Mockito.when(clientRequest.getHeaders()).thenReturn(headers); + ParamValueProcessor spy = Mockito.spy(processor); + Mockito.doAnswer(invocation -> { + outputBodyBuffer = new BufferImpl().appendBytes((value).getBytes(StandardCharsets.UTF_8)); + return null; + }).when(spy).setValue(clientRequest, value); + + spy.setValue(clientRequest, value); + processor.setValue(clientRequest, value); + Assertions.assertEquals(MediaType.APPLICATION_JSON, headers.get(HttpHeaders.CONTENT_TYPE)); + Assertions.assertEquals(value, outputBodyBuffer.toString()); + } + + @Test + public void testSetValueTextPlain() throws Exception { + createClientRequest(); + createProcessor(String.class); + headers.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN); + + ParamValueProcessor spy = Mockito.spy(processor); + Mockito.doAnswer(invocation -> { + outputBodyBuffer = new BufferImpl().appendBytes((value).getBytes(StandardCharsets.UTF_8)); + return null; + }).when(spy).setValue(clientRequest, value); + + spy.setValue(clientRequest, value); + processor.setValue(clientRequest, value); + Assertions.assertEquals(MediaType.TEXT_PLAIN, headers.get(HttpHeaders.CONTENT_TYPE)); + Assertions.assertEquals(value, outputBodyBuffer.toString()); + } + + + @Test + public void testGetParameterPath() { + createProcessor(String.class); + Assertions.assertEquals("", processor.getParameterPath()); + } + + @Test + public void testGetProcessorType() { + createProcessor(String.class); + Assertions.assertEquals("body", processor.getProcessorType()); + } + + @Test + public void testGetValueRawJson() throws Exception { + createRawJsonProcessor(); + initInputStream(); + inputBodyByteBuf.appendString("\"1\"", StandardCharsets.UTF_8.toString()); + + Assertions.assertEquals("\"1\"", processor.getValue(request)); + } + + @Test + public void testGetValueRawJsonHaveAttr() throws Exception { + int body = 10; + createRawJsonProcessor(); + Mockito.when(request.getAttribute(RestConst.BODY_PARAMETER)).thenReturn(body); + + Object result = processor.getValue(request); + Assertions.assertEquals("10", result); + } + + @Test + public void testGetValueRawJsonNoAttrNoStream() throws Exception { + createRawJsonProcessor(); + Mockito.when(request.getInputStream()).thenReturn(null); + + Object result = processor.getValue(request); + Assertions.assertNull(result); + } + + @Test + public void testSetValueRawJson() throws Exception { + createClientRequest(); + createRawJsonProcessor(); + + ParamValueProcessor spy = Mockito.spy(processor); + Mockito.doAnswer(invocation -> { + outputBodyBuffer = new BufferImpl().appendBytes((value).getBytes(StandardCharsets.UTF_8)); + return null; + }).when(spy).setValue(clientRequest, value); + spy.setValue(clientRequest, value); + processor.setValue(clientRequest, value); + Assertions.assertEquals(MediaType.APPLICATION_JSON, headers.get(HttpHeaders.CONTENT_TYPE)); + Assertions.assertEquals("value", outputBodyBuffer.toString()); + } + + static class BodyModel { + public String name; + + public int age; + } + + @Test + public void convertFromFormData() throws Exception { + createProcessor(BodyModel.class); + Map parameterMap = new HashMap<>(); + parameterMap.put("name", new String[] {"n"}); + parameterMap.put("age", new String[] {"10"}); + Mockito.when(request.getParameterMap()).thenReturn(parameterMap); + Mockito.when(request.getContentType()).thenReturn(MediaType.MULTIPART_FORM_DATA + ";utf-8"); + + BodyModel bm = (BodyModel) processor.getValue(request); + Assertions.assertEquals("n", bm.name); + Assertions.assertEquals(10, bm.age); + } + + @Test + public void convertFromUrlencoded() throws Exception { + createProcessor(BodyModel.class); + Map parameterMap = new HashMap<>(); + parameterMap.put("name", new String[] {"n"}); + parameterMap.put("age", new String[] {"10"}); + Mockito.when(request.getParameterMap()).thenReturn(parameterMap); + Mockito.when(request.getContentType()).thenReturn(MediaType.APPLICATION_FORM_URLENCODED + ";utf-8"); + + BodyModel bm = (BodyModel) processor.getValue(request); + Assertions.assertEquals("n", bm.name); + Assertions.assertEquals(10, bm.age); + } +} 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 new file mode 100644 index 00000000000..22d7e8d39db --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessorCreator.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 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); + 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(null, null, param, String.class); + + Assertions.assertEquals(BodyProcessor.class, processor.getClass()); + } + + @Test + public void testCreateRawJson() { + ParamValueProcessorCreator creator = + ParamValueProcessorCreatorManager.INSTANCE.findValue(BodyProcessorCreator.PARAM_TYPE); + 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(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 new file mode 100644 index 00000000000..10cad81b4f4 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessor.java @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Date; +import java.util.HashMap; +import java.util.Map; + +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; +import org.apache.servicecomb.common.rest.codec.param.CookieProcessorCreator.CookieProcessor; +import org.junit.jupiter.api.Assertions; + +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.StdDateFormat; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestCookieProcessor { + final HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + + final Map cookies = new HashMap<>(); + + final RestClientRequest clientRequest = Mockito.mock(RestClientRequest.class); + + final String cookieName = "v1"; + + final String cookieValue = "c1v"; + + private CookieProcessor createProcessor(String name, Class type) { + return new CookieProcessor(name, TypeFactory.defaultInstance().constructType(type), null, true); + } + + private CookieProcessor createProcessor(String name, Class type, String defaultValue, boolean required) { + return new CookieProcessor(name, TypeFactory.defaultInstance().constructType(type), defaultValue, required); + } + + private void createClientRequest() { + Mockito.doAnswer(invocation -> { + cookies.put(cookieName, cookieValue); + return null; + }).when(clientRequest).addCookie(cookieName, cookieValue); + } + + @Test + public void testGetValueNoCookies() throws Exception { + Mockito.when(request.getCookies()).thenReturn(null); + + CookieProcessor processor = createProcessor(cookieName, String.class, null, false); + Object value = processor.getValue(request); + Assertions.assertNull(value); + } + + @Test + public void testNoCookieAndRequired() throws Exception { + Mockito.when(request.getCookies()).thenReturn(null); + + CookieProcessor processor = createProcessor(cookieName, String.class, null, 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.")); + } + } + + @Test + public void testGetValueCookiesNotFound() throws Exception { + Cookie[] cookies = new Cookie[] {new Cookie(cookieName, cookieValue)}; + Mockito.when(request.getCookies()).thenReturn(cookies); + + CookieProcessor processor = createProcessor("c2", String.class, null, false); + Object value = processor.getValue(request); + Assertions.assertNull(value); + } + + @Test + public void testGetValueCookiesFound() throws Exception { + Cookie[] cookies = new Cookie[] {new Cookie(cookieName, cookieValue)}; + Mockito.when(request.getCookies()).thenReturn(cookies); + + CookieProcessor processor = createProcessor(cookieName, String.class); + Object value = processor.getValue(request); + Assertions.assertEquals(cookieValue, value); + } + + @Test + public void testGetValueRequiredTrue() throws Exception { + Cookie[] cookies = new Cookie[] {new Cookie(cookieName, null)}; + Mockito.when(request.getCookies()).thenReturn(cookies); + + CookieProcessor processor = createProcessor(cookieName, String.class, null, 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.")); + } + } + + @Test + public void testGetValueRequiredFalse() throws Exception { + Cookie[] cookies = new Cookie[] {new Cookie(cookieName, null)}; + Mockito.when(request.getCookies()).thenReturn(cookies); + + CookieProcessor processor = createProcessor(cookieName, String.class, "test", false); + Object result = processor.getValue(request); + Assertions.assertEquals("test", result); + } + + @SuppressWarnings("deprecation") + @Test + public void testGetValueCookiesDate() throws Exception { + Date date = new Date(); + String strDate = com.fasterxml.jackson.databind.util.ISO8601Utils.format(date); + Cookie[] cookies = new Cookie[] {new Cookie(cookieName, strDate)}; + Mockito.when(request.getCookies()).thenReturn(cookies); + + CookieProcessor processor = createProcessor(cookieName, Date.class); + Object value = processor.getValue(request); + Assertions.assertEquals(strDate, com.fasterxml.jackson.databind.util.ISO8601Utils.format((Date) value)); + } + + @Test + public void testSetValue() throws Exception { + createClientRequest(); + + CookieProcessor processor = createProcessor(cookieName, String.class); + processor.setValue(clientRequest, cookieValue); + Assertions.assertEquals(cookieValue, cookies.get(cookieName)); + } + + @Test + public void testSetValueDateFixed() throws Exception { + Date date = new Date(1586957400199L); + String strDate = "2020-04-15T13:30:00.199+00:00"; + + String cookieValue = RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date); + Mockito.doAnswer(invocation -> { + cookies.put("h1", cookieValue); + return null; + }).when(clientRequest).addCookie("h1", cookieValue); + CookieProcessor processor = createProcessor("h1", Date.class); + processor.setValue(clientRequest, date); + Assertions.assertEquals(strDate, cookies.get("h1")); + } + + @Test + public void testSetValueDate() throws Exception { + Date date = new Date(); + + String strDate = new StdDateFormat().format(date); + + String cookieValue = RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date); + Mockito.doAnswer(invocation -> { + cookies.put("h1", cookieValue); + return null; + }).when(clientRequest).addCookie("h1", cookieValue); + CookieProcessor processor = createProcessor("h1", Date.class); + processor.setValue(clientRequest, date); + Assertions.assertEquals(strDate, cookies.get("h1")); + } + + @Test + public void testGetProcessorType() { + CookieProcessor processor = createProcessor(cookieName, String.class); + Assertions.assertEquals("cookie", processor.getProcessorType()); + } +} 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 new file mode 100644 index 00000000000..d3769dcb2c5 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessorCreator.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.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 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"); + 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 new file mode 100644 index 00000000000..42aa5343bef --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestFormProcessor.java @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.reflect.Type; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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.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.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); + + final Map forms = new HashMap<>(); + + final RestClientRequest clientRequest = Mockito.mock(RestClientRequest.class); + + private FormProcessor createProcessor(String name, Type type) { + return createProcessor(name, type, null, true); + } + + private FormProcessor createProcessor(String name, Type type, String defaultValue, boolean required) { + JavaType javaType = TypeFactory.defaultInstance().constructType(type); + + 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()) { + 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); + } + + 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.BODY_PARAMETER)).thenReturn(forms); + + ParamValueProcessor processor = createProcessor("name", String.class); + Object value = processor.getValue(request); + Assertions.assertEquals("value", value); + } + + @Test + public void testGetValueNormal() throws Exception { + Mockito.when(request.getParameter("name")).thenReturn("value"); + + ParamValueProcessor processor = createProcessor("name", String.class); + Object value = processor.getValue(request); + Assertions.assertEquals("value", value); + } + + @SuppressWarnings("deprecation") + @Test + public void testGetValueNormalDate() throws Exception { + Date date = new Date(); + String strDate = com.fasterxml.jackson.databind.util.ISO8601Utils.format(date); + Mockito.when(request.getParameter("name")).thenReturn(strDate); + + ParamValueProcessor processor = createProcessor("name", Date.class); + Object value = processor.getValue(request); + Assertions.assertEquals(strDate, com.fasterxml.jackson.databind.util.ISO8601Utils.format((Date) value)); + } + + @Test + public void testGetValueContainerTypeNull() throws Exception { + Mockito.when(request.getParameterValues("name")).thenReturn(null); + + ParamValueProcessor processor = createProcessor("name", String[].class, null, false); + String[] value = (String[]) processor.getValue(request); + Assertions.assertNull(value); + } + + @Test + public void testGetValueNull() throws Exception { + Mockito.when(request.getParameter("name")).thenReturn(null); + + ParamValueProcessor processor = createProcessor("name", String.class, null, 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.")); + } + } + + @Test + public void testGetValueArray() throws Exception { + Mockito.when(request.getParameterValues("name")).thenReturn(new String[] {"value"}); + + ParamValueProcessor processor = createProcessor("name", String[].class); + String[] value = (String[]) processor.getValue(request); + MatcherAssert.assertThat(value, Matchers.arrayContaining("value")); + } + + @SuppressWarnings("unchecked") + @Test + public void testGetValueList() throws Exception { + Mockito.when(request.getParameterValues("name")).thenReturn(new String[] {"value"}); + + ParamValueProcessor processor = createProcessor("name", + TypeFactory.defaultInstance().constructCollectionType(List.class, String.class), + null, true); + Object value = processor.getValue(request); + MatcherAssert.assertThat((List) value, Matchers.contains("value")); + } + + @SuppressWarnings("unchecked") + @Test + public void testGetValueSet() throws Exception { + Mockito.when(request.getParameterValues("name")).thenReturn(new String[] {"value"}); + + ParamValueProcessor processor = createProcessor("name", + TypeFactory.defaultInstance().constructCollectionType(Set.class, String.class), null, + true); + Object value = processor.getValue(request); + MatcherAssert.assertThat((Set) value, Matchers.contains("value")); + } + + @Test + public void testSetValue() throws Exception { + Mockito.doAnswer(invocation -> { + forms.put("name", "value"); + return null; + }).when(clientRequest).addForm("name", "value"); + + ParamValueProcessor processor = createProcessor("name", String.class); + processor.setValue(clientRequest, "value"); + Assertions.assertEquals("value", forms.get("name")); + } + + @Test + public void testSetValueDate() throws Exception { + Date date = new Date(); + + Mockito.doAnswer(invocation -> { + forms.put("name", date); + return null; + }).when(clientRequest).addForm("name", date); + ParamValueProcessor processor = createProcessor("name", Date.class); + processor.setValue(clientRequest, date); + Assertions.assertSame(date, forms.get("name")); + } + + @Test + public void testGetProcessorType() { + ParamValueProcessor processor = createProcessor("name", String.class); + Assertions.assertEquals("formData", processor.getProcessorType()); + } +} 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 new file mode 100644 index 00000000000..f2b7b9c393e --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestFormProcessorCreator.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.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 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); + 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(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 new file mode 100644 index 00000000000..39bed2e809b --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessor.java @@ -0,0 +1,227 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.reflect.Type; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +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.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); + + final Map headers = new HashMap<>(); + + final RestClientRequest clientRequest = Mockito.mock(RestClientRequest.class); + + private HeaderProcessor createProcessor(String name, Type type) { + return createProcessor(name, type, null, true); + } + + private HeaderProcessor createProcessor(String name, Type type, String defaultValue, boolean required) { + JavaType javaType = TypeFactory.defaultInstance().constructType(type); + + HeaderParameter headerParameter = new HeaderParameter(); + headerParameter.setSchema(new Schema()); + headerParameter.name(name) + .required(required); + headerParameter.getSchema().setDefault(defaultValue); + + if (javaType.isContainerType()) { + 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"); + + HeaderProcessor processor = createProcessor("h1", String.class); + Object value = processor.getValue(request); + Assertions.assertEquals("h1v", value); + } + + @SuppressWarnings("deprecation") + @Test + public void testGetValueNormalDate() throws Exception { + Date date = new Date(); + String strDate = com.fasterxml.jackson.databind.util.ISO8601Utils.format(date); + Mockito.when(request.getHeader("h1")).thenReturn(strDate); + + HeaderProcessor processor = createProcessor("h1", Date.class); + Object value = processor.getValue(request); + Assertions.assertEquals(strDate, com.fasterxml.jackson.databind.util.ISO8601Utils.format((Date) value)); + } + + @Test + public void testGetValueContainerTypeNull() throws Exception { + Mockito.when(request.getHeader("h1")).thenReturn(null); + + HeaderProcessor processor = createProcessor("h1", String[].class, null, false); + String[] value = (String[]) processor.getValue(request); + Assertions.assertNull(value); + } + + @Test + public void testGetValueRequiredTrue() throws Exception { + Mockito.when(request.getHeader("h1")).thenReturn(null); + + HeaderProcessor processor = createProcessor("h1", String.class); + 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.")); + } + } + + @Test + public void testGetValueRequiredFalse() throws Exception { + Mockito.when(request.getHeader("h1")).thenReturn(null); + + HeaderProcessor processor = createProcessor("h1", String.class, "test", false); + Object value = processor.getValue(request); + Assertions.assertEquals("test", value); + } + + @Test + public void testGetValueArray() throws Exception { + Mockito.when(request.getHeaders("h1")).thenReturn(Collections.enumeration(Arrays.asList("h1v"))); + + HeaderProcessor processor = createProcessor("h1", String[].class); + String[] value = (String[]) processor.getValue(request); + MatcherAssert.assertThat(value, Matchers.arrayContaining("h1v")); + } + + @SuppressWarnings("unchecked") + @Test + public void testGetValueList() throws Exception { + Mockito.when(request.getHeaders("h1")).thenReturn(Collections.enumeration(Arrays.asList("h1v"))); + + HeaderProcessor processor = createProcessor("h1", + TypeFactory.defaultInstance().constructCollectionType(List.class, String.class), + null, true); + Object value = processor.getValue(request); + MatcherAssert.assertThat((List) value, Matchers.contains("h1v")); + } + + @SuppressWarnings("unchecked") + @Test + public void testGetValueSet() throws Exception { + Mockito.when(request.getHeaders("h1")).thenReturn(Collections.enumeration(Arrays.asList("h1v"))); + + HeaderProcessor processor = createProcessor("h1", + TypeFactory.defaultInstance().constructCollectionType(Set.class, String.class), + null, true); + Object value = processor.getValue(request); + MatcherAssert.assertThat((Set) value, Matchers.contains("h1v")); + } + + @Test + public void testSetValue() throws Exception { + Mockito.doAnswer(invocation -> { + headers.put("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString("h1v")); + return null; + }).when(clientRequest).putHeader("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString("h1v")); + + HeaderProcessor processor = createProcessor("h1", String.class); + processor.setValue(clientRequest, "h1v"); + Assertions.assertEquals("h1v", headers.get("h1")); + } + + @Test + public void testSetValueNull() throws Exception { + Mockito.doAnswer(invocation -> { + headers.put("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(null)); + return null; + }).when(clientRequest).putHeader("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(null)); + HeaderProcessor processor = createProcessor("h1", String.class); + processor.setValue(clientRequest, null); + Assertions.assertEquals(0, headers.size()); + } + + @Test + public void testSetValueDateFixed() throws Exception { + Date date = new Date(1586957400199L); + String strDate = "2020-04-15T13:30:00.199+00:00"; + + Mockito.doAnswer(invocation -> { + headers.put("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date)); + return null; + }).when(clientRequest).putHeader("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date)); + + HeaderProcessor processor = createProcessor("h1", Date.class); + processor.setValue(clientRequest, date); + Assertions.assertEquals(strDate, headers.get("h1")); + } + + @Test + public void testSetValueDate() throws Exception { + Date date = new Date(); + String strDate = new StdDateFormat().format(date); + Mockito.doAnswer(invocation -> { + headers.put("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date)); + return null; + }).when(clientRequest).putHeader("h1", RestObjectMapperFactory.getConsumerWriterMapper().convertToString(date)); + + HeaderProcessor processor = createProcessor("h1", Date.class); + processor.setValue(clientRequest, date); + Assertions.assertEquals(strDate, headers.get("h1")); + } + + @Test + public void testGetProcessorType() { + HeaderProcessor processor = createProcessor("h1", String.class); + Assertions.assertEquals("header", processor.getProcessorType()); + } +} 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 new file mode 100644 index 00000000000..d275b16b62e --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestHeaderProcessorCreator.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.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 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(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 new file mode 100644 index 00000000000..ff8218768cf --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestPathProcessor.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.param; + +import java.util.HashMap; +import java.util.Map; + +import jakarta.servlet.http.HttpServletRequest; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.common.rest.codec.param.PathProcessorCreator.PathProcessor; +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 TestPathProcessor { + final HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + + final Map pathVars = new HashMap<>(); + + ParamValueProcessor processor; + + private void createProcessor(String name, Class type) { + processor = new PathProcessor(name, TypeFactory.defaultInstance().constructType(type), null, true); + } + + private void prepareGetValue(String name, Class type) { + createProcessor(name, type); + Mockito.when(request.getAttribute(RestConst.PATH_PARAMETERS)).thenReturn(pathVars); + } + + @Test + public void testGetValueNoPathVars() throws Exception { + createProcessor("name", String.class); + + Assertions.assertNull(processor.getValue(request)); + } + + @Test + public void testGetValuePathNotFound() throws Exception { + prepareGetValue("name", String.class); + + Assertions.assertNull(processor.getValue(request)); + } + + @Test + public void testGetValuePathNormal() throws Exception { + prepareGetValue("name", String.class); + pathVars.put("name", "value"); + + Assertions.assertEquals("value", processor.getValue(request)); + } + + @Test + public void testGetSpaceEncoded() throws Exception { + prepareGetValue("name", String.class); + pathVars.put("name", "a%20b"); + + Assertions.assertEquals("a b", processor.getValue(request)); + } + + @Test + public void testGetPlus() throws Exception { + prepareGetValue("name", String.class); + pathVars.put("name", "a+b"); + + Assertions.assertEquals("a+b", processor.getValue(request)); + } + + @Test + public void testGetPercentage() throws Exception { + prepareGetValue("name", String.class); + pathVars.put("name", "%25%25"); + + Assertions.assertEquals("%%", processor.getValue(request)); + } + + @Test + public void testGetColon() throws Exception { + prepareGetValue("name", String.class); + pathVars.put("name", "aa:bb"); + Assertions.assertEquals("aa:bb", processor.getValue(request)); + } + + @Test + public void testGetProcessorType() { + createProcessor("name", String.class); + Assertions.assertEquals("path", processor.getProcessorType()); + } +} 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 new file mode 100644 index 00000000000..0f40291c82b --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestPathProcessorCreator.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.common.rest.codec.param; + +import org.apache.servicecomb.common.rest.codec.param.PathProcessorCreator.PathProcessor; +import org.junit.jupiter.api.Assertions; +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() { + ParamValueProcessorCreator creator = + ParamValueProcessorCreatorManager.INSTANCE.findValue(PathProcessorCreator.PARAMTYPE); + Parameter parameter = new PathParameter(); + parameter.setName("path"); + parameter.setSchema(new Schema()); + + 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 new file mode 100644 index 00000000000..be18fb8378b --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessor.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.common.rest.codec.param; + +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.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, + Parameter.StyleEnum style, boolean explode) { + return createProcessor(name, type, null, true, style, explode); + } + + private ParamValueProcessor createProcessor(String name, Class type, String defaultValue, boolean required, + Parameter.StyleEnum style, boolean explode) { + JavaType javaType = TypeFactory.defaultInstance().constructType(type); + + QueryParameter queryParameter = new QueryParameter(); + queryParameter.name(name) + .required(required) + .setSchema(new StringSchema()); + queryParameter.getSchema().setDefault(defaultValue); + + if (javaType.isContainerType()) { + queryParameter.setSchema(new ArraySchema()); + queryParameter.setExplode(explode); + queryParameter.setStyle(style); + } + return new QueryProcessor(queryParameter, javaType); + } + + @Test + public void testGetValueNormal() throws Exception { + Mockito.when(request.getParameter("name")).thenReturn("value"); + + ParamValueProcessor processor = createProcessor("name", String.class, StyleEnum.FORM, true); + Object value = processor.getValue(request); + Assertions.assertEquals("value", value); + } + + @Test + public void testGetValueContainerType() throws Exception { + Mockito.when(request.getParameterValues("name")).thenReturn(new String[] {"value", "value2"}); + + ParamValueProcessor processor = createProcessor("name", String[].class, StyleEnum.FORM, true); + String[] value = (String[]) processor.getValue(request); + MatcherAssert.assertThat(value, Matchers.arrayContaining("value", "value2")); + } + + @Test + public void testGetValueOnCollectionFormatIsCsv() throws Exception { + Mockito.when(request.getParameter("name")).thenReturn("value2,value3"); + + 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, StyleEnum.FORM, true); + Assertions.assertEquals("query", processor.getProcessorType()); + } + + @Test + public void testGetValueRequiredTrue() throws Exception { + Mockito.when(request.getParameter("name")).thenReturn(null); + + 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 name is required.")); + } + } + + @Test + public void testGetValueRequiredFalse() throws Exception { + Mockito.when(request.getParameter("name")).thenReturn(null); + + 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 new file mode 100644 index 00000000000..a75e1df2b2f --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestQueryProcessorCreator.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.common.rest.codec.param; + +import org.apache.servicecomb.common.rest.codec.param.QueryProcessorCreator.QueryProcessor; +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.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"); + parameter.setSchema(new Schema()); + ParamValueProcessor processor = creator.create(null, parameter.getName(), parameter, String.class); + + Assertions.assertEquals(QueryProcessor.class, processor.getClass()); + + String result = (String) processor.convertValue("Hello", TypeFactory.defaultInstance().constructType(String.class)); + Assertions.assertEquals("Hello", result); + + result = (String) processor.convertValue("", TypeFactory.defaultInstance().constructType(String.class)); + Assertions.assertEquals("", result); + + result = (String) processor.convertValue(null, TypeFactory.defaultInstance().constructType(String.class)); + Assertions.assertNull(result); + } + + @SuppressWarnings("UnusedAssignment") + @Test + public void testCreateNullAsEmpty() throws Exception { + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + 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(null, parameter.getName(), parameter, String.class); + + Assertions.assertEquals(QueryProcessor.class, processor.getClass()); + + Mockito.when(request.getParameter("query")).thenReturn("Hello"); + String result = (String) processor.getValue(request); + Assertions.assertEquals("Hello", result); + + Mockito.when(request.getParameter("query")).thenReturn(""); + result = (String) processor.getValue(request); + Assertions.assertNull(result); + + Mockito.when(request.getParameter("query")).thenReturn(null); + result = (String) processor.convertValue(null, TypeFactory.defaultInstance().constructType(String.class)); + result = (String) processor.getValue(request); + Assertions.assertNull(result); + } +} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceJsonProcessor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceJsonProcessor.java new file mode 100644 index 00000000000..db8e71cd5a9 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceJsonProcessor.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.common.rest.codec.produce; + +import static org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager.DEFAULT_SERIAL_CLASS; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Assertions; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.vertx.core.buffer.Buffer; +import org.junit.jupiter.api.Test; + +public class TestProduceJsonProcessor { + final ProduceProcessor pp = ProduceProcessorManager.INSTANCE.findDefaultJsonProcessor(); + + final JavaType stringType = TypeFactory.defaultInstance().constructType(String.class); + + @Test + public void testEncodeResponseNull() throws Exception { + Buffer buffer = pp.encodeResponse(null); + Assertions.assertNull(buffer); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + pp.encodeResponse(os, null); + Assertions.assertEquals(0, os.size()); + } + + @Test + public void testDecodeResponseNull() throws Exception { + JavaType resultType = TypeFactory.unknownType(); + Object result = pp.decodeResponse(Buffer.buffer(), resultType); + Assertions.assertNull(result); + + ByteArrayInputStream is = new ByteArrayInputStream(new byte[] {}); + try { + pp.decodeResponse(is, resultType); + Assertions.fail(); + } catch (Exception e) { + Assertions.assertTrue(e instanceof MismatchedInputException); + } + } + + @Test + public void testBufferNormal() throws Exception { + String value = "abc"; + Buffer buffer = pp.encodeResponse(value); + Assertions.assertEquals("\"abc\"", buffer.toString(StandardCharsets.UTF_8)); + + Object result = pp.decodeResponse(buffer, stringType); + Assertions.assertEquals(value, result); + } + + @Test + public void testStreamNormal() throws Exception { + String value = "abc"; + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + pp.encodeResponse(os, value); + Assertions.assertEquals("\"abc\"", os.toString(StandardCharsets.UTF_8.name())); + + ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); + Object result = pp.decodeResponse(is, stringType); + Assertions.assertEquals(value, result); + + os.close(); + is.close(); + } + + @Test + public void testSetSerializationView() { + Assertions.assertEquals(DEFAULT_SERIAL_CLASS, pp.getSerializationView()); + + pp.setSerializationView(null); + Assertions.assertEquals(DEFAULT_SERIAL_CLASS, pp.getSerializationView()); + + pp.setSerializationView(Object.class); + Assertions.assertEquals(Object.class.getCanonicalName(), pp.getSerializationView()); + } +} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceProcessorManager.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceProcessorManager.java new file mode 100644 index 00000000000..58dd3e6d856 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceProcessorManager.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.common.rest.codec.produce; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestProduceProcessorManager { + @Test + public void testDefault() { + Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultJsonProcessor(), + ProduceProcessorManager.INSTANCE.findDefaultProcessor()); + } +} 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 new file mode 100644 index 00000000000..d717072a27c --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceTextPlainProcessor.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.common.rest.codec.produce; + +import static org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager.DEFAULT_SERIAL_CLASS; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Assertions; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.vertx.core.buffer.Buffer; +import org.junit.jupiter.api.Test; + +public class TestProduceTextPlainProcessor { + final ProduceProcessor pp = ProduceProcessorManager.INSTANCE.findDefaultPlainProcessor(); + + final JavaType stringType = TypeFactory.defaultInstance().constructType(String.class); + + @Test + public void testEncodeResponseNull() throws Exception { + Buffer buffer = pp.encodeResponse(null); + Assertions.assertNull(buffer); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + pp.encodeResponse(os, null); + Assertions.assertEquals(0, os.size()); + } + + @Test + public void testdecodeResponseNull() throws Exception { + JavaType resultType = TypeFactory.unknownType(); + Object result = pp.decodeResponse(Buffer.buffer(), resultType); + Assertions.assertNull(result); + + ByteArrayInputStream is = new ByteArrayInputStream("\"\"".getBytes(StandardCharsets.UTF_8)); + result = pp.decodeResponse(is, resultType); + Assertions.assertEquals(result, ""); + } + + @Test + public void testBufferNormal() throws Exception { + String value = "abc"; + Buffer buffer = pp.encodeResponse(value); + Assertions.assertEquals(value, buffer.toString(StandardCharsets.UTF_8)); + + Object result = pp.decodeResponse(buffer, stringType); + Assertions.assertEquals(value, result); + } + + @Test + public void testStreamNormal() throws Exception { + String value = "abc"; + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + pp.encodeResponse(os, value); + Assertions.assertEquals(value, os.toString(StandardCharsets.UTF_8.name())); + + ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); + Object result = pp.decodeResponse(is, stringType); + Assertions.assertEquals(value, result); + + os.close(); + is.close(); + } + + @Test + public void testSetSerializationView() { + Assertions.assertEquals(DEFAULT_SERIAL_CLASS, pp.getSerializationView()); + + pp.setSerializationView(null); + Assertions.assertEquals(DEFAULT_SERIAL_CLASS, pp.getSerializationView()); + + pp.setSerializationView(Object.class); + 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 new file mode 100644 index 00000000000..890075cbe7a --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecCsvTest.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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(); + } + + @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%2Cv2", "v1", "v2"); + } + + @Test + void should_encode_common_numbers() throws Exception { + should_encode("?q=1%2C2", 1, 2); + } + + @Test + void should_encode_chinese_values() throws Exception { + should_encode("?q=%E4%B8%AD%E6%96%87%2Cv2", "中文", "v2"); + } + + @Test + void should_encode_ignore_null() throws Exception { + should_encode("?q=v1%2Cv2", "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,2", 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,,2", new int[] {1, 0, 2}); + } + + @Test + void should_decode_values_end_with_delimiter() { + should_decode("1,,", new int[] {1, 0, 0}); + } + + @Test + void should_decode_values_start_with_delimiter() { + should_decode(",,1", new int[] {0, 0, 1}); + } + } +} 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 new file mode 100644 index 00000000000..3c56d1b1ce6 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMultiTest.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.query; + +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(); + } + + @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&q=v2", "v1", "v2"); + } + + @Test + void should_encode_common_numbers() throws Exception { + should_encode("?q=1&q=2", 1, 2); + } + + @Test + void should_encode_chinese_values() throws Exception { + should_encode("?q=%E4%B8%AD%E6%96%87&q=v2", "中文", "v2"); + } + + @Test + void should_encode_ignore_null() throws Exception { + should_encode("?q=v1&q=v2", "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(new String[] {"1"}, new int[] {1}); + } + + @Test + void should_decode_common_values_to_array() { + should_decode(new String[] {"1", "2"}, new int[] {1, 2}); + } + + @Test + void should_decode_empty_string_to_array() { + should_decode(new String[] {""}, new int[] {0}); + } + + @Test + void should_decode_common_values_with_empty_string_to_array() { + should_decode(new String[] {"1", "", "2"}, new int[] {1, 0, 2}); + } + + @Test + void should_decode_single_value_to_single() { + should_decode("1", 1); + } + + @Test + void should_decode_null_to_single() { + should_decode((String) null, null); + } + } +} 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 new file mode 100644 index 00000000000..83f2b80062c --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecPipesTest.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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(); + } + + @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%7Cv2", "v1", "v2"); + } + + @Test + void should_encode_common_numbers() throws Exception { + should_encode("?q=1%7C2", 1, 2); + } + + @Test + void should_encode_chinese_values() throws Exception { + should_encode("?q=%E4%B8%AD%E6%96%87%7Cv2", "中文", "v2"); + } + + @Test + void should_encode_ignore_null() throws Exception { + should_encode("?q=v1%7Cv2", "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|2", 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||2", new int[] {1, 0, 2}); + } + + @Test + void should_decode_values_end_with_delimiter() { + should_decode("1||", new int[] {1, 0, 0}); + } + + @Test + void should_decode_values_start_with_delimiter() { + should_decode("||1", new int[] {0, 0, 1}); + } + } +} 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 new file mode 100644 index 00000000000..c6e521f6718 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecSsvTest.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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); + } + + @BeforeEach + void setUp() { + codec = new QueryCodecSsv(); + } + + @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+v2", "v1", "v2"); + } + + @Test + void should_encode_common_numbers() throws Exception { + should_encode("?q=1+2", 1, 2); + } + + @Test + void should_encode_chinese_values() throws Exception { + should_encode("?q=%E4%B8%AD%E6%96%87+v2", "中文", "v2"); + } + + @Test + void should_encode_ignore_null() throws Exception { + should_encode("?q=v1+v2", "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 2", 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 2", new int[] {1, 0, 2}); + } + + @Test + void should_decode_values_end_with_delimiter() { + should_decode("1 ", new int[] {1, 0, 0}); + } + + @Test + void should_decode_values_start_with_delimiter() { + should_decode(" 1", new int[] {0, 0, 1}); + } + } +} 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 new file mode 100644 index 00000000000..03347607b36 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecTestBase.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.query; + +import static org.assertj.core.api.Assertions.assertThat; + +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; +import org.apache.servicecomb.foundation.vertx.http.AbstractHttpServletRequest; + +import com.fasterxml.jackson.databind.type.TypeFactory; + +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; + + final String queryName = "q"; + + void should_encode(String encodedValue, Object... values) throws Exception { + URLPathStringBuilder builder = new URLPathStringBuilder(); + codec.encode(builder, queryName, values); + + assertThat(builder.build()).isEqualTo(encodedValue); + } + + void should_decode(String value, Object decodedValue) { + HttpServletRequest request = new AbstractHttpServletRequest() { + @Override + public String getParameter(String name) { + return value; + } + }; + + should_decode(request, decodedValue); + } + + void should_decode(String[] value, Object decodedValue) { + HttpServletRequest request = new AbstractHttpServletRequest() { + @Override + public String[] getParameterValues(String name) { + return value; + } + }; + + should_decode(request, decodedValue); + } + + private void should_decode(HttpServletRequest request, Object decodedValue) { + Class targetType = decodedValue == null ? Object.class : decodedValue.getClass(); + + QueryParameter queryParameter = new QueryParameter(); + queryParameter.setSchema(new Schema()); + queryParameter.getSchema().setFormat(codec.getCodecName()); + if (targetType.isArray()) { + queryParameter.setSchema(new ArraySchema()); + } + + QueryProcessor queryProcessor = new QueryProcessor(queryParameter, + TypeFactory.defaultInstance().constructType(targetType)); + + Object values = codec.decode(queryProcessor, request); + + assertThat(values).isEqualTo(decodedValue); + } +} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecsTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecsTest.java new file mode 100644 index 00000000000..7ccdbb2b38b --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecsTest.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.common.rest.codec.query; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class QueryCodecsTest { + static class MyQueryCodecCsv extends QueryCodecCsv { + @Override + public int getOrder() { + return 0; + } + } + + @Test + void can_override_by_customize_implement() { + QueryCodecs queryCodecs = QueryCodecs.createForTest(); + assertThat(queryCodecs.find(QueryCodecCsv.CODEC_NAME).getClass()) + .isEqualTo(QueryCodecCsv.class); + + queryCodecs = new QueryCodecs(singletonList(new MyQueryCodecCsv())); + assertThat(queryCodecs.find(QueryCodecCsv.CODEC_NAME).getClass()) + .isEqualTo(MyQueryCodecCsv.class); + } +} 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 new file mode 100644 index 00000000000..db0b09f83fe --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestPath.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.definition; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.definition.path.PathRegExp; +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 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() { + } + + @AfterEach + public void tearDown() { + } + + @Test + public void testPathRegExp() throws Exception { + PathRegExp oPathRegExp = new PathRegExp("//{test}//"); + Assertions.assertEquals(1, oPathRegExp.getGroupCount()); + Assertions.assertEquals(0, oPathRegExp.getGroupWithRegExpCount()); + PathRegExp oSecondPathRegExp = new PathRegExp("{[^/:]+?}"); + Assertions.assertEquals(1, oSecondPathRegExp.getGroupCount()); + Assertions.assertEquals(1, oSecondPathRegExp.getGroupWithRegExpCount()); + Assertions.assertEquals("test/", PathRegExp.ensureEndWithSlash("test/")); + Assertions.assertEquals("test/", PathRegExp.ensureEndWithSlash("test")); + Assertions.assertNull(oSecondPathRegExp.match("{test/test}", null)); + Assertions.assertEquals("(]+?)/(.*)", (oSecondPathRegExp.toString())); + Assertions.assertFalse(oSecondPathRegExp.isStaticPath()); + Assertions.assertEquals(0, oSecondPathRegExp.getStaticCharCount()); + Assertions.assertNotEquals(null, (oPathRegExp.match("//{test}//", new HashMap<>()))); + // Error Scenarios + new PathRegExp("//{test \t}//"); + // Error Scenarios for double {{ + try { + new PathRegExp("//{test{"); + Assertions.fail("an exception is expected!"); + } catch (Exception e) { + Assertions.assertTrue(e.getMessage().contains("A variable must not contain an extra")); + } + // Error Scenarios for illegal }} + try { + new PathRegExp("//}"); + Assertions.fail("an exception is expected!"); + } catch (Exception e) { + Assertions.assertTrue(e.getMessage().contains("is only allowed as")); + } + // Error Scenarios for illegal ; + try { + new PathRegExp("//;"); + Assertions.fail("an exception is expected!"); + } catch (Exception e) { + Assertions.assertTrue(e.getMessage().contains("matrix parameters are not allowed in")); + } + + // Error Scenarios for NO } ; + try { + new PathRegExp("//{test"); + Assertions.fail("an exception is expected!"); + } catch (Exception e) { + Assertions.assertTrue(e.getMessage().contains("No '}' found after")); + } + } + + @Test + public void testUrlPathBuilder() throws Exception { + Map paramMap = new HashMap<>(); + + Parameter pathParameter = new PathParameter(); + pathParameter.setName("id"); + pathParameter.setSchema(new Schema<>()); + RestParam oRestParam = new RestParam(null, pathParameter, int.class); + paramMap.put(oRestParam.getParamName(), oRestParam); + + Parameter queryParameter = new QueryParameter(); + queryParameter.setName("q"); + queryParameter.setSchema(new Schema<>()); + oRestParam = new RestParam(null, queryParameter, String.class); + paramMap.put(oRestParam.getParamName(), oRestParam); + + URLPathBuilder oURLPathBuilder = new URLPathBuilder("/root/{id}", paramMap); + Map parameters = new HashMap<>(); + parameters.put("id", 100); + parameters.put("q", "query"); + Assertions.assertEquals("/root/100?q=query", oURLPathBuilder.createRequestPath(parameters)); + Assertions.assertEquals("/root/100", oURLPathBuilder.createPathString(parameters)); + } + + @Test + public void testQueryVarParamWriter() { + boolean status = true; + + Parameter parameter = new QueryParameter(); + 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); + try { + Map parameters = new HashMap<>(); + parameters.put("queryVar", "T"); + verify(writer, parameters, "?queryVar=T"); + parameters.put("queryVar", null); + verify(writer, parameters, ""); + parameters.put("queryVar", new String[] {"a", "b"}); + verify(writer, parameters, "?queryVar=a&queryVar=b"); + parameters.put("queryVar", new String[] {"a", null, "b"}); + verify(writer, parameters, "?queryVar=a&queryVar=b"); + parameters.put("queryVar", Arrays.asList("Lars", "Simon")); + verify(writer, parameters, "?queryVar=Lars&queryVar=Simon"); + parameters.put("queryVar", "测试"); + verify(writer, parameters, "?queryVar=%E6%B5%8B%E8%AF%95"); + parameters.put("queryVar", "a b"); + verify(writer, parameters, "?queryVar=a+b"); + parameters.put("queryVar", "a+b"); + verify(writer, parameters, "?queryVar=a%2Bb"); + } catch (Exception e) { + status = false; + } + Assertions.assertTrue(status); + } + + private void verify(QueryVarParamWriter writer, Map args, String expect) throws Exception { + URLPathStringBuilder sb = new URLPathStringBuilder(); + writer.write(sb, args); + Assertions.assertEquals(expect, sb.build()); + } +} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationComparator.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationComparator.java new file mode 100644 index 00000000000..66a4a5bad54 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationComparator.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.common.rest.definition; + +import org.apache.servicecomb.common.rest.locator.MicroservicePaths; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestRestOperationComparator { + @Test + public void testStaticCharCount() { + RestOperationMeta less = new RestOperationMeta(); + less.setAbsolutePath("/a/{id}"); + + RestOperationMeta more = new RestOperationMeta(); + more.setAbsolutePath("/abc/{id}"); + + MicroservicePaths paths = new MicroservicePaths(); + paths.addResource(less); + paths.addResource(more); + paths.sortPath(); + + Assertions.assertSame(more, paths.getDynamicPathOperationList().get(0)); + Assertions.assertSame(less, paths.getDynamicPathOperationList().get(1)); + } + + @Test + public void testVarGroupCount() { + RestOperationMeta less = new RestOperationMeta(); + less.setAbsolutePath("/ab/{id}"); + + RestOperationMeta more = new RestOperationMeta(); + more.setAbsolutePath("/a/{test}/{id}"); + + MicroservicePaths paths = new MicroservicePaths(); + paths.addResource(less); + paths.addResource(more); + paths.sortPath(); + + Assertions.assertSame(more, paths.getDynamicPathOperationList().get(0)); + Assertions.assertSame(less, paths.getDynamicPathOperationList().get(1)); + } + + @Test + public void testGroupWithRegExpCount() { + RestOperationMeta less = new RestOperationMeta(); + less.setAbsolutePath("/a/{test}/{id}"); + + RestOperationMeta more = new RestOperationMeta(); + more.setAbsolutePath("/a/{test : .+}/{id}"); + + MicroservicePaths paths = new MicroservicePaths(); + paths.addResource(less); + paths.addResource(more); + paths.sortPath(); + + Assertions.assertSame(more, paths.getDynamicPathOperationList().get(0)); + Assertions.assertSame(less, paths.getDynamicPathOperationList().get(1)); + } +} 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 new file mode 100644 index 00000000000..df8ebe47a75 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationMeta.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.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 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.core.executor.ExecutorManager; +import org.apache.servicecomb.core.transport.TransportManager; +import org.hamcrest.MatcherAssert; +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.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(value = MediaType.APPLICATION_JSON) + public String emptyProduces() { + return null; + } + + @Path("/emptyProducesWithView") + @GET + @Produces(value = MediaType.APPLICATION_JSON) + @JsonView(Object.class) + public String emptyProducesWithView() { + return null; + } + + @Path("/notSupport") + @GET + @Produces(value = MediaType.APPLICATION_JSON) + public void notSupport() { + + } + + @Path("/notSupportWithView") + @GET + @Produces(value = MediaType.APPLICATION_JSON) + @JsonView(Object.class) + public void notSupportWithView() { + + } + + @Path("/textPlain") + @GET + @Produces(MediaType.TEXT_PLAIN) + public String textPlain() { + return null; + } + + @Path("/textPlainWithView") + @GET + @Produces(MediaType.TEXT_PLAIN) + @JsonView(Object.class) + public String textPlainWithView() { + return null; + } + + @Path("/json") + @GET + @Produces(MediaType.APPLICATION_JSON) + public String json() { + return null; + } + + @Path("/jsonWithView") + @GET + @Produces(MediaType.APPLICATION_JSON) + @JsonView(Object.class) + public String jsonWithView() { + return null; + } + + @Path("/textCharJsonChar") + @GET + @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) + public void textCharJsonChar() { + + } + + @Path("/textCharJsonCharWithView") + @GET + @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) + @JsonView(Object.class) + public void textCharJsonCharWithView() { + + } + + @Path("/download") + @GET + @Produces(MediaType.APPLICATION_JSON) + public File download() { + return null; + } + + @Path("/downloadWithView") + @GET + @Produces(MediaType.APPLICATION_JSON) + @JsonView(Object.class) + public File downloadWithView() { + return null; + } + + @Path("/form") + @POST + public void form(@FormParam("form") String form) { + } + + @Path("/formWithView") + @POST + public void formWithView(@FormParam("form") String form) { + } + } + + SCBEngine scbEngine; + + OpenAPI swagger; + + OperationMeta meta; + + RestOperationMeta operationMeta; + + @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()); + } + + @AfterEach + public void teardown() { + scbEngine.destroy(); + } + + private void findOperation(String operationId) { + meta = Mockito.spy(scbEngine.getProducerMicroserviceMeta().operationMetas().get("test.sid1." + operationId)); + operationMeta = Mockito.spy(RestMetaUtils.getRestOperationMeta(meta)); + SchemaMeta schemaMeta = Mockito.spy(meta.getSchemaMeta()); + Mockito.when(meta.getSchemaMeta()).thenReturn(schemaMeta); + Mockito.when(schemaMeta.getSwagger()).thenReturn(swagger); + } + + @Test + public void generatesAbsolutePathWithRootBasePath() { + findOperation("textCharJsonChar"); + + MatcherAssert.assertThat(operationMeta.getAbsolutePath(), is("/textCharJsonChar")); + } + + @Test + public void generatesAbsolutePathWithNonRootBasePath() { + findOperation("textCharJsonChar"); + 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")); + } + + @Test + public void generatesAbsolutePathWithNullPath() { + findOperation("textCharJsonChar"); + 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); + + MatcherAssert.assertThat(restOperationMeta.getAbsolutePath(), is("/")); + } + + @Test + public void generatesAbsolutePathWithEmptyPath() { + findOperation("textCharJsonChar"); + 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); + + MatcherAssert.assertThat(restOperationMeta.getAbsolutePath(), is("/")); + } + + @Test + public void consecutiveSlashesAreRemoved() { + findOperation("textCharJsonChar"); + 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); + + MatcherAssert.assertThat(restOperationMeta.getAbsolutePath(), is("/rest/sayHi/")); + } + + @Test + public void testFormDataFlagTrue() { + findOperation("form"); + + MatcherAssert.assertThat(operationMeta.isFormData(), is(true)); + } + + @Test + public void testFormDataFlagFalse() { + findOperation("json"); + + MatcherAssert.assertThat(operationMeta.isFormData(), is(false)); + } +} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriterTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriterTest.java new file mode 100644 index 00000000000..99ef928b48d --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/PathVarParamWriterTest.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.common.rest.definition.path; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.definition.RestParam; +import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class PathVarParamWriterTest { + + @Test + public void writePlainPath() throws Exception { + PathVarParamWriter pathVarParamWriter = createPathVarParamWriter(); + + URLPathStringBuilder pathBuilder = new URLPathStringBuilder(); + Map parameters = new HashMap<>(); + parameters.put("test", "abc"); + pathVarParamWriter.write(pathBuilder, parameters); + Assertions.assertEquals("abc", pathBuilder.build()); + } + + @Test + public void writePathWithSpace() throws Exception { + PathVarParamWriter pathVarParamWriter = createPathVarParamWriter(); + + URLPathStringBuilder pathBuilder = new URLPathStringBuilder(); + Map parameters = new HashMap<>(); + parameters.put("test", "a 20bc"); + pathVarParamWriter.write(pathBuilder, parameters); + Assertions.assertEquals("a%2020bc", pathBuilder.build()); + } + + @Test + public void writePathWithPercentage() throws Exception { + PathVarParamWriter pathVarParamWriter = createPathVarParamWriter(); + URLPathStringBuilder pathBuilder = new URLPathStringBuilder(); + pathBuilder.appendPath("/api/"); + Map parameters = new HashMap<>(); + parameters.put("test", "a%%bc"); + pathVarParamWriter.write(pathBuilder, parameters); + Assertions.assertEquals("/api/a%25%25bc", pathBuilder.build()); + } + + @Test + public void writePathParamWithSlash() throws Exception { + PathVarParamWriter pathVarParamWriter = createPathVarParamWriter(); + URLPathStringBuilder pathBuilder = new URLPathStringBuilder(); + pathBuilder.appendPath("/api/"); + Map parameters = new HashMap<>(); + parameters.put("test", "a/bc"); + pathVarParamWriter.write(pathBuilder, parameters); + Assertions.assertEquals("/api/a%2Fbc", pathBuilder.build()); + } + + @Test + public void writeIntegerParam() throws Exception { + PathVarParamWriter pathVarParamWriter = createPathVarParamWriter(); + URLPathStringBuilder pathBuilder = new URLPathStringBuilder(); + Map parameters = new HashMap<>(); + parameters.put("test", 12); + pathVarParamWriter.write(pathBuilder, parameters); + Assertions.assertEquals("12", pathBuilder.build()); + } + + private PathVarParamWriter createPathVarParamWriter() { + RestParam restParam = Mockito.mock(RestParam.class); + Mockito.when(restParam.getParamName()).thenReturn("test"); + return new PathVarParamWriter(restParam); + } +} 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 new file mode 100644 index 00000000000..1aafc89fbb3 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/QueryVarParamWriterTest.java @@ -0,0 +1,258 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.definition.path; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +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 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; + + private static QueryVarParamWriter queryVarParamWriterMulti; + + 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.setSchema(new Schema()); + parameter.setStyle(StyleEnum.FORM); + parameter.setExplode(false); + queryVarParamWriterCsv = new QueryVarParamWriter( + new RestParam(null, parameter, String[].class)); + + parameter = new QueryParameter(); + parameter.setName("q"); + parameter.setSchema(new Schema()); + parameter.setStyle(StyleEnum.FORM); + parameter.setExplode(true); + queryVarParamWriterMulti = new QueryVarParamWriter( + new RestParam(null, parameter, String[].class)); + + parameter = new QueryParameter(); + parameter.setName("q"); + parameter.setSchema(new Schema()); + queryVarParamWriterDefault = new QueryVarParamWriter( + new RestParam(null, parameter, String[].class)); + } + + @Test + public void write() throws Exception { + URLPathStringBuilder stringBuilder = new URLPathStringBuilder(); + Map parameters = new HashMap<>(); + parameters.put("q", "a"); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("?q=a", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("?q=a", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("?q=a", stringBuilder.build()); + } + + @Test + public void writeNull() throws Exception { + URLPathStringBuilder stringBuilder = new URLPathStringBuilder(); + Map parameters = new HashMap<>(); + parameters.put("test", null); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + } + + @Test + public void writeArray() throws Exception { + URLPathStringBuilder stringBuilder = new URLPathStringBuilder(); + Map parameters = new HashMap<>(); + parameters.put("q", new String[] {"ab", "cd", "ef"}); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("?q=ab%2Ccd%2Cef", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.build()); + + // encode space char + stringBuilder = new URLPathStringBuilder(); + parameters.put("q", new String[] {"a b", " ", "", "ef"}); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("?q=a+b%2C+%2C%2Cef", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.build()); + + // pass blank string + stringBuilder = new URLPathStringBuilder(); + parameters.put("q", new String[] {""}); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("?q=", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("?q=", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("?q=", stringBuilder.build()); + + // pass empty + stringBuilder = new URLPathStringBuilder(); + parameters.put("q", new String[] {}); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + // pass null + parameters.put("q", new String[] {null}); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + + parameters.put("q", new String[] {null, "ab", null, "cd", null, null, "", null, "ef", null}); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("?q=ab%2Ccd%2C%2Cef", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("?q=ab&q=cd&q=&q=ef", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("?q=ab&q=cd&q=&q=ef", stringBuilder.build()); + } + + @Test + public void writeList() throws Exception { + List queryList = Arrays.asList("ab", "cd", "ef"); + Map parameters = new HashMap<>(); + parameters.put("q", queryList); + URLPathStringBuilder stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("?q=ab%2Ccd%2Cef", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("?q=ab&q=cd&q=ef", stringBuilder.build()); + + // encode space char + parameters.put("q", Arrays.asList("a b", " ", "", "ef")); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("?q=a+b%2C+%2C%2Cef", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("?q=a+b&q=+&q=&q=ef", stringBuilder.build()); + + // pass blank string + stringBuilder = new URLPathStringBuilder(); + parameters.put("q", Collections.singletonList("")); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("?q=", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("?q=", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("?q=", stringBuilder.build()); + + // pass empty + stringBuilder = new URLPathStringBuilder(); + parameters.put("q", new ArrayList<>()); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + // pass null + parameters.put("q", Collections.singletonList(null)); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("", stringBuilder.build()); + + parameters.put("q", Arrays.asList(null, "ab", null, "cd", null, null, "", null, "ef", null)); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterCsv.write(stringBuilder, parameters); + Assertions.assertEquals("?q=ab%2Ccd%2C%2Cef", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterMulti.write(stringBuilder, parameters); + Assertions.assertEquals("?q=ab&q=cd&q=&q=ef", stringBuilder.build()); + stringBuilder = new URLPathStringBuilder(); + queryVarParamWriterDefault.write(stringBuilder, parameters); + Assertions.assertEquals("?q=ab&q=cd&q=&q=ef", stringBuilder.build()); + } +} 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 new file mode 100644 index 00000000000..bcff2ff1523 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathBuilderTest.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.definition.path; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.LinkedHashMap; +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 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<>(); + addParam("p0", int.class, PathParameter::new, paramMap); + addParam("p1", String.class, PathParameter::new, paramMap); + addParam("q0", int.class, QueryParameter::new, paramMap); + addParam("q1", String.class, QueryParameter::new, paramMap); + + URLPathBuilder urlPathBuilder = new URLPathBuilder("/path/{p0}/and/{p1}", paramMap); + Map parameters = new HashMap<>(); + parameters.put("p0", 10); + parameters.put("p1", "abcPath"); + parameters.put("q0", 11); + parameters.put("q1", "queryABC"); + Assertions.assertEquals("/path/10/and/abcPath?q0=11&q1=queryABC", + urlPathBuilder.createRequestPath(parameters)); + Assertions.assertEquals("/path/10/and/abcPath", + urlPathBuilder.createPathString(parameters)); + } + + @Test + public void testEncode() throws Exception { + Map paramMap = new LinkedHashMap<>(); + addParam("p", String.class, PathParameter::new, paramMap); + addParam("q", String.class, QueryParameter::new, paramMap); + + URLPathBuilder urlPathBuilder = new URLPathBuilder("/path/{p}", paramMap); + Map parameters = new HashMap<>(); + parameters.put("p", "ab%% %cd%"); + parameters.put("q", "ab%% %cd%"); + Assertions.assertEquals("/path/ab%25%25%20%25cd%25?q=ab%25%25+%25cd%25", + urlPathBuilder.createRequestPath(parameters)); + Assertions.assertEquals("/path/ab%25%25%20%25cd%25", + urlPathBuilder.createPathString(parameters)); + } + + @Test + public void testMultiQuery() throws Exception { + Map paramMap = new LinkedHashMap<>(); + addParam("strArr", String[].class, QueryParameter::new, paramMap); + addParam("intArr", int[].class, QueryParameter::new, paramMap); + + URLPathBuilder urlPathBuilder = new URLPathBuilder("/path", paramMap); + Map parameters = new HashMap<>(); + parameters.put("strArr", new Object[] {"a", "b", "c"}); + parameters.put("intArr", new Object[] {1, 2, 3}); + Assertions.assertEquals("/path?strArr=a&strArr=b&strArr=c&intArr=1&intArr=2&intArr=3", + urlPathBuilder.createRequestPath(parameters)); + parameters.put("strArr", new Object[] {}); + parameters.put("intArr", new Object[] {1, 2, 3}); + Assertions.assertEquals("/path?intArr=1&intArr=2&intArr=3", + urlPathBuilder.createRequestPath(parameters)); + parameters.put("strArr", new Object[] {"a", "b", "c"}); + parameters.put("intArr", new Object[] {}); + Assertions.assertEquals("/path?strArr=a&strArr=b&strArr=c", + urlPathBuilder.createRequestPath(parameters)); + parameters.put("strArr", new Object[] {}); + parameters.put("intArr", new Object[] {}); + Assertions.assertEquals("/path", + urlPathBuilder.createRequestPath(parameters)); + } + + @Test + public void testRegexPathParam() throws Exception { + Map paramMap = new LinkedHashMap<>(); + addParam("p0", int.class, PathParameter::new, paramMap); + addParam("p1", String.class, PathParameter::new, paramMap); + addParam("q0", int.class, QueryParameter::new, paramMap); + addParam("q1", String.class, QueryParameter::new, paramMap); + + URLPathBuilder urlPathBuilder = new URLPathBuilder("/path/{p0 : .*}/and/{p1:.*}", paramMap); + Map parameters = new HashMap<>(); + parameters.put("p0", 10); + parameters.put("p1", "abcPath"); + parameters.put("q0", 11); + parameters.put("q1", "queryABC"); + Assertions.assertEquals("/path/10/and/abcPath?q0=11&q1=queryABC", + urlPathBuilder.createRequestPath(parameters)); + Assertions.assertEquals("/path/10/and/abcPath", + urlPathBuilder.createPathString(parameters)); + } + + private void addParam(String paramName, Type paramType, + ParameterConstructor constructor, Map paramMap) { + Parameter parameter = constructor.construct(); + parameter.setName(paramName); + parameter.setSchema(new Schema()); + paramMap.put(paramName, new RestParam(null, parameter, paramType)); + } + + interface ParameterConstructor { + Parameter construct(); + } +} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathStringBuilderTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathStringBuilderTest.java new file mode 100644 index 00000000000..5ccb4a7eff2 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/path/URLPathStringBuilderTest.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.common.rest.definition.path; + +import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class URLPathStringBuilderTest { + @Test + public void testNormal() { + URLPathStringBuilder builder = new URLPathStringBuilder(); + builder.appendPath("/path"); + builder.appendQuery("q", "abc"); + Assertions.assertEquals("/path?q=abc", builder.build()); + } + + @Test + public void appendPath() { + URLPathStringBuilder builder = new URLPathStringBuilder(); + builder.appendPath("/abc"); + Assertions.assertEquals("/abc", builder.build()); + builder.appendPath("/de fg"); + Assertions.assertEquals("/abc/de fg", builder.build()); + } + + @Test + public void appendQuery() { + URLPathStringBuilder builder = new URLPathStringBuilder(); + Assertions.assertEquals("", builder.build()); + builder.appendQuery("ab", "cd"); + Assertions.assertEquals("?ab=cd", builder.build()); + builder.appendQuery("ef", ""); + Assertions.assertEquals("?ab=cd&ef=", builder.build()); + builder.appendQuery("gh", "jk"); + Assertions.assertEquals("?ab=cd&ef=&gh=jk", builder.build()); + } +} 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 new file mode 100644 index 00000000000..0decec28bd9 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 com.google.common.net.HttpHeaders.CONTENT_LENGTH; +import static com.google.common.net.HttpHeaders.TRANSFER_ENCODING; +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 org.apache.servicecomb.common.rest.HttpTransportContext; +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.SCBStatus; +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.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.invocation.InvocationFactory; +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 jakarta.servlet.http.Part; +import jakarta.ws.rs.core.MediaType; + +public class RestServerCodecFilterTest { + final RestServerCodecFilter codecFilter = new RestServerCodecFilter(); + + Invocation invocation; + + final Endpoint endpoint = Mockito.mock(Endpoint.class); + + final OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + + final SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); + + final MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); + + final InvocationRuntimeType invocationRuntimeType = Mockito.mock(InvocationRuntimeType.class); + + final RestOperationMeta restOperationMeta = Mockito.mock(RestOperationMeta.class); + + final HttpTransportContext transportContext = Mockito.mock(HttpTransportContext.class); + + final HttpServletResponseEx responseEx = Mockito.mock(HttpServletResponseEx.class); + + final MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + + 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() { + 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(); + } + + @BeforeEach + public void setUp() { + Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); + Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); + Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); + 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")); + } + + @Test + public void should_not_invoke_filter_when_decode_request_failed() { + FilterNode nextNode = Mockito.mock(FilterNode.class); + mockDecodeRequestFail(); + + codecFilter.onFilter(invocation, nextNode); + + Mockito.verify(nextNode, Mockito.times(0)).onFilter(invocation); + } + + @Test + 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)); + + 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)); + JavaType javaType = Mockito.mock(JavaType.class); + Mockito.when(invocationRuntimeType.findResponseType(200)).thenReturn(javaType); + Mockito.when(javaType.getRawClass()).thenAnswer(invocationOnMock -> Part.class); + Mockito.when(invocation.getTransportContext()).thenReturn(transportContext); + Mockito.when(transportContext.getResponseEx()).thenReturn(responseEx); + + codecFilter.onFilter(invocation, nextNode).get(); + } + + @Test + public void should_encode_response_header() throws ExecutionException, InterruptedException { + headers.add("h1", "v1"); + success_invocation(); + + Mockito.verify(responseEx).addHeader("h1", "v1"); + } + + @Test + public void should_not_encode_content_length_header() throws ExecutionException, InterruptedException { + headers.add("h1", "v1") + .add("h2", "v2") + .add(CONTENT_LENGTH, "10"); + success_invocation(); + + Mockito.verify(responseEx, Mockito.times(1)).addHeader("h1", "v1"); + Mockito.verify(responseEx, Mockito.times(1)).addHeader("h2", "v2"); + } + + @Test + public void should_not_encode_transfer_encoding_header() throws ExecutionException, InterruptedException { + headers.add("h1", "v1") + .add("h2", "v2") + .add(TRANSFER_ENCODING, "test"); + success_invocation(); + + Mockito.verify(responseEx, Mockito.times(1)).addHeader("h1", "v1"); + Mockito.verify(responseEx, Mockito.times(1)).addHeader("h2", "v2"); + } +} 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 new file mode 100644 index 00000000000..d99fb3b04f1 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestMicroservicePaths.java @@ -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. + */ + +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.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.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 TestMicroservicePaths { + SCBEngine scbEngine; + + MicroservicePaths paths; + + @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; + } + + @AfterEach + public void teardown() { + scbEngine.destroy(); + } + + @Test + public void staticGroup() { + RestOperationMeta meta = paths.getStaticPathOperationMap().get("/static/").findValue("POST"); + Assertions.assertSame("postStatic", meta.getOperationMeta().getOperationId()); + + meta = paths.getStaticPathOperationMap().get("/static/").findValue("GET"); + Assertions.assertSame("getStatic", meta.getOperationMeta().getOperationId()); + } + + @Test + public void testAddResourceStaticDuplicatedHttpMethod() { + RestOperationMeta staticResPost = Mockito.mock(RestOperationMeta.class); + Mockito.when(staticResPost.getHttpMethod()).thenReturn("POST"); + Mockito.when(staticResPost.getAbsolutePath()).thenReturn("/static/"); + Mockito.when(staticResPost.isAbsoluteStaticPath()).thenReturn(true); + + ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, + () -> 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()); + } +} 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 new file mode 100644 index 00000000000..b0de6caf78b --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestPathSchema.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.common.rest.locator; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; + +@Path("/") +public class TestPathSchema { + @Path("/static") + @GET + public void getStatic() { + + } + + @Path("/static") + @POST + public void postStatic() { + + } + + @Path("/staticEx") + @GET + public void getStaticEx() { + + } + + @Path("/dynamic/{id}") + @GET + public void dynamicId(@PathParam("id") String id) { + + } + + @Path("/dynamicEx/{id}") + @GET + public void dynamicExId(@PathParam("id") String id) { + + } +} 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 new file mode 100644 index 00000000000..84ae22015ae --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/locator/TestServicePathManager.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.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.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.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() { + 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() { + scbEngine.destroy(); + ClassLoaderScopeContext.clearClassLoaderScopeProperty(); + } + + @Test + public void testBuildProducerPathsNoPrefix() { + 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); + + scbEngine.destroy(); + } + + @Test + public void testBuildProducerPathsHasPrefix() { + ClassLoaderScopeContext.setClassLoaderScopeProperty(DefinitionConst.URL_PREFIX, "/root/rest"); + + 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 + spm.producerLocateOperation("/root/rest/static/", "GET"); + spm.producerLocateOperation("/root/rest/static/", "POST"); + spm.producerLocateOperation("/root/rest/dynamic/1/", "GET"); + spm.producerLocateOperation("/root/rest/dynamicEx/1/", "GET"); + + scbEngine.destroy(); + } +} 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 new file mode 100644 index 00000000000..f95468f4f38 --- /dev/null +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/resource/TestClassPathStaticResourceHandler.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.resource; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; +import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; +import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; +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.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +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(); + + @BeforeAll + public static void setup() { + handler.setWebRoot("web-root/"); + } + + @Test + public void normal() throws IOException { + Response response = handler.handle("index.html"); + Part part = response.getResult(); + + try (InputStream is = part.getInputStream()) { + 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)); + Assertions.assertEquals("inline", response.getHeader(HttpHeaders.CONTENT_DISPOSITION)); + } + + @Test + public void notExist() { + Response response = handler.handle("notExist.html"); + + InvocationException invocationException = response.getResult(); + Assertions.assertEquals(Status.NOT_FOUND, invocationException.getStatus()); + Assertions.assertEquals(Status.NOT_FOUND.getReasonPhrase(), + ((CommonExceptionData) invocationException.getErrorData()).getMessage()); + Assertions.assertEquals(404, response.getStatusCode()); + Assertions.assertEquals("Not Found", response.getReasonPhrase()); + } + + @Test + public void attack() { + Response response = handler.handle("../microservice.yaml"); + + InvocationException invocationException = response.getResult(); + Assertions.assertEquals(Status.NOT_FOUND, invocationException.getStatus()); + Assertions.assertEquals(Status.NOT_FOUND.getReasonPhrase(), + ((CommonExceptionData) invocationException.getErrorData()).getMessage()); + Assertions.assertEquals(404, response.getStatusCode()); + Assertions.assertEquals("Not Found", response.getReasonPhrase()); + } + + @Test + public void readContentFailed() throws IOException { + handler = Mockito.spy(TestClassPathStaticResourceHandler.handler); + Mockito.when(handler.findResource("web-root/index.html")) + .thenThrow(new RuntimeExceptionWithoutStackTrace("read content failed.")); + + try (LogCollector logCollector = new LogCollector()) { + Response response = handler.handle("index.html"); + + Assertions.assertEquals("failed to process static resource, path=web-root/index.html", + logCollector.getLastEvents().getMessage().getFormattedMessage()); + + InvocationException invocationException = response.getResult(); + Assertions.assertEquals(Status.INTERNAL_SERVER_ERROR, invocationException.getStatus()); + Assertions.assertEquals("failed to process static resource.", + ((CommonExceptionData) invocationException.getErrorData()).getMessage()); + Assertions.assertEquals(Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatusCode()); + Assertions.assertEquals(Status.INTERNAL_SERVER_ERROR.getReasonPhrase(), response.getReasonPhrase()); + } + } +} diff --git a/common/common-rest/src/test/resources/config/test.properties b/common/common-rest/src/test/resources/config/test.properties deleted file mode 100644 index d12341ed3dc..00000000000 --- a/common/common-rest/src/test/resources/config/test.properties +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -1=0 -1.1=0 -1.2=0 \ No newline at end of file diff --git a/common/common-rest/src/test/resources/log4j.properties b/common/common-rest/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/common/common-rest/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/common/common-rest/src/test/resources/log4j2.xml b/common/common-rest/src/test/resources/log4j2.xml new file mode 100644 index 00000000000..8cee07220ec --- /dev/null +++ b/common/common-rest/src/test/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/common/common-rest/src/test/resources/microservice.yaml b/common/common-rest/src/test/resources/microservice.yaml index 6c07990e198..90fd8d97c8e 100644 --- a/common/common-rest/src/test/resources/microservice.yaml +++ b/common/common-rest/src/test/resources/microservice.yaml @@ -1,18 +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. +## --------------------------------------------------------------------------- + APPLICATION_ID: test service_description: name: test version: 0.0.1 -cse: +servicecomb: service: registry: - address: http://127.0.0.1:9980 + address: http://127.0.0.1:30100 rest: address: 0.0.0.0:8080 highway: address: 0.0.0.0:7070 - #executors: - #default: test - #Provider: - #server: test - #server.wrapParam: test - diff --git a/common/common-rest/src/test/resources/web-root/index.html b/common/common-rest/src/test/resources/web-root/index.html new file mode 100644 index 00000000000..8fe33dc3cfa --- /dev/null +++ b/common/common-rest/src/test/resources/web-root/index.html @@ -0,0 +1,17 @@ + + diff --git a/common/pom.xml b/common/pom.xml index 1ed5fb765b8..c39c89ff4ce 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -1,11 +1,12 @@ - + 4.0.0 - io.servicecomb + org.apache.servicecomb java-chassis-parent - 0.3.0-SNAPSHOT - ../parent + 3.4.0-SNAPSHOT + ../parents/default common + Java Chassis::Common pom - common-javassist - common-protobuf - common-rest + common-protobuf + common-rest + common-access-log - \ No newline at end of file + diff --git a/core/pom.xml b/core/pom.xml index ea52360b99b..af73c36dff9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -1,11 +1,12 @@ + + org.apache.servicecomb + foundation-test-scaffolding + test + - org.slf4j - slf4j-log4j12 + org.apache.logging.log4j + log4j-slf4j-impl test - log4j - log4j + org.apache.logging.log4j + log4j-core + test + + + org.jmockit + jmockit test diff --git a/core/src/main/java/io/servicecomb/core/BootListener.java b/core/src/main/java/io/servicecomb/core/BootListener.java deleted file mode 100644 index 81199c71634..00000000000 --- a/core/src/main/java/io/servicecomb/core/BootListener.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -public interface BootListener { - enum EventType { - BEFORE_HANDLER, - AFTER_HANDLER, - BEFORE_PRODUCER_PROVIDER, - AFTER_PRODUCER_PROVIDER, - BEFORE_CONSUMER_PROVIDER, - AFTER_CONSUMER_PROVIDER, - BEFORE_TRANSPORT, - AFTER_TRANSPORT, - BEFORE_REGISTRY, - AFTER_REGISTRY - } - - class BootEvent { - private EventType eventType; - - public EventType getEventType() { - return eventType; - } - - public void setEventType(EventType eventType) { - this.eventType = eventType; - } - } - - void onBootEvent(BootEvent event); -} diff --git a/core/src/main/java/io/servicecomb/core/Const.java b/core/src/main/java/io/servicecomb/core/Const.java deleted file mode 100644 index 2be06b23071..00000000000 --- a/core/src/main/java/io/servicecomb/core/Const.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -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 ANY_TRANSPORT = ""; - - public static final String VERSION_RULE_LATEST = "latest"; - - public static final String DEFAULT_VERSION_RULE = VERSION_RULE_LATEST; - - public static final String PRODUCER_OPERATION = "producer-operation"; - - public static final String SRC_MICROSERVICE = "x-cse-src-microservice"; - - public static final String TARGET_MICROSERVICE = "x-cse-target-microservice"; -} diff --git a/core/src/main/java/io/servicecomb/core/ConsumerProvider.java b/core/src/main/java/io/servicecomb/core/ConsumerProvider.java deleted file mode 100644 index 2e300331b2a..00000000000 --- a/core/src/main/java/io/servicecomb/core/ConsumerProvider.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -/** - * consumer端对业务的接口,不同场景是完全不同的 - * 所以这里只定义consumer对core的接口 - */ -public interface ConsumerProvider { - String getName(); - - void init() throws Exception; -} diff --git a/core/src/main/java/io/servicecomb/core/CseApplicationListener.java b/core/src/main/java/io/servicecomb/core/CseApplicationListener.java deleted file mode 100644 index b4380edff64..00000000000 --- a/core/src/main/java/io/servicecomb/core/CseApplicationListener.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import java.util.Collection; - -import javax.inject.Inject; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -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.event.ContextClosedEvent; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.context.support.AbstractApplicationContext; -import org.springframework.core.Ordered; - -import io.servicecomb.core.BootListener.BootEvent; -import io.servicecomb.core.BootListener.EventType; -import io.servicecomb.core.definition.loader.SchemaListenerManager; -import io.servicecomb.core.endpoint.AbstractEndpointsCache; -import io.servicecomb.core.handler.HandlerConfigUtils; -import io.servicecomb.core.provider.consumer.ConsumerProviderManager; -import io.servicecomb.core.provider.consumer.ReferenceConfigUtils; -import io.servicecomb.core.provider.producer.ProducerProviderManager; -import io.servicecomb.core.transport.TransportManager; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.FortifyUtils; -import io.servicecomb.serviceregistry.RegistryUtils; - -public class CseApplicationListener - implements ApplicationListener, Ordered, ApplicationContextAware { - private static final Logger LOGGER = LoggerFactory.getLogger(CseApplicationListener.class); - - private static boolean isInit = false; - - @Inject - private ProducerProviderManager producerProviderManager; - - @Inject - private ConsumerProviderManager consumerProviderManager; - - @Inject - private TransportManager transportManager; - - @Inject - private SchemaListenerManager schemaListenerManager; - - private Collection bootListenerList; - - private Class initEventClass = ContextRefreshedEvent.class; - - private ApplicationContext applicationContext; - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - BeanUtils.setContext(applicationContext); - RegistryUtils.init(); - } - - public void setInitEventClass(Class initEventClass) { - this.initEventClass = initEventClass; - } - - @Override - public int getOrder() { - // should run before default listener, eg: ZuulConfiguration - return -1000; - } - - protected void triggerEvent(EventType eventType) { - BootEvent event = new BootEvent(); - event.setEventType(eventType); - - for (BootListener listener : bootListenerList) { - listener.onBootEvent(event); - } - } - - @Override - public void onApplicationEvent(ApplicationEvent event) { - if (initEventClass.isInstance(event)) { - //TODO to load when webapplication context is used for discovery client, need to check if can use the order and undo this change with proper fix. - if (!isInit) { - try { - bootListenerList = applicationContext.getBeansOfType(BootListener.class).values(); - - AbstractEndpointsCache.init(RegistryUtils.getInstanceCacheManager(), transportManager); - - triggerEvent(EventType.BEFORE_HANDLER); - HandlerConfigUtils.init(); - triggerEvent(EventType.AFTER_HANDLER); - - triggerEvent(EventType.BEFORE_PRODUCER_PROVIDER); - producerProviderManager.init(); - triggerEvent(EventType.AFTER_PRODUCER_PROVIDER); - - triggerEvent(EventType.BEFORE_CONSUMER_PROVIDER); - consumerProviderManager.init(); - triggerEvent(EventType.AFTER_CONSUMER_PROVIDER); - - triggerEvent(EventType.BEFORE_TRANSPORT); - transportManager.init(); - triggerEvent(EventType.AFTER_TRANSPORT); - - schemaListenerManager.notifySchemaListener(); - - triggerEvent(EventType.BEFORE_REGISTRY); - RegistryUtils.run(); - ReferenceConfigUtils.setReady(true); - triggerEvent(EventType.AFTER_REGISTRY); - - // 当程序退出时,进行相关清理,注意:kill -9 {pid}下无效 - // 1. 去注册实例信息 - // TODO 服务优雅退出 - if (applicationContext instanceof AbstractApplicationContext) { - ((AbstractApplicationContext) applicationContext).registerShutdownHook(); - } - isInit = true; - } catch (Exception e) { - LOGGER.error("cse init failed, {}", FortifyUtils.getErrorInfo(e)); - } - } - } else if (event instanceof ContextClosedEvent) { - LOGGER.warn("cse is closing now..."); - RegistryUtils.destory(); - isInit = false; - } - } -} diff --git a/core/src/main/java/io/servicecomb/core/CseContext.java b/core/src/main/java/io/servicecomb/core/CseContext.java deleted file mode 100644 index c615cfe6790..00000000000 --- a/core/src/main/java/io/servicecomb/core/CseContext.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import javax.inject.Inject; - -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.loader.SchemaListenerManager; -import io.servicecomb.core.definition.loader.SchemaLoader; -import io.servicecomb.core.definition.schema.ConsumerSchemaFactory; -import io.servicecomb.core.provider.consumer.ConsumerProviderManager; -import io.servicecomb.core.provider.producer.ProducerProviderManager; -import io.servicecomb.core.transport.TransportManager; -import io.servicecomb.swagger.engine.SwaggerEnvironment; - -public class CseContext { - private static final CseContext INSTANCE = new CseContext(); - - public static CseContext getInstance() { - return INSTANCE; - } - - private SchemaListenerManager schemaListenerManager; - - private SchemaLoader schemaLoader; - - private MicroserviceMetaManager microserviceMetaManager; - - private ConsumerSchemaFactory consumerSchemaFactory; - - private ConsumerProviderManager consumerProviderManager; - - private ProducerProviderManager producerProviderManager; - - private TransportManager transportManager; - - private SwaggerEnvironment swaggerEnvironment; - - public SchemaListenerManager getSchemaListenerManager() { - return schemaListenerManager; - } - - public SchemaLoader getSchemaLoader() { - return schemaLoader; - } - - public MicroserviceMetaManager getMicroserviceMetaManager() { - return microserviceMetaManager; - } - - public ConsumerSchemaFactory getConsumerSchemaFactory() { - return consumerSchemaFactory; - } - - public ConsumerProviderManager getConsumerProviderManager() { - return consumerProviderManager; - } - - public ProducerProviderManager getProducerProviderManager() { - return producerProviderManager; - } - - public TransportManager getTransportManager() { - return transportManager; - } - - public SwaggerEnvironment getSwaggerEnvironment() { - return swaggerEnvironment; - } - - @Inject - public void setSwaggerEnvironment(SwaggerEnvironment swaggerEnvironment) { - this.swaggerEnvironment = swaggerEnvironment; - } - - @Inject - public void setMicroserviceMetaManager(MicroserviceMetaManager microserviceMetaManager) { - this.microserviceMetaManager = microserviceMetaManager; - } - - @Inject - public void setSchemaLoader(SchemaLoader schemaLoader) { - this.schemaLoader = schemaLoader; - } - - @Inject - public void setConsumerSchemaFactory(ConsumerSchemaFactory consumerSchemaFactory) { - this.consumerSchemaFactory = consumerSchemaFactory; - } - - @Inject - public void setConsumerProviderManager(ConsumerProviderManager consumerProviderManager) { - this.consumerProviderManager = consumerProviderManager; - } - - @Inject - public void setProducerProviderManager(ProducerProviderManager producerProviderManager) { - this.producerProviderManager = producerProviderManager; - } - - @Inject - public void setSchemaListenerManager(SchemaListenerManager schemaListenerManager) { - this.schemaListenerManager = schemaListenerManager; - } - - @Inject - public void setTransportManager(TransportManager transportManager) { - this.transportManager = transportManager; - } -} diff --git a/core/src/main/java/io/servicecomb/core/Endpoint.java b/core/src/main/java/io/servicecomb/core/Endpoint.java deleted file mode 100644 index 8ae58c5ba7b..00000000000 --- a/core/src/main/java/io/servicecomb/core/Endpoint.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; - -/** - * Endpoint : 表示一个Endpoint。Tranport识别其内部格式. - */ -public class Endpoint { - // 格式:rest://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; - - // 内部格式, 只有Transport能够认识 - private final Object address; - - public Endpoint(Transport transport, String endpoint) { - this(transport, endpoint, null); - } - - public Endpoint(Transport transport, String endpoint, MicroserviceInstance instance) { - this.transport = transport; - this.endpoint = endpoint; - this.instance = instance; - this.address = transport.parseAddress(this.endpoint); - } - - public String getEndpoint() { - return endpoint; - } - - public MicroserviceInstance getMicroserviceInstance() { - return instance; - } - - public Transport getTransport() { - return transport; - } - - public Object getAddress() { - return address; - } - - @Override - public String toString() { - return endpoint; - } -} diff --git a/core/src/main/java/io/servicecomb/core/Handler.java b/core/src/main/java/io/servicecomb/core/Handler.java deleted file mode 100644 index e1a927291a0..00000000000 --- a/core/src/main/java/io/servicecomb/core/Handler.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.InvocationType; - -// 每个微服务 + invocationType,都对应一个handler实例 -public interface Handler { - void init(MicroserviceMeta microserviceMeta, InvocationType invocationType); - - void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception; -} diff --git a/core/src/main/java/io/servicecomb/core/Invocation.java b/core/src/main/java/io/servicecomb/core/Invocation.java deleted file mode 100644 index 53fb47796c3..00000000000 --- a/core/src/main/java/io/servicecomb/core/Invocation.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executor; - -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.provider.consumer.ReferenceConfig; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.InvocationType; -import io.servicecomb.swagger.invocation.SwaggerInvocation; - -public class Invocation extends SwaggerInvocation { - private ReferenceConfig referenceConfig; - - // 本次调用对应的schemaMeta - private SchemaMeta schemaMeta; - - // 本次调用对应的operatoinMeta - private OperationMeta operationMeta; - - // loadbalance查询得到的地址,由transport client使用 - // 之所以不放在handlerContext中,是因为这属于核心数据,没必要走那样的机制 - private Endpoint endpoint; - - // 只用于handler之间传递数据,是本地数据 - private Map handlerContext = new HashMap<>(); - - // handler链,是arrayList,可以高效地通过index访问 - private List handlerList; - - private int handlerIndex; - - // 应答的处理器 - // 同步模式:避免应答在网络线程中处理解码等等业务级逻辑 - private Executor responseExecutor; - - public Invocation(ReferenceConfig referenceConfig, OperationMeta operationMeta, Object[] swaggerArguments) { - this.invocationType = InvocationType.CONSUMER; - this.referenceConfig = referenceConfig; - init(operationMeta, swaggerArguments); - } - - public Invocation(Endpoint endpoint, OperationMeta operationMeta, Object[] swaggerArguments) { - this.invocationType = InvocationType.PRODUCER; - this.endpoint = endpoint; - init(operationMeta, swaggerArguments); - } - - private void init(OperationMeta operationMeta, Object[] swaggerArguments) { - this.schemaMeta = operationMeta.getSchemaMeta(); - this.operationMeta = operationMeta; - this.swaggerArguments = swaggerArguments; - this.handlerList = getHandlerChain(); - handlerIndex = 0; - } - - public Transport getTransport() { - return endpoint.getTransport(); - } - - public List getHandlerChain() { - return (InvocationType.CONSUMER.equals(invocationType)) ? schemaMeta.getConsumerHandlerChain() - : schemaMeta.getProviderHandlerChain(); - } - - public Executor getResponseExecutor() { - return responseExecutor; - } - - public void setResponseExecutor(Executor responseExecutor) { - this.responseExecutor = responseExecutor; - } - - public SchemaMeta getSchemaMeta() { - return schemaMeta; - } - - public OperationMeta getOperationMeta() { - return operationMeta; - } - - public Object[] getArgs() { - return swaggerArguments; - } - - public Endpoint getEndpoint() { - return endpoint; - } - - public void setEndpoint(Endpoint endpoint) { - this.endpoint = endpoint; - } - - public Map getHandlerContext() { - return handlerContext; - } - - public int getHandlerIndex() { - return handlerIndex; - } - - public void setHandlerIndex(int handlerIndex) { - this.handlerIndex = handlerIndex; - } - - public void next(AsyncResponse asyncResp) throws Exception { - // 不必判断有效性,因为整个流程都是内部控制的 - int runIndex = handlerIndex; - handlerIndex++; - handlerList.get(runIndex).handle(this, asyncResp); - } - - public String getSchemaId() { - return schemaMeta.getSchemaId(); - } - - public String getOperationName() { - return operationMeta.getOperationId(); - } - - public String getConfigTransportName() { - return referenceConfig.getTransport(); - } - - public String getRealTransportName() { - return (endpoint != null) ? endpoint.getTransport().getName() : getConfigTransportName(); - } - - public String getMicroserviceName() { - return schemaMeta.getMicroserviceName(); - } - - public String getAppId() { - return schemaMeta.getMicroserviceMeta().getAppId(); - } - - public String getMicroserviceVersionRule() { - return referenceConfig.getMicroserviceVersionRule(); - } - - public String getInvocationQualifiedName() { - return invocationType.name() + " " + getRealTransportName() + " " - + getOperationMeta().getMicroserviceQualifiedName(); - } - - public String getMicroserviceQualifiedName() { - return operationMeta.getMicroserviceQualifiedName(); - } -} diff --git a/core/src/main/java/io/servicecomb/core/ProducerProvider.java b/core/src/main/java/io/servicecomb/core/ProducerProvider.java deleted file mode 100644 index b7a376579d8..00000000000 --- a/core/src/main/java/io/servicecomb/core/ProducerProvider.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -public interface ProducerProvider { - void init() throws Exception; - - String getName(); -} diff --git a/core/src/main/java/io/servicecomb/core/Transport.java b/core/src/main/java/io/servicecomb/core/Transport.java deleted file mode 100644 index a7b29660af4..00000000000 --- a/core/src/main/java/io/servicecomb/core/Transport.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import io.servicecomb.swagger.invocation.AsyncResponse; - -// TODO:感觉要拆成显式的client、server才好些 -public interface Transport { - String getName(); - - default int getOrder() { - return 0; - } - - default boolean canInit() { - return true; - } - - boolean init() throws Exception; - - /** - * endpoint的格式为 URI,比如rest://192.168.1.1:8080 - */ - Object parseAddress(String endpoint); - - /** - * 本tranport的监听地址 - */ - Endpoint getEndpoint() throws Exception; - - /** - * 用于上报到服务中心,要求是其他节点可访问的地址 - */ - Endpoint getPublishEndpoint() throws Exception; - - void send(Invocation invocation, AsyncResponse asyncResp) throws Exception; -} diff --git a/core/src/main/java/io/servicecomb/core/config/ConfigurationSpringInitializer.java b/core/src/main/java/io/servicecomb/core/config/ConfigurationSpringInitializer.java deleted file mode 100644 index fe723eea1a8..00000000000 --- a/core/src/main/java/io/servicecomb/core/config/ConfigurationSpringInitializer.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.config; - -import java.io.IOException; -import java.util.Properties; - -import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; - -import io.servicecomb.foundation.common.config.ConfigMgr; - -public class ConfigurationSpringInitializer extends PropertyPlaceholderConfigurer { - // 以逗号分隔 - private String configId; - - public ConfigurationSpringInitializer() { - setIgnoreUnresolvablePlaceholders(true); - setOrder(Integer.MAX_VALUE - 1); - } - - public void setConfigId(String configId) { - this.configId = configId; - } - - @Override - protected void loadProperties(Properties props) throws IOException { - for (String id : configId.split(",")) { - try { - Properties config = ConfigMgr.INSTANCE.getConfig(id); - props.putAll(config); - } catch (Exception e) { - throw new IOException(e); - } - } - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/CommonService.java b/core/src/main/java/io/servicecomb/core/definition/CommonService.java deleted file mode 100644 index 22258ead21d..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/CommonService.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition; - -import java.util.Collection; - -import io.servicecomb.foundation.common.RegisterManager; - -public class CommonService { - protected String name; - - protected RegisterManager operationMgr; - - public void createOperationMgr(String operationMgrName) { - operationMgr = new RegisterManager<>(operationMgrName); - } - - public void regOperation(String operationName, OPERATION operaton) { - operationMgr.register(operationName, operaton); - } - - public OPERATION findOperation(String operation) { - return operationMgr.findValue(operation); - } - - public OPERATION ensureFindOperation(String operation) { - return operationMgr.ensureFindValue(operation); - } - - public Collection getOperations() { - return operationMgr.values(); - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/MicroserviceMeta.java b/core/src/main/java/io/servicecomb/core/definition/MicroserviceMeta.java deleted file mode 100644 index 95bc616bb65..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/MicroserviceMeta.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import io.servicecomb.foundation.common.RegisterManager; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.Const; - -/** - * 微服务名为microserviceName(app内部)或者appId:microserviceName(跨app) - * operation的查询key为operation的qualifiedName - */ -public class MicroserviceMeta extends CommonService { - private String appId; - - // 不包括appId的名字 - private String shortName; - - // 如果要生成class,在这个loader中创建 - private ClassLoader classLoader; - - // key为schema id - private RegisterManager idSchemaMetaMgr; - - // key为schema interface - // 只有一个interface对应一个schemaMeta时,才允许根据接口查询schema - // 否则直接抛异常,只能显式地指定schemaId来使用 - private Map, List> intfSchemaMetaMgr = new ConcurrentHashMap<>(); - - private final Object intfSchemaLock = new Object(); - - private Map extData = new ConcurrentHashMap<>(); - - public MicroserviceMeta(String microserviceName) { - classLoader = Thread.currentThread().getContextClassLoader(); - parseMicroserviceName(microserviceName); - createOperationMgr("Operation meta mgr for microservice " + microserviceName); - idSchemaMetaMgr = new RegisterManager<>("Schema meta id mgr for microservice " + microserviceName); - } - - public void regSchemaMeta(SchemaMeta schemaMeta) { - idSchemaMetaMgr.register(schemaMeta.getSchemaId(), schemaMeta); - regSchemaAndInterface(schemaMeta); - - for (OperationMeta operationMeta : schemaMeta.getOperations()) { - regOperation(operationMeta.getSchemaQualifiedName(), operationMeta); - } - } - - private void regSchemaAndInterface(SchemaMeta schemaMeta) { - Class intf = schemaMeta.getSwaggerIntf(); - synchronized (intfSchemaLock) { - List schemaList = intfSchemaMetaMgr.get(intf); - if (schemaList == null) { - schemaList = new ArrayList<>(); - intfSchemaMetaMgr.put(intf, schemaList); - } - - schemaList.add(schemaMeta); - } - } - - public SchemaMeta ensureFindSchemaMeta(String schemaId) { - return idSchemaMetaMgr.ensureFindValue(schemaId); - } - - public SchemaMeta findSchemaMeta(String schemaId) { - return idSchemaMetaMgr.findValue(schemaId); - } - - public SchemaMeta ensureFindSchemaMeta(Class schemaIntf) { - SchemaMeta schemaMeta = findSchemaMeta(schemaIntf); - if (schemaMeta == null) { - String msg = - String.format("No schema interface is %s.", schemaIntf.getName()); - throw new Error(msg); - } - - return schemaMeta; - } - - public SchemaMeta findSchemaMeta(Class schemaIntf) { - List schemaList = intfSchemaMetaMgr.get(schemaIntf); - if (schemaList == null) { - return null; - } - - if (schemaList.size() > 1) { - String msg = - String.format("More than one schema interface is %s, please use schemaId to choose a schema.", - schemaIntf.getName()); - throw new Error(msg); - } - - synchronized (intfSchemaLock) { - return schemaList.get(0); - } - } - - public Collection getSchemaMetas() { - return idSchemaMetaMgr.values(); - } - - public void putExtData(String key, Object data) { - extData.put(key, data); - } - - @SuppressWarnings("unchecked") - public T getExtData(String key) { - return (T) extData.get(key); - } - - public String getAppId() { - return appId; - } - - public String getShortName() { - return shortName; - } - - public ClassLoader getClassLoader() { - return classLoader; - } - - protected void parseMicroserviceName(String microserviceName) { - int idxAt = microserviceName.indexOf(Const.APP_SERVICE_SEPARATOR); - if (idxAt == -1) { - appId = RegistryUtils.getAppId(); - name = microserviceName; - shortName = name; - return; - } - - appId = microserviceName.substring(0, idxAt); - name = microserviceName; - shortName = microserviceName.substring(idxAt + 1); - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/MicroserviceMetaManager.java b/core/src/main/java/io/servicecomb/core/definition/MicroserviceMetaManager.java deleted file mode 100644 index cda233f4a51..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/MicroserviceMetaManager.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition; - -import java.util.Collection; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Component; - -import io.servicecomb.foundation.common.RegisterManager; -import io.servicecomb.serviceregistry.api.registry.Microservice; - -/** - * key为microserviceName(app内部)或者appId:microserviceName(跨app) - */ -@Component -public class MicroserviceMetaManager extends RegisterManager { - private static final String MICROSERVICE_SCHEMA_MGR = "microservice meta manager"; - - private final Object lock = new Object(); - - public MicroserviceMetaManager() { - super(MICROSERVICE_SCHEMA_MGR); - } - - public SchemaMeta ensureFindSchemaMeta(String microserviceName, String schemaId) { - MicroserviceMeta microserviceMeta = ensureFindValue(microserviceName); - return microserviceMeta.ensureFindSchemaMeta(schemaId); - } - - public Collection getAllSchemaMeta(String microserviceName) { - MicroserviceMeta microserviceMeta = ensureFindValue(microserviceName); - return microserviceMeta.getSchemaMetas(); - } - - public MicroserviceMeta getOrCreateMicroserviceMeta(Microservice microservice) { - String microserviceName = microservice.getServiceName(); - MicroserviceMeta microserviceMeta = getOrCreateMicroserviceMeta(microserviceName); - if (!StringUtils.isEmpty(microservice.getAlias())) { - if (findValue(microservice.getAlias()) == null) { - register(microservice.getAlias(), microserviceMeta); - } - } - - return microserviceMeta; - } - - public MicroserviceMeta getOrCreateMicroserviceMeta(String microserviceName) { - MicroserviceMeta microserviceMeta = findValue(microserviceName); - if (microserviceMeta == null) { - synchronized (lock) { - microserviceMeta = findValue(microserviceName); - if (microserviceMeta == null) { - microserviceMeta = new MicroserviceMeta(microserviceName); - register(microserviceName, microserviceMeta); - } - } - } - - return microserviceMeta; - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/OperationMeta.java b/core/src/main/java/io/servicecomb/core/definition/OperationMeta.java deleted file mode 100644 index 7cea2b88e9a..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/OperationMeta.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition; - -import java.lang.reflect.Method; -import java.util.Locale; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; - -import io.servicecomb.core.executor.ExecutorManager; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.response.ResponseMeta; -import io.servicecomb.swagger.invocation.response.ResponsesMeta; -import io.swagger.models.Operation; - -public class OperationMeta { - private SchemaMeta schemaMeta; - - // schemaId:operation - private String schemaQualifiedName; - - // microserviceName:schemaId:operation - private String microserviceQualifiedName; - - // 契约对应的method,与consumer、producer的method没有必然关系 - private Method method; - - private boolean sync; - - private String httpMethod; - - private String operationPath; - - private Operation swaggerOperation; - - // 在哪个executor上执行 - private Executor executor; - - private ResponsesMeta responsesMeta = new ResponsesMeta(); - - // transport、provider、consumer端都可能需要扩展数据 - // 为避免每个地方都做复杂的层次管理,直接在这里保存扩展数据 - private Map extData = new ConcurrentHashMap<>(); - - public void init(SchemaMeta schemaMeta, Method method, String operationPath, String httpMethod, - Operation swaggerOperation) { - this.schemaMeta = schemaMeta; - schemaQualifiedName = schemaMeta.getSchemaId() + "." + method.getName(); - microserviceQualifiedName = schemaMeta.getMicroserviceName() + "." + schemaQualifiedName; - this.operationPath = operationPath; - this.method = method; - this.httpMethod = httpMethod.toUpperCase(Locale.US); - this.swaggerOperation = swaggerOperation; - executor = ExecutorManager.findExecutor(this); - - collectMethodType(); - - responsesMeta.init(schemaMeta.getMicroserviceMeta().getClassLoader(), - schemaMeta.getPackageName(), - schemaMeta.getSwagger(), - swaggerOperation, - method.getGenericReturnType()); - } - - public String getHttpMethod() { - return httpMethod; - } - - public void setHttpMethod(String httpMethod) { - this.httpMethod = httpMethod; - } - - public String getOperationPath() { - return operationPath; - } - - private void collectMethodType() { - Class[] params = method.getParameterTypes(); - if (params.length == 0) { - sync = true; - return; - } - - Class lastParam = params[params.length - 1]; - sync = !AsyncResponse.class.isAssignableFrom(lastParam); - } - - public Operation getSwaggerOperation() { - return swaggerOperation; - } - - public ResponseMeta findResponseMeta(int statusCode) { - return responsesMeta.findResponseMeta(statusCode); - } - - public SchemaMeta getSchemaMeta() { - return schemaMeta; - } - - public String getSchemaQualifiedName() { - return schemaQualifiedName; - } - - public String getMicroserviceQualifiedName() { - return microserviceQualifiedName; - } - - public String getMicroserviceName() { - return schemaMeta.getMicroserviceName(); - } - - public Method getMethod() { - return method; - } - - public String getOperationId() { - return swaggerOperation.getOperationId(); - } - - // 调用者保证参数正确性 - public String getParamName(int idx) { - return swaggerOperation.getParameters().get(idx).getName(); - } - - public void putExtData(String key, Object data) { - extData.put(key, data); - } - - @SuppressWarnings("unchecked") - public T getExtData(String key) { - return (T) extData.get(key); - } - - public boolean isSync() { - return sync; - } - - public Executor getExecutor() { - return executor; - } - - public void setExecutor(Executor executor) { - this.executor = executor; - } - - public int getParamSize() { - return swaggerOperation.getParameters().size(); - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/SchemaMeta.java b/core/src/main/java/io/servicecomb/core/definition/SchemaMeta.java deleted file mode 100644 index 9d4487caa2e..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/SchemaMeta.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition; - -import java.lang.reflect.Method; -import java.util.List; -import java.util.Map.Entry; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.core.Handler; -import io.servicecomb.core.exception.ExceptionUtils; -import io.servicecomb.foundation.common.utils.ReflectUtils; -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.swagger.models.HttpMethod; -import io.swagger.models.Operation; -import io.swagger.models.Path; -import io.swagger.models.Swagger; - -public class SchemaMeta extends CommonService { - private static final Logger LOGGER = LoggerFactory.getLogger(SchemaMeta.class); - - // 如果要生成class,使用这个package - private String packageName; - - private Swagger swagger; - - private MicroserviceMeta microserviceMeta; - - // microserviceName:schemaId - private String microserviceQualifiedName; - - // 契约对应的接口 - private Class swaggerIntf; - - // handlerChain是microservice级别的 - private List consumerHandlerChain; - - private List providerHandlerChain; - - public SchemaMeta(Swagger swagger, MicroserviceMeta microserviceMeta, String schemaId) { - this.packageName = SchemaUtils.generatePackageName(microserviceMeta, schemaId); - - this.swagger = swagger; - this.name = schemaId; - - this.microserviceMeta = microserviceMeta; - this.microserviceQualifiedName = microserviceMeta.getName() + "." + schemaId; - // 确保swagger对应的接口是存在的 - swaggerIntf = ClassUtils.getOrCreateInterface(swagger, microserviceMeta.getClassLoader(), packageName); - - createOperationMgr("schemaMeta " + schemaId + " operation mgr"); - operationMgr.setRegisterErrorFmt("Operation name repeat, schema=%s, operation=%s"); - - initOperations(); - } - - public String getPackageName() { - return packageName; - } - - private void initOperations() { - for (Entry entry : swagger.getPaths().entrySet()) { - String strPath = entry.getKey(); - Path path = entry.getValue(); - for (Entry operationEntry : path.getOperationMap().entrySet()) { - Operation operation = operationEntry.getValue(); - if (operation.getOperationId() == null) { - throw ExceptionUtils.operationIdInvalid(getSchemaId(), strPath); - } - - // io.servicecomb.swagger.engine.SwaggerEnvironment.createConsumer(Class, Class) - // io.servicecomb.swagger.engine.SwaggerEnvironment.createProducer(Object, Swagger) - // had make sure that consumer/swagger or producer/swagger can work - // - // in this place, do not throw exception when method not exists - // eg: - // swagger interface is a.b.c, and consumer interface is a.b.c too. - // version 1, there are the same - // version 2, producer add a new operation, that means swagger have more operation than consumer interface a.b.c - // interface a.b.c in consumer process is the old interface - // so for swagger, can not do any valid check here - // only need to save found method, that's enough. - Method method = ReflectUtils.findMethod(swaggerIntf, operation.getOperationId()); - if (method == null) { - LOGGER.warn("method {} not found in swagger interface {}, schemaId={}", - operation.getOperationId(), - swaggerIntf.getName(), - getSchemaId()); - continue; - } - - String httpMethod = operationEntry.getKey().name(); - OperationMeta operationMeta = new OperationMeta(); - operationMeta.init(this, method, strPath, httpMethod, operation); - operationMgr.register(method.getName(), operationMeta); - } - } - } - - public Swagger getSwagger() { - return swagger; - } - - public String getSchemaId() { - return name; - } - - public String getMicroserviceQualifiedName() { - return microserviceQualifiedName; - } - - public String getMicroserviceName() { - return microserviceMeta.getName(); - } - - public MicroserviceMeta getMicroserviceMeta() { - return microserviceMeta; - } - - public Class getSwaggerIntf() { - return swaggerIntf; - } - - public List getConsumerHandlerChain() { - return consumerHandlerChain; - } - - public void setConsumerHandlerChain(List consumerHandlerChain) { - this.consumerHandlerChain = consumerHandlerChain; - } - - public List getProviderHandlerChain() { - return providerHandlerChain; - } - - public void setProviderHandlerChain(List providerHandlerChain) { - this.providerHandlerChain = providerHandlerChain; - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/SchemaUtils.java b/core/src/main/java/io/servicecomb/core/definition/SchemaUtils.java deleted file mode 100644 index 2dd86c2558c..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/SchemaUtils.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition; - -import java.net.URL; - -import org.apache.commons.io.IOUtils; - -import com.fasterxml.jackson.core.JsonProcessingException; - -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.swagger.models.Swagger; -import io.swagger.util.Yaml; - -public final class SchemaUtils { - - private SchemaUtils() { - } - - public static String generatePackageName(MicroserviceMeta microserviceMeta, String schemaId) { - String name = "cse.gen." + microserviceMeta.getAppId() + "." + microserviceMeta.getShortName() + "." - + schemaId; - - return ClassUtils.correctClassName(name); - } - - public static String swaggerToString(Swagger swagger) { - try { - return Yaml.mapper().writeValueAsString(swagger); - } catch (JsonProcessingException e) { - throw new Error(e); - } - } - - public static Swagger parseSwagger(URL url) { - try { - String swaggerContext = IOUtils.toString(url); - return Yaml.mapper().readValue(swaggerContext, Swagger.class); - } catch (Throwable e) { - throw new Error(e); - } - } - - public static Swagger parseSwagger(String swaggerContent) { - try { - return Yaml.mapper().readValue(swaggerContent, Swagger.class); - } catch (Throwable e) { - throw new Error(e); - } - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/loader/DynamicSchemaLoader.java b/core/src/main/java/io/servicecomb/core/definition/loader/DynamicSchemaLoader.java deleted file mode 100644 index caceb770a42..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/loader/DynamicSchemaLoader.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition.loader; - -import java.util.ArrayList; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.Resource; - -import io.servicecomb.core.CseContext; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.foundation.common.config.PaaSResourceUtils; -import io.servicecomb.serviceregistry.RegistryUtils; - -/** - * 场景: - * 1.consumer - * 网管调用产品 - * 网管事先不知道产品对应的微服务名 - * 产品注册到网管后,网管根据注册信息,进行契约注册 - * 2.producer - * 需要支持在不同的产品中部署为不同的微服务名 - * 微服务名是由环境变量等等方式注入的 - * 此时可以在BootListener中进行注册(必须在producer初始化之前注册契约) - */ -public class DynamicSchemaLoader { - private static final Logger LOGGER = LoggerFactory.getLogger(DynamicSchemaLoader.class); - - public static final DynamicSchemaLoader INSTANCE = new DynamicSchemaLoader(); - - /** - * 动态注册指定目录下的schema契约到当前服务 - * @param schemaLocation eg. "classpath*:schemas/*.yaml" - */ - public void registerSchemas(String schemaLocation) { - registerSchemas(RegistryUtils.getMicroservice().getServiceName(), schemaLocation); - } - - /** - * 动态注册指定目录下的schema契约到指定服务 - * @param microserviceName name of microservice - * @param schemaLocation eg. "classpath*:schemas/*.yaml" - */ - public void registerSchemas(String microserviceName, String schemaLocation) { - LOGGER.info("dynamic register schemas for {} in {}", microserviceName, schemaLocation); - - List schemaMetaList = new ArrayList<>(); - Resource[] resArr = PaaSResourceUtils.getResources(schemaLocation); - for (Resource resource : resArr) { - SchemaMeta schemaMeta = - CseContext.getInstance().getSchemaLoader().registerSchema(microserviceName, resource); - - schemaMetaList.add(schemaMeta); - } - - CseContext.getInstance().getSchemaListenerManager().notifySchemaListener(schemaMetaList); - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/loader/SchemaListener.java b/core/src/main/java/io/servicecomb/core/definition/loader/SchemaListener.java deleted file mode 100644 index ba6c97c46fb..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/loader/SchemaListener.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition.loader; - -import io.servicecomb.core.definition.SchemaMeta; - -public interface SchemaListener { - // 不必考虑多线程并发 - void onSchemaLoaded(SchemaMeta... schemaMetas); -} diff --git a/core/src/main/java/io/servicecomb/core/definition/loader/SchemaListenerManager.java b/core/src/main/java/io/servicecomb/core/definition/loader/SchemaListenerManager.java deleted file mode 100644 index 08d3a8233d6..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/loader/SchemaListenerManager.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition.loader; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import javax.inject.Inject; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.SchemaMeta; - -/** - * key为microserviceName - */ -@Component -public class SchemaListenerManager { - @Autowired(required = false) - private List schemaListenerList = new ArrayList<>(); - - @Inject - private MicroserviceMetaManager microserviceMetaManager; - - public void setSchemaListenerList(List schemaListenerList) { - this.schemaListenerList = schemaListenerList; - } - - public void setMicroserviceMetaManager(MicroserviceMetaManager microserviceMetaManager) { - this.microserviceMetaManager = microserviceMetaManager; - } - - public void notifySchemaListener(MicroserviceMeta... microserviceMetas) { - List schemaMetaList = new ArrayList<>(); - for (MicroserviceMeta microserviceMeta : microserviceMetas) { - schemaMetaList.addAll(microserviceMeta.getSchemaMetas()); - } - - notifySchemaListener(schemaMetaList.toArray(new SchemaMeta[schemaMetaList.size()])); - } - - public void notifySchemaListener() { - Collection microserviceMetas = microserviceMetaManager.values(); - notifySchemaListener(microserviceMetas.toArray(new MicroserviceMeta[microserviceMetas.size()])); - } - - public void notifySchemaListener(SchemaMeta... schemaMetas) { - for (SchemaListener listener : schemaListenerList) { - listener.onSchemaLoaded(schemaMetas); - } - } - - public void notifySchemaListener(List schemaMetaList) { - SchemaMeta[] schemaMetas = schemaMetaList.toArray(new SchemaMeta[schemaMetaList.size()]); - notifySchemaListener(schemaMetas); - } - - public SchemaMeta ensureFindSchemaMeta(String microserviceName, String schemaId) { - MicroserviceMeta microserviceMeta = microserviceMetaManager.ensureFindValue(microserviceName); - return microserviceMeta.ensureFindSchemaMeta(schemaId); - } - - public Collection getAllSchemaMeta(String microserviceName) { - MicroserviceMeta microserviceMeta = microserviceMetaManager.ensureFindValue(microserviceName); - return microserviceMeta.getSchemaMetas(); - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/loader/SchemaLoader.java b/core/src/main/java/io/servicecomb/core/definition/loader/SchemaLoader.java deleted file mode 100644 index e7c2f1a68e3..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/loader/SchemaLoader.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition.loader; - -import java.util.List; - -import javax.inject.Inject; - -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.Resource; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - -import io.servicecomb.core.Handler; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.definition.SchemaUtils; -import io.servicecomb.core.handler.ConsumerHandlerManager; -import io.servicecomb.core.handler.ProducerHandlerManager; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.serviceregistry.api.registry.BasePath; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.swagger.models.Swagger; - -@Component -public class SchemaLoader { - private static final Logger LOGGER = LoggerFactory.getLogger(SchemaLoader.class); - - @Inject - protected MicroserviceMetaManager microserviceMetaManager; - - public void setMicroserviceMetaManager(MicroserviceMetaManager microserviceMetaManager) { - this.microserviceMetaManager = microserviceMetaManager; - } - - /** - * resource的路径格式,至少是以这个形式结尾:schemaId.yaml - */ - public SchemaMeta registerSchema(String microserviceName, Resource resource) { - try { - String schemaId = FilenameUtils.getBaseName(resource.getFilename()); - - String swaggerContent = IOUtils.toString(resource.getURL()); - SchemaMeta schemaMeta = registerSchema(microserviceName, schemaId, swaggerContent); - - return schemaMeta; - } catch (Throwable e) { - throw new Error(e); - } - } - - public SchemaMeta registerSchema(String microserviceName, String schemaId, String swaggerContent) { - Swagger swagger = SchemaUtils.parseSwagger(swaggerContent); - if (swagger == null) { - throw new Error(String.format("Parse the swagger for %s:%s failed", microserviceName, schemaId)); - } - - return registerSchema(microserviceName, schemaId, swagger); - } - - public SchemaMeta registerSchema(String microserviceName, String schemaId, - Swagger swagger) { - MicroserviceMeta microserviceMeta = microserviceMetaManager.getOrCreateMicroserviceMeta(microserviceName); - - return registerSchema(microserviceMeta, schemaId, swagger); - } - - public SchemaMeta registerSchema(MicroserviceMeta microserviceMeta, String schemaId, - Swagger swagger) { - String microserviceName = microserviceMeta.getName(); - LOGGER.info("register schema {}/{}/{}", microserviceMeta.getAppId(), microserviceName, schemaId); - - SchemaMeta schemaMeta = new SchemaMeta(swagger, microserviceMeta, schemaId); - - List producerHandlerChain = ProducerHandlerManager.INSTANCE.getOrCreate(microserviceName); - schemaMeta.setProviderHandlerChain(producerHandlerChain); - - List consumerHandlerChain = ConsumerHandlerManager.INSTANCE.getOrCreate(microserviceName); - schemaMeta.setConsumerHandlerChain(consumerHandlerChain); - - microserviceMeta.regSchemaMeta(schemaMeta); - - putSelfBasePathIfAbsent(microserviceName, swagger.getBasePath()); - - return schemaMeta; - } - - public void putSelfBasePathIfAbsent(String microserviceName, String basePath) { - if (basePath == null || basePath.length() == 0) { - return; - } - - Microservice microservice = RegistryUtils.getMicroservice(); - if (!microservice.getServiceName().equals(microserviceName)) { - return; - } - - String urlPrefix = System.getProperty(Const.URL_PREFIX); - if (!StringUtils.isEmpty(urlPrefix) && !basePath.startsWith(urlPrefix)) { - basePath = urlPrefix + basePath; - } - - List paths = microservice.getPaths(); - for (BasePath path : paths) { - if (path.getPath().equals(basePath)) { - return; - } - } - - BasePath basePathObj = new BasePath(); - basePathObj.setPath(basePath); - paths.add(basePathObj); - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/schema/AbstractSchemaFactory.java b/core/src/main/java/io/servicecomb/core/definition/schema/AbstractSchemaFactory.java deleted file mode 100644 index 4c9597fe06c..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/schema/AbstractSchemaFactory.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition.schema; - -import java.net.URL; - -import javax.inject.Inject; - -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.definition.SchemaUtils; -import io.servicecomb.core.definition.loader.SchemaLoader; -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.swagger.generator.core.CompositeSwaggerGeneratorContext; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.servicecomb.swagger.generator.core.SwaggerGeneratorContext; -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.swagger.models.Swagger; - -/** - * 由consumer或producer发起的契约注册 - * 在consumer场景中,如果本地没有契约,需要从服务中心下载契约 - * 在producer场景中,如果本地没有契约,需要根据实现类动态生成契约 - */ -public abstract class AbstractSchemaFactory { - @Inject - protected MicroserviceMetaManager microserviceMetaManager; - - @Inject - protected SchemaLoader schemaLoader; - - @Inject - protected CompositeSwaggerGeneratorContext compositeSwaggerGeneratorContext; - - public void setMicroserviceMetaManager(MicroserviceMetaManager microserviceMetaManager) { - this.microserviceMetaManager = microserviceMetaManager; - } - - // 因为aop的存在,schemaInstance的class不一定等于schemaClass - protected SchemaMeta getOrCreateSchema(CONTEXT context) { - MicroserviceMeta microserviceMeta = context.getMicroserviceMeta(); - SchemaMeta schemaMeta = microserviceMeta.findSchemaMeta(context.getSchemaId()); - if (schemaMeta == null) { - schemaMeta = createSchema(context); - } - context.setSchemaMeta(schemaMeta); - - return schemaMeta; - } - - protected abstract SchemaMeta createSchema(CONTEXT context); - - protected Swagger loadSwagger(CONTEXT context) { - return loadSwagger(context.getMicroserviceName(), context.getSchemaId()); - } - - protected Swagger loadSwagger(String microserviceName, String schemaId) { - String path = generateSchemaPath(microserviceName, schemaId); - URL url = Thread.currentThread().getContextClassLoader().getResource(path); - if (url == null) { - return null; - } - - return SchemaUtils.parseSwagger(url); - } - - protected String generateSchemaPath(String microserviceName, String schemaId) { - int idxAt = microserviceName.indexOf(Const.APP_SERVICE_SEPARATOR); - if (idxAt < 0) { - return String.format("microservices/%s/%s.yaml", microserviceName, schemaId); - } - - String appId = microserviceName.substring(0, idxAt); - String realMicroserviceName = microserviceName.substring(idxAt + 1); - return String.format("applications/%s/%s/%s.yaml", - appId, - realMicroserviceName, - schemaId); - } - - protected SwaggerGenerator generateSwagger(CONTEXT context) { - SwaggerGeneratorContext generatorContext = - compositeSwaggerGeneratorContext.selectContext(context.getProviderClass()); - SwaggerGenerator generator = new SwaggerGenerator(generatorContext, context.getProviderClass()); - generator.setPackageName( - SchemaUtils.generatePackageName(context.getMicroserviceMeta(), context.getSchemaId())); - generator.generate(); - - // 确保接口是存在的 - ClassUtils.getOrCreateInterface(generator); - return generator; - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/schema/ConsumerSchemaContext.java b/core/src/main/java/io/servicecomb/core/definition/schema/ConsumerSchemaContext.java deleted file mode 100644 index e3613849b04..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/schema/ConsumerSchemaContext.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition.schema; - -import io.servicecomb.serviceregistry.api.registry.Microservice; - -public class ConsumerSchemaContext extends SchemaContext { - protected Microservice microservice; - - public Microservice getMicroservice() { - return microservice; - } - - public void setMicroservice(Microservice microservice) { - this.microservice = microservice; - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/schema/ConsumerSchemaFactory.java b/core/src/main/java/io/servicecomb/core/definition/schema/ConsumerSchemaFactory.java deleted file mode 100644 index 24aa872b129..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/schema/ConsumerSchemaFactory.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition.schema; - -import javax.inject.Inject; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.definition.SchemaUtils; -import io.servicecomb.core.definition.loader.SchemaListenerManager; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.swagger.models.Swagger; - -@Component -public class ConsumerSchemaFactory extends AbstractSchemaFactory { - private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerSchemaFactory.class); - - @Inject - protected SchemaListenerManager schemaListenerManager; - - private final Object lock = new Object(); - - public void setSchemaListenerManager(SchemaListenerManager schemaListenerManager) { - this.schemaListenerManager = schemaListenerManager; - } - - // 透明rpc场景,因为每次指定schema调用,所以可以懒加载 - // 而rest场景,是根据path反向查找schema,所以必须所有的schema都存在才行 - // 两种场景可能都会来初始化,为避免管理复杂度,改为全部全量初始化 - // - // 用于rest consumer注册的场景,此时启动流程已经完成,需要主动通知listener - // microserviceName可能是本app内的微服务 - // 也可能是appid:name形式的其他app的微服务 - public MicroserviceMeta getOrCreateMicroserviceMeta(String microserviceName, String microserviceVersionRule) { - MicroserviceMeta microserviceMeta = microserviceMetaManager.findValue(microserviceName); - if (microserviceMeta != null) { - return microserviceMeta; - } - - // 极小概率事件,不必做microservice级的锁分解了 - synchronized (lock) { - microserviceMeta = microserviceMetaManager.findValue(microserviceName); - if (microserviceMeta != null) { - return microserviceMeta; - } - - // 获取指定服务中有哪些schemaId - // 先取本地,再从服务中心取,如果服务中心取成功了,则将schema id合并处理 - microserviceMeta = new MicroserviceMeta(microserviceName); - Microservice microservice = - findMicroservice(microserviceMeta, microserviceVersionRule); - if (microservice == null) { - throw new Error( - String.format("can not get microservice from service center, appId=%s, name=%s", - microserviceMeta.getAppId(), - microserviceName)); - } - - getOrCreateConsumerSchema(microserviceMeta, microservice); - - microserviceMetaManager.register(microserviceName, microserviceMeta); - schemaListenerManager.notifySchemaListener(microserviceMeta); - return microserviceMeta; - } - } - - protected Microservice findMicroservice(MicroserviceMeta microserviceMeta, String microserviceVersionRule) { - String appId = microserviceMeta.getAppId(); - String microserviceName = microserviceMeta.getName(); - ServiceRegistryClient client = RegistryUtils.getServiceRegistryClient(); - String microserviceId = client.getMicroserviceId(appId, - microserviceMeta.getShortName(), - microserviceVersionRule); - if (StringUtils.isEmpty(microserviceId)) { - LOGGER.error("can not get microservice id, {}:{}:{}", appId, microserviceName, microserviceVersionRule); - return null; - } - - Microservice microservice = client.getMicroservice(microserviceId); - if (microservice == null) { - LOGGER.error("can not get microservice, {}:{}:{}", appId, microserviceName, microserviceVersionRule); - return null; - } - - LOGGER.info("Found schema ids from service center, {}:{}:{}:{}", - appId, - microserviceName, - microserviceVersionRule, - microservice.getSchemas()); - return microservice; - } - - // 允许consumerIntf与schemaId对应的interface原型不同,用于支持context类型的参数 - // consumerIntf为null,表示原型与契约相同 - protected void getOrCreateConsumerSchema(MicroserviceMeta microserviceMeta, Microservice microservice) { - for (String schemaId : microservice.getSchemas()) { - ConsumerSchemaContext context = new ConsumerSchemaContext(); - context.setMicroserviceMeta(microserviceMeta); - context.setMicroservice(microservice); - context.setSchemaId(schemaId); - context.setProviderClass(null); - - getOrCreateSchema(context); - } - } - - @Override - protected SchemaMeta createSchema(ConsumerSchemaContext context) { - // 尝试从规划的目录或服务中心加载契约 - Swagger swagger = loadSwagger(context); - - // 注册契约 - return schemaLoader.registerSchema(context.getMicroserviceMeta(), context.getSchemaId(), swagger); - } - - @Override - protected Swagger loadSwagger(ConsumerSchemaContext context) { - Swagger swagger = super.loadSwagger(context); - if (swagger != null) { - return swagger; - } - - ServiceRegistryClient client = RegistryUtils.getServiceRegistryClient(); - String schemaContent = client.getSchema(context.getMicroservice().getServiceId(), context.getSchemaId()); - LOGGER.info("load schema from service center, microservice={}:{}:{}, schemaId={}, result={}", - context.getMicroservice().getAppId(), - context.getMicroservice().getServiceName(), - context.getMicroservice().getVersion(), - context.getSchemaId(), - !StringUtils.isEmpty(schemaContent)); - if (schemaContent != null) { - return SchemaUtils.parseSwagger(schemaContent); - } - - throw new Error( - String.format("no schema in local, and can not get schema from service center, %s:%s", - context.getMicroserviceName(), - context.getSchemaId())); - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/schema/ProducerSchemaContext.java b/core/src/main/java/io/servicecomb/core/definition/schema/ProducerSchemaContext.java deleted file mode 100644 index e5603ad5aa5..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/schema/ProducerSchemaContext.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition.schema; - -public class ProducerSchemaContext extends SchemaContext { - protected Object producerInstance; - - public Object getProducerInstance() { - return producerInstance; - } - - public void setProducerInstance(Object producerInstance) { - this.producerInstance = producerInstance; - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/schema/ProducerSchemaFactory.java b/core/src/main/java/io/servicecomb/core/definition/schema/ProducerSchemaFactory.java deleted file mode 100644 index d2cb5446e31..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/schema/ProducerSchemaFactory.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition.schema; - -import java.util.Set; - -import javax.inject.Inject; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectWriter; - -import io.servicecomb.core.Const; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.swagger.engine.SwaggerEnvironment; -import io.servicecomb.swagger.engine.SwaggerProducer; -import io.servicecomb.swagger.engine.SwaggerProducerOperation; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.swagger.models.Swagger; -import io.swagger.util.Yaml; - -@Component -public class ProducerSchemaFactory extends AbstractSchemaFactory { - private static final Logger LOGGER = LoggerFactory.getLogger(ProducerSchemaFactory.class); - - @Inject - private SwaggerEnvironment swaggerEnv; - - private ObjectWriter writer = Yaml.pretty(); - - private String getSwaggerContent(Swagger swagger) { - try { - return writer.writeValueAsString(swagger); - } catch (JsonProcessingException e) { - throw new Error(e); - } - } - - public void setSwaggerEnv(SwaggerEnvironment swaggerEnv) { - this.swaggerEnv = swaggerEnv; - } - - // 只会在启动流程中调用 - public SchemaMeta getOrCreateProducerSchema(String microserviceName, String schemaId, - Class producerClass, - Object producerInstance) { - MicroserviceMeta microserviceMeta = microserviceMetaManager.getOrCreateMicroserviceMeta(microserviceName); - - ProducerSchemaContext context = new ProducerSchemaContext(); - context.setMicroserviceMeta(microserviceMeta); - context.setSchemaId(schemaId); - context.setProviderClass(producerClass); - context.setProducerInstance(producerInstance); - - SchemaMeta schemaMeta = getOrCreateSchema(context); - - SwaggerProducer producer = swaggerEnv.createProducer(producerInstance, schemaMeta.getSwagger()); - for (OperationMeta operationMeta : schemaMeta.getOperations()) { - SwaggerProducerOperation producerOperation = producer.findOperation(operationMeta.getOperationId()); - operationMeta.putExtData(Const.PRODUCER_OPERATION, producerOperation); - } - - return schemaMeta; - } - - protected SchemaMeta createSchema(ProducerSchemaContext context) { - // 尝试从规划的目录加载契约 - Swagger swagger = loadSwagger(context); - if (swagger == null) { - Set combinedNames = RegistryUtils.getServiceRegistry().getCombinedMicroserviceNames(); - for (String name : combinedNames) { - swagger = loadSwagger(name, context.getSchemaId()); - if (swagger != null) { - break; - } - } - } - - // 根据class动态产生契约 - SwaggerGenerator generator = generateSwagger(context); - if (swagger == null) { - swagger = generator.getSwagger(); - String swaggerContent = getSwaggerContent(swagger); - LOGGER.info("generate swagger for {}/{}/{}, swagger: {}", - context.getMicroserviceMeta().getAppId(), - context.getMicroserviceName(), - context.getSchemaId(), - swaggerContent); - } - - // 注册契约 - return schemaLoader.registerSchema(context.getMicroserviceMeta(), context.getSchemaId(), swagger); - } -} diff --git a/core/src/main/java/io/servicecomb/core/definition/schema/SchemaContext.java b/core/src/main/java/io/servicecomb/core/definition/schema/SchemaContext.java deleted file mode 100644 index fdfcc7abca5..00000000000 --- a/core/src/main/java/io/servicecomb/core/definition/schema/SchemaContext.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition.schema; - -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.SchemaMeta; - -public class SchemaContext { - protected MicroserviceMeta microserviceMeta; - - protected String schemaId; - - protected SchemaMeta schemaMeta; - - // consumer或producer对应的class - protected Class providerClass; - - public String getMicroserviceName() { - return microserviceMeta.getName(); - } - - public MicroserviceMeta getMicroserviceMeta() { - return microserviceMeta; - } - - public void setMicroserviceMeta(MicroserviceMeta microserviceMeta) { - this.microserviceMeta = microserviceMeta; - } - - public String getSchemaId() { - return schemaId; - } - - public void setSchemaId(String schemaId) { - this.schemaId = schemaId; - } - - public SchemaMeta getSchemaMeta() { - return schemaMeta; - } - - public void setSchemaMeta(SchemaMeta schemaMeta) { - this.schemaMeta = schemaMeta; - } - - public Class getProviderClass() { - return providerClass; - } - - public void setProviderClass(Class providerClass) { - this.providerClass = providerClass; - } -} diff --git a/core/src/main/java/io/servicecomb/core/endpoint/AbstractEndpointsCache.java b/core/src/main/java/io/servicecomb/core/endpoint/AbstractEndpointsCache.java deleted file mode 100644 index d6b2779125e..00000000000 --- a/core/src/main/java/io/servicecomb/core/endpoint/AbstractEndpointsCache.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.endpoint; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.springframework.util.StringUtils; - -import io.servicecomb.core.Transport; -import io.servicecomb.core.transport.TransportManager; -import io.servicecomb.serviceregistry.cache.CacheEndpoint; -import io.servicecomb.serviceregistry.cache.InstanceCache; -import io.servicecomb.serviceregistry.cache.InstanceCacheManager; - -/** - * registry模块不理解core中的概念 - * 所以要将字符串的各种信息转义一下,方便运行时使用 - */ -public abstract class AbstractEndpointsCache { - protected static InstanceCacheManager instanceCacheManager; - - protected static TransportManager transportManager; - - protected List endpoints = new ArrayList<>(); - - protected String transportName; - - protected InstanceCache instanceCache = null; - - public static void init(InstanceCacheManager instanceCacheManager, TransportManager transportManager) { - AbstractEndpointsCache.instanceCacheManager = instanceCacheManager; - AbstractEndpointsCache.transportManager = transportManager; - } - - /** - * transportName 可能为"",表示走任意健康的地址即可 - */ - public AbstractEndpointsCache(String appId, String microserviceName, String microserviceVersionRule, - String transportName) { - this.transportName = transportName; - this.instanceCache = new InstanceCache(appId, microserviceName, microserviceVersionRule, null); - } - - public List getLatestEndpoints() { - InstanceCache newCache = instanceCacheManager.getOrCreate(instanceCache.getAppId(), - instanceCache.getMicroserviceName(), - instanceCache.getMicroserviceVersionRule()); - if (!instanceCache.cacheChanged(newCache)) { - return endpoints; - } - - // 走到这里,肯定已经是存在"有效"地址了(可能是个空列表,表示没有存活的实例) - // 先创建,成功了,再走下面的更新逻辑 - List tmpEndpoints = createEndpints(newCache); - - this.instanceCache = newCache; - this.endpoints = tmpEndpoints; - return endpoints; - } - - protected List createEndpints(InstanceCache newCache) { - Map> transportMap = getOrCreateTransportMap(newCache); - - return createEndpoints(transportMap); - } - - protected List createEndpoints(Map> transportMap) { - List tmpEndpoints = new ArrayList<>(); - for (Entry> entry : transportMap.entrySet()) { - Transport transport = transportManager.findTransport(entry.getKey()); - if (transport == null) { - continue; - } - - List endpointList = entry.getValue(); - if (endpointList == null) { - continue; - } - - for (CacheEndpoint cacheEndpont : endpointList) { - ENDPOINT endpoint = createEndpoint(transport, cacheEndpont); - tmpEndpoints.add(endpoint); - } - } - return tmpEndpoints; - } - - private Map> getOrCreateTransportMap(InstanceCache newCache) { - Map> allTransportMap = newCache.getOrCreateTransportMap(); - if (StringUtils.isEmpty(transportName)) { - // 未指定tranport,将所有transport全取出来 - return allTransportMap; - } - - Map> transportMap = new HashMap<>(); - transportMap.put(transportName, allTransportMap.get(transportName)); - return transportMap; - } - - protected abstract ENDPOINT createEndpoint(Transport transport, CacheEndpoint cacheEndpoint); -} diff --git a/core/src/main/java/io/servicecomb/core/endpoint/EndpointsCache.java b/core/src/main/java/io/servicecomb/core/endpoint/EndpointsCache.java deleted file mode 100644 index ab778c527b5..00000000000 --- a/core/src/main/java/io/servicecomb/core/endpoint/EndpointsCache.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.endpoint; - -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Transport; -import io.servicecomb.serviceregistry.cache.CacheEndpoint; - -public class EndpointsCache extends AbstractEndpointsCache { - - public EndpointsCache(String appId, String microserviceName, String microserviceVersionRule, - String transportName) { - super(appId, microserviceName, microserviceVersionRule, transportName); - } - - @Override - protected Endpoint createEndpoint(Transport transport, CacheEndpoint cacheEndpoint) { - return new Endpoint(transport, cacheEndpoint.getEndpoint(), cacheEndpoint.getInstance()); - } -} diff --git a/core/src/main/java/io/servicecomb/core/exception/CseException.java b/core/src/main/java/io/servicecomb/core/exception/CseException.java deleted file mode 100644 index f526ead180e..00000000000 --- a/core/src/main/java/io/servicecomb/core/exception/CseException.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.exception; - -public class CseException extends RuntimeException { - - private static final long serialVersionUID = 8027482777502649656L; - - private final String code; - - public CseException(String code, String message) { - super(message); - this.code = code; - } - - public CseException(String code, String message, Throwable cause) { - super(message, cause); - this.code = code; - } - - public String getCode() { - return code; - } - - @Override - public String toString() { - return "ServiceDefinitionException Code:" + code + ", Message:" + getMessage(); - } -} diff --git a/core/src/main/java/io/servicecomb/core/exception/ExceptionUtils.java b/core/src/main/java/io/servicecomb/core/exception/ExceptionUtils.java deleted file mode 100644 index 7d5c625ae6d..00000000000 --- a/core/src/main/java/io/servicecomb/core/exception/ExceptionUtils.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.exception; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.foundation.common.RegisterManager; -import io.servicecomb.foundation.common.utils.FortifyUtils; - -public class ExceptionUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionUtils.class); - - // 异常码 - private static final String ERROR_DESC_MGR_MSG = "error desc mgr"; - - protected static final RegisterManager ERROR_DESC_MGR = new RegisterManager<>(ERROR_DESC_MGR_MSG); - - private static final String CSE_SCHEMA_OPERATION_ID_INVALID = "cse.schema.operation.id.invalid"; - - private static final String CSE_HANDLER_REF_NOT_EXIST = "cse.handler.ref.not.exist"; - - private static final String CSE_PRODUCER_OPERATION_NOT_EXIST = "cse.producer.operation.not.exist"; - - private static final String CSE_LB_NO_AVAILABLE_ADDRESS = "cse.lb.no.available.address"; - - static { - ERROR_DESC_MGR.register(CSE_HANDLER_REF_NOT_EXIST, "Handler not exist, id=%s"); - ERROR_DESC_MGR.register(CSE_SCHEMA_OPERATION_ID_INVALID, "OperationId is invalid, schemaId=%s, path=%s"); - ERROR_DESC_MGR.register(CSE_PRODUCER_OPERATION_NOT_EXIST, - "Producer operation not exist, schemaId=%s, operationName=%s"); - ERROR_DESC_MGR.register(CSE_LB_NO_AVAILABLE_ADDRESS, - "No available address found. microserviceName=%s, version=%s, transportName=%s"); - } - - protected ExceptionUtils() { - } - - // TODO:应该改为protected,不允许随便调,所有异常,都必须是强类型的 - public static CseException createCseException(String code, Object... args) { - String msg = String.format(ERROR_DESC_MGR.ensureFindValue(code), args); - - CseException exception = new CseException(code, msg); - LOGGER.error(FortifyUtils.getErrorInfo(exception)); - return exception; - } - - public static CseException createCseException(String code, Throwable cause, Object... args) { - String msg = String.format(ERROR_DESC_MGR.ensureFindValue(code), args); - - CseException exception = new CseException(code, msg, cause); - LOGGER.error(FortifyUtils.getErrorInfo(exception)); - return exception; - } - - public static CseException producerOperationNotExist(String schemaId, String operationName) { - return createCseException(CSE_PRODUCER_OPERATION_NOT_EXIST, - schemaId, - operationName); - } - - public static CseException operationIdInvalid(String schemaId, String path) { - return createCseException(CSE_SCHEMA_OPERATION_ID_INVALID, - schemaId, - path); - } - - public static CseException handlerRefNotExist(String id) { - return createCseException(CSE_HANDLER_REF_NOT_EXIST, id); - } - - public static CseException lbAddressNotFound(String microserviceName, String microserviceVersionRule, - String transportName) { - return createCseException(CSE_LB_NO_AVAILABLE_ADDRESS, - microserviceName, - microserviceVersionRule, - transportName); - } -} diff --git a/core/src/main/java/io/servicecomb/core/executor/ExecutorManager.java b/core/src/main/java/io/servicecomb/core/executor/ExecutorManager.java deleted file mode 100644 index a3244481cd5..00000000000 --- a/core/src/main/java/io/servicecomb/core/executor/ExecutorManager.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.executor; - -import java.util.concurrent.Executor; - -import com.netflix.config.DynamicPropertyFactory; - -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.foundation.common.utils.BeanUtils; - -public final class ExecutorManager { - private ExecutorManager() { - } - - // 只会在初始化时执行,一点点重复的查找,没必要做缓存 - public static Executor findExecutor(OperationMeta operationMeta) { - Executor executor = findByKey("cse.executors.Provider." + operationMeta.getSchemaQualifiedName()); - if (executor != null) { - return executor; - } - - // 尝试schema级别 - executor = findByKey("cse.executors.Provider." + operationMeta.getSchemaMeta().getName()); - if (executor != null) { - return executor; - } - - executor = findByKey("cse.executors.default"); - if (executor != null) { - return executor; - } - - return BeanUtils.getBean("cse.executor.default"); - } - - protected static Executor findByKey(String beanIdKey) { - String beanId = DynamicPropertyFactory.getInstance().getStringProperty(beanIdKey, null).get(); - if (beanId != null) { - return BeanUtils.getBean(beanId); - } - - return null; - } -} diff --git a/core/src/main/java/io/servicecomb/core/executor/FixedThreadExecutor.java b/core/src/main/java/io/servicecomb/core/executor/FixedThreadExecutor.java deleted file mode 100644 index 8dc250f24be..00000000000 --- a/core/src/main/java/io/servicecomb/core/executor/FixedThreadExecutor.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.executor; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicInteger; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.config.DynamicPropertyFactory; - -public class FixedThreadExecutor implements Executor { - private static final Logger LOGGER = LoggerFactory.getLogger(FixedThreadExecutor.class); - - public static final String KEY_GROUP = "servicecomb.executor.default.group"; - - public static final String KEY_THREAD = "servicecomb.executor.default.thread-per-group"; - - // to avoid multiple network thread conflicted when put tasks to executor queue - private List executorList = new ArrayList<>(); - - // for bind network thread to one executor - // it's impossible that has too many network thread, so index will not too big that less than 0 - private AtomicInteger index = new AtomicInteger(); - - private Map threadExectorMap = new ConcurrentHashMap<>(); - - public FixedThreadExecutor() { - int groupCount = DynamicPropertyFactory.getInstance().getIntProperty(KEY_GROUP, 2).get(); - int threadPerGroup = DynamicPropertyFactory.getInstance() - .getIntProperty(KEY_THREAD, Runtime.getRuntime().availableProcessors()) - .get(); - LOGGER.info("executor group {}, thread per group {}.", groupCount, threadPerGroup); - - for (int groupIdx = 0; groupIdx < groupCount; groupIdx++) { - executorList.add(Executors.newFixedThreadPool(threadPerGroup)); - } - } - - @Override - public void execute(Runnable command) { - long threadId = Thread.currentThread().getId(); - Executor executor = threadExectorMap.computeIfAbsent(threadId, this::chooseExecutor); - - executor.execute(command); - } - - private Executor chooseExecutor(long threadId) { - int idx = index.getAndIncrement() % executorList.size(); - return executorList.get(idx); - } -} diff --git a/core/src/main/java/io/servicecomb/core/executor/ReactiveExecutor.java b/core/src/main/java/io/servicecomb/core/executor/ReactiveExecutor.java deleted file mode 100644 index 371903f8245..00000000000 --- a/core/src/main/java/io/servicecomb/core/executor/ReactiveExecutor.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.executor; - -import java.util.concurrent.Executor; - -/** - * 用于在verticle中就地执行,不做多余的调度,这是性能最高的一种模型 - */ -public class ReactiveExecutor implements Executor { - - @Override - public void execute(Runnable command) { - command.run(); - } -} diff --git a/core/src/main/java/io/servicecomb/core/handler/AbstractHandlerManager.java b/core/src/main/java/io/servicecomb/core/handler/AbstractHandlerManager.java deleted file mode 100644 index df50dd61f11..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/AbstractHandlerManager.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.springframework.util.StringUtils; - -import com.netflix.config.DynamicPropertyFactory; - -import io.servicecomb.core.Handler; -import io.servicecomb.core.handler.config.Config; -import io.servicecomb.foundation.common.AbstractObjectManager; - -// key为microserviceName -public abstract class AbstractHandlerManager extends AbstractObjectManager> { - - private String defaultChainDef; - - private Config config; - - protected abstract String getName(); - - // consumer、provider端,最后一个handler,都是由框架指定的,业务不可配置 - protected abstract Handler getLastHandler(); - - // 内置默认值,用于业务未指定时的取值 - protected abstract String getInnerDefaultChainDef(); - - private void loadDefaultChainDef() { - String key = "cse.handler.chain." + getName() + ".default"; - - defaultChainDef = DynamicPropertyFactory.getInstance() - .getStringProperty(key, getInnerDefaultChainDef()) - .get(); - } - - private List> convertToChainClass(String chainDef) { - String[] handlerIds = chainDef.split(","); - Map> handlerMaps = config.getHandlerClassMap(); - List> result = new ArrayList<>(); - for (String handlerId : handlerIds) { - if (handlerId != null) { - handlerId = handlerId.trim(); - } - if (StringUtils.isEmpty(handlerId)) { - continue; - } - - Class cls = handlerMaps.get(handlerId); - if (cls == null) { - throw new Error("can not find handler :" + handlerId); - } - result.add(cls); - } - return result; - } - - public void init(Config config) { - this.config = config; - - loadDefaultChainDef(); - } - - private List createHandlerChain(String chainDef) { - List> chainClasses = convertToChainClass(chainDef); - - List handlerList = new ArrayList<>(); - handlerList.add(ShutdownHookHandler.INSTANCE); - - for (Class cls : chainClasses) { - try { - handlerList.add(cls.newInstance()); - } catch (Exception e) { - // 在启动阶段直接抛异常出来 - throw new Error(e); - } - } - handlerList.add(getLastHandler()); - return handlerList; - } - - @Override - protected String getKey(String microserviceName) { - return microserviceName; - } - - @Override - protected List create(String microserviceName) { - String chainDef = DynamicPropertyFactory.getInstance() - .getStringProperty("cse.handler.chain." + getName() + ".service." + microserviceName, - defaultChainDef) - .get(); - return createHandlerChain(chainDef); - } -} diff --git a/core/src/main/java/io/servicecomb/core/handler/ConsumerHandlerManager.java b/core/src/main/java/io/servicecomb/core/handler/ConsumerHandlerManager.java deleted file mode 100644 index fa59a004270..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/ConsumerHandlerManager.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler; - -import io.servicecomb.core.Handler; -import io.servicecomb.core.handler.impl.TransportClientHandler; - -public class ConsumerHandlerManager extends AbstractHandlerManager { - public static final ConsumerHandlerManager INSTANCE = new ConsumerHandlerManager(); - - @Override - protected String getName() { - return "Consumer"; - } - - @Override - protected String getInnerDefaultChainDef() { - return "simpleLB"; - } - - @Override - protected Handler getLastHandler() { - return TransportClientHandler.INSTANCE; - } -} diff --git a/core/src/main/java/io/servicecomb/core/handler/HandlerConfigUtils.java b/core/src/main/java/io/servicecomb/core/handler/HandlerConfigUtils.java deleted file mode 100644 index 7b4b101ead3..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/HandlerConfigUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler; - -import java.util.List; - -import org.springframework.core.io.Resource; - -import io.servicecomb.core.handler.config.Config; -import io.servicecomb.foundation.common.config.PaaSResourceUtils; -import io.servicecomb.foundation.common.config.impl.XmlLoaderUtils; - -public final class HandlerConfigUtils { - private HandlerConfigUtils() { - } - - private static Config loadConfig() throws Exception { - Config config = new Config(); - - List resList = - PaaSResourceUtils.getSortedResources("classpath*:config/cse.handler.xml", ".handler.xml"); - for (Resource res : resList) { - Config tmpConfig = XmlLoaderUtils.load(res, Config.class); - config.mergeFrom(tmpConfig); - } - - return config; - } - - public static void init() throws Exception { - Config config = loadConfig(); - ConsumerHandlerManager.INSTANCE.init(config); - ProducerHandlerManager.INSTANCE.init(config); - } -} diff --git a/core/src/main/java/io/servicecomb/core/handler/ProducerHandlerManager.java b/core/src/main/java/io/servicecomb/core/handler/ProducerHandlerManager.java deleted file mode 100644 index e0c086a74f8..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/ProducerHandlerManager.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler; - -import io.servicecomb.core.Handler; -import io.servicecomb.core.handler.impl.ProducerOperationHandler; - -public class ProducerHandlerManager extends AbstractHandlerManager { - public static final ProducerHandlerManager INSTANCE = new ProducerHandlerManager(); - - @Override - protected String getName() { - return "Provider"; - } - - @Override - protected String getInnerDefaultChainDef() { - return ""; - } - - @Override - protected Handler getLastHandler() { - return ProducerOperationHandler.INSTANCE; - } -} diff --git a/core/src/main/java/io/servicecomb/core/handler/ShutdownHookHandler.java b/core/src/main/java/io/servicecomb/core/handler/ShutdownHookHandler.java deleted file mode 100644 index 2aec46a1514..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/ShutdownHookHandler.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.handler.impl.AbstractHandler; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.Response; - -/** - * 实现调用链的优雅停止: 当调用链没有返回的时候,等待返回或者超时 - */ -public final class ShutdownHookHandler extends AbstractHandler implements Runnable { - private static final Logger LOG = LoggerFactory.getLogger(ShutdownHookHandler.class); - - public static final ShutdownHookHandler INSTANCE = new ShutdownHookHandler(); - - private final AtomicLong requestCounter = new AtomicLong(0); - - private final AtomicLong responseCounter = new AtomicLong(0); - - private final int timeout = 600; - - private final int period = 10; - - private volatile boolean shuttingDown = false; - - private ShutdownHookHandler() { - Runtime.getRuntime().addShutdownHook(new Thread(this)); - } - - public long getActiveCount() { - return requestCounter.get() - responseCounter.get(); - } - - @Override - public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception { - if (shuttingDown) { - asyncResp.handle(Response.createFail(invocation.getInvocationType(), - "shutting down in progress")); - return; - } - - // TODO:统计功能应该独立出来,在链中统计,会有各种bug - // 下面的两次catch,可能会导致一次请求,对应2次应答 - requestCounter.incrementAndGet(); - try { - invocation.next(resp -> { - try { - asyncResp.handle(resp); - responseCounter.incrementAndGet(); - } catch (Throwable e) { - responseCounter.incrementAndGet(); - throw e; - } - }); - } catch (Throwable e) { - responseCounter.incrementAndGet(); - throw e; - } - } - - @Override - public void run() { - shuttingDown = true; - LOG.warn("handler chain is shutting down"); - int time = 0; - while (getActiveCount() != 0 && time <= timeout) { - try { - TimeUnit.SECONDS.sleep(period); - } catch (InterruptedException e) { - LOG.warn(e.getMessage()); - } - time = time + period; - LOG.warn("waiting invocation to finish in seconds " + time); - } - LOG.warn("handler chain is shut down"); - } -} diff --git a/core/src/main/java/io/servicecomb/core/handler/config/Config.java b/core/src/main/java/io/servicecomb/core/handler/config/Config.java deleted file mode 100644 index 17afb74311e..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/config/Config.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler.config; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; - -import io.servicecomb.core.Handler; - -@JacksonXmlRootElement -public class Config { - // key为handler id - private Map> handlerClassMap = new HashMap<>(); - - public void mergeFrom(Config otherConfig) { - handlerClassMap.putAll(otherConfig.handlerClassMap); - } - - public Map> getHandlerClassMap() { - return this.handlerClassMap; - } - - @JacksonXmlProperty(localName = "handler") - @JacksonXmlElementWrapper(useWrapping = false) - public void setHandlerConfigList(List handlerConfigList) { - for (HandlerConfig handlerConfig : handlerConfigList) { - handlerClassMap.put(handlerConfig.getHandlerId(), handlerConfig.getClazz()); - } - } -} diff --git a/core/src/main/java/io/servicecomb/core/handler/config/HandlerConfig.java b/core/src/main/java/io/servicecomb/core/handler/config/HandlerConfig.java deleted file mode 100644 index 0a457034b73..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/config/HandlerConfig.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler.config; - -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; - -import io.servicecomb.core.Handler; - -public class HandlerConfig { - private String handlerId; - - private Class clazz; - - @JacksonXmlProperty(localName = "id", isAttribute = true) - public String getHandlerId() { - return handlerId; - } - - public void setHandlerId(String handlerId) { - this.handlerId = handlerId; - } - - @JacksonXmlProperty(localName = "class", isAttribute = true) - public Class getClazz() { - return clazz; - } - - public void setClazz(Class clazz) { - this.clazz = clazz; - } - - @SuppressWarnings("unchecked") - public void setClazz(String clazz) throws ClassNotFoundException { - this.clazz = (Class) Class.forName(clazz); - } -} diff --git a/core/src/main/java/io/servicecomb/core/handler/impl/AbstractHandler.java b/core/src/main/java/io/servicecomb/core/handler/impl/AbstractHandler.java deleted file mode 100644 index 23e37f5ad69..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/impl/AbstractHandler.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler.impl; - -import io.servicecomb.core.Handler; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.swagger.invocation.InvocationType; - -public abstract class AbstractHandler implements Handler { - @Override - public void init(MicroserviceMeta microserviceMeta, InvocationType invocationType) { - - } -} diff --git a/core/src/main/java/io/servicecomb/core/handler/impl/ProducerOperationHandler.java b/core/src/main/java/io/servicecomb/core/handler/impl/ProducerOperationHandler.java deleted file mode 100644 index 64ae7a81b4d..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/impl/ProducerOperationHandler.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler.impl; - -import io.servicecomb.core.Const; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.exception.ExceptionUtils; -import io.servicecomb.swagger.engine.SwaggerProducerOperation; -import io.servicecomb.swagger.invocation.AsyncResponse; - -public class ProducerOperationHandler extends AbstractHandler { - public static final ProducerOperationHandler INSTANCE = new ProducerOperationHandler(); - - @Override - public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception { - SwaggerProducerOperation producerOperation = - invocation.getOperationMeta().getExtData(Const.PRODUCER_OPERATION); - if (producerOperation == null) { - asyncResp.producerFail( - ExceptionUtils.producerOperationNotExist(invocation.getSchemaId(), - invocation.getOperationName())); - return; - } - producerOperation.invoke(invocation, asyncResp); - } -} diff --git a/core/src/main/java/io/servicecomb/core/handler/impl/SimpleLoadBalanceHandler.java b/core/src/main/java/io/servicecomb/core/handler/impl/SimpleLoadBalanceHandler.java deleted file mode 100644 index 3448438bda6..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/impl/SimpleLoadBalanceHandler.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler.impl; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; - -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.endpoint.EndpointsCache; -import io.servicecomb.core.exception.ExceptionUtils; -import io.servicecomb.swagger.invocation.AsyncResponse; - -/** - * 内置轮询lb,方便demo之类的场景,不必去依赖lb包 - */ -public class SimpleLoadBalanceHandler extends AbstractHandler { - private AtomicInteger index = new AtomicInteger(); - - // key为transportName - private volatile Map endpointsCacheMap = new ConcurrentHashMap<>(); - - @Override - public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception { - // 调用者未指定transport时,这里得到的是"",也直接使用,不必特殊处理 - String transportName = invocation.getConfigTransportName(); - - EndpointsCache endpointsCache = endpointsCacheMap.get(transportName); - if (endpointsCache == null) { - synchronized (this) { - endpointsCache = endpointsCacheMap.get(invocation.getConfigTransportName()); - if (endpointsCache == null) { - endpointsCache = new EndpointsCache(invocation.getAppId(), invocation.getMicroserviceName(), - invocation.getMicroserviceVersionRule(), transportName); - endpointsCacheMap.put(transportName, endpointsCache); - } - } - } - List endpoints = endpointsCache.getLatestEndpoints(); - - if (endpoints == null || endpoints.isEmpty()) { - asyncResp.consumerFail(ExceptionUtils.lbAddressNotFound(invocation.getMicroserviceName(), - invocation.getMicroserviceVersionRule(), - transportName)); - return; - } - - int idx = Math.abs(index.getAndIncrement()); - idx = idx % endpoints.size(); - Endpoint endpoint = endpoints.get(idx); - - invocation.setEndpoint(endpoint); - - invocation.next(asyncResp); - } -} diff --git a/core/src/main/java/io/servicecomb/core/handler/impl/TransportClientHandler.java b/core/src/main/java/io/servicecomb/core/handler/impl/TransportClientHandler.java deleted file mode 100644 index be29b08a156..00000000000 --- a/core/src/main/java/io/servicecomb/core/handler/impl/TransportClientHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler.impl; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.Transport; -import io.servicecomb.swagger.invocation.AsyncResponse; - -public class TransportClientHandler extends AbstractHandler { - - private static final Logger log = LoggerFactory.getLogger(TransportClientHandler.class); - - public static final TransportClientHandler INSTANCE = new TransportClientHandler(); - - @Override - public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception { - Transport transport = invocation.getTransport(); - - log.debug( - "Sending request {} to end point {}", - invocation.getOperationName(), - transport.getEndpoint().getEndpoint()); - - transport.send(invocation, asyncResp); - } -} diff --git a/core/src/main/java/io/servicecomb/core/invocation/InvocationFactory.java b/core/src/main/java/io/servicecomb/core/invocation/InvocationFactory.java deleted file mode 100644 index 1871c47bb09..00000000000 --- a/core/src/main/java/io/servicecomb/core/invocation/InvocationFactory.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.invocation; - -import io.servicecomb.core.Const; -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.provider.consumer.ReferenceConfig; -import io.servicecomb.serviceregistry.RegistryUtils; - -public final class InvocationFactory { - private InvocationFactory() { - } - - private static String getMicroserviceName() { - return RegistryUtils.getMicroservice().getServiceName(); - } - - public static Invocation forConsumer(ReferenceConfig referenceConfig, OperationMeta operationMeta, - Object[] swaggerArguments) { - Invocation invocation = new Invocation(referenceConfig, - operationMeta, - swaggerArguments); - invocation.addContext(Const.SRC_MICROSERVICE, getMicroserviceName()); - return invocation; - } - - /** - * consumer端使用,schemaMeta级别的缓存,每次调用根据operationName来执行 - */ - public static Invocation forConsumer(ReferenceConfig referenceConfig, SchemaMeta schemaMeta, String operationName, - Object[] swaggerArguments) { - OperationMeta operationMeta = schemaMeta.ensureFindOperation(operationName); - Invocation invocation = new Invocation(referenceConfig, - operationMeta, - swaggerArguments); - invocation.addContext(Const.SRC_MICROSERVICE, getMicroserviceName()); - return invocation; - } - - /** - * 为tcc场景提供的快捷方式,consumer端使用 - */ - public static Invocation forConsumer(ReferenceConfig referenceConfig, String operationQualifiedName, - Object[] swaggerArguments) { - MicroserviceMeta microserviceMeta = referenceConfig.getMicroserviceMeta(); - OperationMeta operationMeta = microserviceMeta.ensureFindOperation(operationQualifiedName); - - Invocation invocation = new Invocation(referenceConfig, - operationMeta, - swaggerArguments); - invocation.addContext(Const.SRC_MICROSERVICE, getMicroserviceName()); - return invocation; - } - - /** - * transport server收到请求时,创建invocation - */ - public static Invocation forProvider(Endpoint endpoint, - OperationMeta operationMeta, - Object[] swaggerArguments) { - return new Invocation(endpoint, - operationMeta, - swaggerArguments); - } -} diff --git a/core/src/main/java/io/servicecomb/core/provider/CseBeanPostProcessor.java b/core/src/main/java/io/servicecomb/core/provider/CseBeanPostProcessor.java deleted file mode 100644 index e256f72d1de..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/CseBeanPostProcessor.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.core.provider; - -import java.lang.reflect.Field; -import java.util.List; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.stereotype.Component; -import org.springframework.util.ReflectionUtils; - -@Component -public class CseBeanPostProcessor implements ApplicationContextAware, BeanPostProcessor { - private ApplicationContext applicationContext; - - public interface EmptyBeanPostProcessor extends BeanPostProcessor { - default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - return bean; - } - - default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - return bean; - } - } - - public interface ProviderProcessor extends EmptyBeanPostProcessor { - void processProvider(ApplicationContext applicationContext, String beanName, Object bean); - } - - public interface ConsumerFieldProcessor extends EmptyBeanPostProcessor { - void processConsumerField(ApplicationContext applicationContext, Object bean, - Field field); - } - - @Autowired(required = false) - private List providerProcessorList; - - @Autowired(required = false) - private List consumerProcessorList; - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - } - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (consumerProcessorList == null || consumerProcessorList.isEmpty()) { - return bean; - } - // 扫描所有field,处理扩展的field标注 - ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() { - public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { - for (ConsumerFieldProcessor processor : consumerProcessorList) { - processor.processConsumerField(applicationContext, bean, field); - } - } - }); - return bean; - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (providerProcessorList != null && !providerProcessorList.isEmpty()) { - for (ProviderProcessor processor : providerProcessorList) { - processor.processProvider(applicationContext, beanName, bean); - } - } - - return bean; - } -} diff --git a/core/src/main/java/io/servicecomb/core/provider/consumer/AbstractConsumerProvider.java b/core/src/main/java/io/servicecomb/core/provider/consumer/AbstractConsumerProvider.java deleted file mode 100644 index 1a1d4e1eb2f..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/consumer/AbstractConsumerProvider.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.provider.consumer; - -import javax.inject.Inject; - -import io.servicecomb.core.ConsumerProvider; -import io.servicecomb.core.definition.MicroserviceMetaManager; - -public abstract class AbstractConsumerProvider implements ConsumerProvider { - @Inject - protected MicroserviceMetaManager microserviceMetaManager; - - @Override - public void init() throws Exception { - } -} diff --git a/core/src/main/java/io/servicecomb/core/provider/consumer/ConsumerProviderManager.java b/core/src/main/java/io/servicecomb/core/provider/consumer/ConsumerProviderManager.java deleted file mode 100644 index fb4d1097283..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/consumer/ConsumerProviderManager.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.provider.consumer; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import javax.inject.Inject; - -import org.springframework.stereotype.Component; - -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicStringProperty; - -import io.servicecomb.core.Const; -import io.servicecomb.core.ConsumerProvider; -import io.servicecomb.core.definition.schema.ConsumerSchemaFactory; - -@Component -public class ConsumerProviderManager { - @Inject - private List consumerProviderList; - - @Inject - private ConsumerSchemaFactory consumerSchemaFactory; - - // key为微服务名 - private volatile Map referenceConfigMap = new ConcurrentHashMap<>(); - - public void setConsumerSchemaFactory(ConsumerSchemaFactory consumerSchemaFactory) { - this.consumerSchemaFactory = consumerSchemaFactory; - } - - public void init() throws Exception { - for (ConsumerProvider provider : consumerProviderList) { - provider.init(); - } - } - - public ReferenceConfig createReferenceConfig(String microserviceName, String microserviceVersion, - String transport) { - return new ReferenceConfig(consumerSchemaFactory, microserviceName, microserviceVersion, transport); - } - - public ReferenceConfig getReferenceConfig(String microserviceName) { - ReferenceConfig config = referenceConfigMap.get(microserviceName); - if (config == null) { - synchronized (this) { - config = referenceConfigMap.get(microserviceName); - if (config == null) { - String key = "cse.references." + microserviceName; - DynamicStringProperty versionRule = DynamicPropertyFactory.getInstance() - .getStringProperty(key + ".version-rule", Const.VERSION_RULE_LATEST); - DynamicStringProperty transport = - DynamicPropertyFactory.getInstance().getStringProperty(key + ".transport", - Const.ANY_TRANSPORT); - - config = new ReferenceConfig(consumerSchemaFactory, microserviceName, versionRule.getValue(), - transport.getValue()); - referenceConfigMap.put(microserviceName, config); - } - } - } - - return config; - } - - // 只用于测试场景 - public ReferenceConfig setTransport(String microserviceName, String transport) { - ReferenceConfig config = getReferenceConfig(microserviceName); - config.setTransport(transport); - - return config; - } -} diff --git a/core/src/main/java/io/servicecomb/core/provider/consumer/InvokerUtils.java b/core/src/main/java/io/servicecomb/core/provider/consumer/InvokerUtils.java deleted file mode 100644 index a3e8a793d68..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/consumer/InvokerUtils.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.provider.consumer; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.invocation.InvocationFactory; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.ExceptionFactory; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -public final class InvokerUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(InvokerUtils.class); - - private InvokerUtils() { - } - - public static Object syncInvoke(String microserviceName, String schemaId, String operationName, Object[] args) { - ReferenceConfig referenceConfig = ReferenceConfigUtils.getForInvoke(microserviceName); - SchemaMeta schemaMeta = referenceConfig.getMicroserviceMeta().ensureFindSchemaMeta(schemaId); - Invocation invocation = InvocationFactory.forConsumer(referenceConfig, schemaMeta, operationName, args); - return syncInvoke(invocation); - } - - public static Object syncInvoke(String microserviceName, String microserviceVersion, String transport, - String schemaId, String operationName, Object[] args) { - ReferenceConfig referenceConfig = - ReferenceConfigUtils.getForInvoke(microserviceName, microserviceVersion, transport); - SchemaMeta schemaMeta = referenceConfig.getMicroserviceMeta().ensureFindSchemaMeta(schemaId); - Invocation invocation = InvocationFactory.forConsumer(referenceConfig, schemaMeta, operationName, args); - return syncInvoke(invocation); - } - - public static Object syncInvoke(Invocation invocation) throws InvocationException { - Response response = innerSyncInvoke(invocation); - if (response.isSuccessed()) { - return response.getResult(); - } - - throw ExceptionFactory.convertConsumerException((Throwable) response.getResult()); - } - - public static Response innerSyncInvoke(Invocation invocation) { - try { - SyncResponseExecutor respExecutor = new SyncResponseExecutor(); - invocation.setResponseExecutor(respExecutor); - - invocation.next(resp -> { - respExecutor.setResponse(resp); - }); - - return respExecutor.waitResponse(); - } catch (Throwable e) { - String msg = - String.format("invoke failed, %s", invocation.getOperationMeta().getMicroserviceQualifiedName()); - LOGGER.debug(msg, e); - return Response.createConsumerFail(e); - } - } - - public static void reactiveInvoke(Invocation invocation, AsyncResponse asyncResp) { - try { - ReactiveResponseExecutor respExecutor = new ReactiveResponseExecutor(); - invocation.setResponseExecutor(respExecutor); - - invocation.next(asyncResp); - } catch (Throwable e) { - LOGGER.error("invoke failed, {}", invocation.getOperationMeta().getMicroserviceQualifiedName()); - asyncResp.consumerFail(e); - } - } - - public static Object invoke(Invocation invocation) { - if (invocation.getOperationMeta().isSync()) { - return syncInvoke(invocation); - } - - Object[] args = invocation.getArgs(); - AsyncResponse asyncResp = (AsyncResponse) args[args.length - 1]; - reactiveInvoke(invocation, asyncResp); - return null; - } -} diff --git a/core/src/main/java/io/servicecomb/core/provider/consumer/ReactiveResponseExecutor.java b/core/src/main/java/io/servicecomb/core/provider/consumer/ReactiveResponseExecutor.java deleted file mode 100644 index aa87cbf3ad9..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/consumer/ReactiveResponseExecutor.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.provider.consumer; - -import java.util.concurrent.Executor; - -/** - * reactive场景,就地执行即可 - */ -public class ReactiveResponseExecutor implements Executor { - @Override - public void execute(Runnable cmd) { - cmd.run(); - } -} diff --git a/core/src/main/java/io/servicecomb/core/provider/consumer/ReferenceConfig.java b/core/src/main/java/io/servicecomb/core/provider/consumer/ReferenceConfig.java deleted file mode 100644 index 8a1f3ae0bd1..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/consumer/ReferenceConfig.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.provider.consumer; - -import io.servicecomb.core.Const; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.schema.ConsumerSchemaFactory; - -public class ReferenceConfig { - private MicroserviceMeta microserviceMeta; - - private String microserviceVersionRule = Const.DEFAULT_VERSION_RULE; - - private String transport = Const.ANY_TRANSPORT; - - public ReferenceConfig() { - } - - public ReferenceConfig(ConsumerSchemaFactory consumerSchemaFactory, String microserviceName, - String microserviceVersionRule, String transport) { - this.microserviceMeta = consumerSchemaFactory.getOrCreateMicroserviceMeta(microserviceName, - microserviceVersionRule); - - this.microserviceVersionRule = microserviceVersionRule; - this.transport = transport; - } - - public MicroserviceMeta getMicroserviceMeta() { - return microserviceMeta; - } - - public String getMicroserviceVersionRule() { - return microserviceVersionRule; - } - - public void setMicroserviceVersionRule(String microserviceVersionRule) { - this.microserviceVersionRule = microserviceVersionRule; - } - - public String getTransport() { - return transport; - } - - public void setTransport(String transport) { - this.transport = transport; - } -} diff --git a/core/src/main/java/io/servicecomb/core/provider/consumer/ReferenceConfigUtils.java b/core/src/main/java/io/servicecomb/core/provider/consumer/ReferenceConfigUtils.java deleted file mode 100644 index 093e4ea3914..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/consumer/ReferenceConfigUtils.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.provider.consumer; - -import org.springframework.stereotype.Component; - -import io.servicecomb.core.CseContext; - -@Component -public class ReferenceConfigUtils { - private static boolean ready; - - public static void setReady(boolean ready) { - ReferenceConfigUtils.ready = ready; - } - - private static void assertIsReady() { - if (!ready) { - throw new IllegalStateException("System is not ready for remote calls. " - + "When beans are making remote calls in initialization, it's better to " - + "implement io.servicecomb.core.BootListener and do it after EventType.AFTER_REGISTRY."); - } - } - - public static ReferenceConfig getForInvoke(String microserviceName) { - assertIsReady(); - - return CseContext.getInstance().getConsumerProviderManager().getReferenceConfig(microserviceName); - } - - public static ReferenceConfig getForInvoke(String microserviceName, String microserviceVersion, - String transport) { - assertIsReady(); - - return CseContext.getInstance().getConsumerProviderManager().createReferenceConfig(microserviceName, - microserviceVersion, - transport); - } -} diff --git a/core/src/main/java/io/servicecomb/core/provider/consumer/SyncResponseExecutor.java b/core/src/main/java/io/servicecomb/core/provider/consumer/SyncResponseExecutor.java deleted file mode 100644 index bd6cce1b05c..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/consumer/SyncResponseExecutor.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.provider.consumer; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; - -import io.servicecomb.swagger.invocation.Response; - -/** - * 业务线程在阻塞等待着,不必另起线程 - * 将应答流程包装为Runnable,先唤醒业务线程,再在业务线程中执行runnable - */ -public class SyncResponseExecutor implements Executor { - private CountDownLatch latch; - - private Runnable cmd; - - private Response response; - - public SyncResponseExecutor() { - latch = new CountDownLatch(1); - } - - @Override - public void execute(Runnable cmd) { - this.cmd = cmd; - - // one network thread, many connections, then this notify will be performance bottlenecks - // if save to a queue, and other thread(s) to invoke countDown, will get good performance - // but if have multile network thread, this "optimization" will reduce performance - // now not change this. - latch.countDown(); - } - - public Response waitResponse() throws InterruptedException { - // TODO:增加配置,指定超时时间 - latch.await(); - // cmd为null,是没走execute,直接返回的场景 - if (cmd != null) { - cmd.run(); - } - - return response; - } - - public void setResponse(Response response) { - this.response = response; - if (cmd == null) { - // 走到这里,没有cmd - // 说明没走到网络线程,直接就返回了 - // 或者在网络线程中没使用execute的方式返回,这会导致返回流程在网络线程中执行 - // 虽然不合适,但是也不应该导致业务线程无法唤醒 - latch.countDown(); - } - } -} diff --git a/core/src/main/java/io/servicecomb/core/provider/producer/AbstractProducerProvider.java b/core/src/main/java/io/servicecomb/core/provider/producer/AbstractProducerProvider.java deleted file mode 100644 index a97871bca38..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/producer/AbstractProducerProvider.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.provider.producer; - -import io.servicecomb.core.ProducerProvider; - -public abstract class AbstractProducerProvider implements ProducerProvider { -} diff --git a/core/src/main/java/io/servicecomb/core/provider/producer/ProducerMeta.java b/core/src/main/java/io/servicecomb/core/provider/producer/ProducerMeta.java deleted file mode 100644 index 7ee07c465d1..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/producer/ProducerMeta.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.provider.producer; - -public class ProducerMeta { - private String schemaId; - - private Object instance; - - // 因为aop的存在,instance的class不一定等于instanceClass - private Class instanceClass; - - public ProducerMeta() { - } - - public ProducerMeta(String schemaId, Object instance, Class instanceClass) { - this.schemaId = schemaId; - this.instance = instance; - this.instanceClass = instanceClass; - } - - public String getSchemaId() { - return schemaId; - } - - public void setSchemaId(String schemaId) { - this.schemaId = schemaId; - } - - public Object getInstance() { - return instance; - } - - public void setInstance(Object instance) { - this.instance = instance; - } - - public Class getInstanceClass() { - return instanceClass; - } - - public void setInstanceClass(Class instanceClass) { - this.instanceClass = instanceClass; - } -} diff --git a/core/src/main/java/io/servicecomb/core/provider/producer/ProducerProviderManager.java b/core/src/main/java/io/servicecomb/core/provider/producer/ProducerProviderManager.java deleted file mode 100644 index 2c6931426dc..00000000000 --- a/core/src/main/java/io/servicecomb/core/provider/producer/ProducerProviderManager.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.provider.producer; - -import java.util.List; - -import javax.inject.Inject; - -import org.springframework.stereotype.Component; - -import io.servicecomb.core.ProducerProvider; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.definition.SchemaUtils; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.Microservice; - -@Component -public class ProducerProviderManager { - @Inject - private List producerProviderList; - - @Inject - private MicroserviceMetaManager microserviceMetaManager; - - public void init() throws Exception { - for (ProducerProvider provider : producerProviderList) { - provider.init(); - } - - Microservice microservice = RegistryUtils.getMicroservice(); - MicroserviceMeta microserviceMeta = microserviceMetaManager.getOrCreateMicroserviceMeta(microservice); - for (SchemaMeta schemaMeta : microserviceMeta.getSchemaMetas()) { - String content = SchemaUtils.swaggerToString(schemaMeta.getSwagger()); - microservice.addSchema(schemaMeta.getSchemaId(), content); - } - } -} diff --git a/core/src/main/java/io/servicecomb/core/transport/AbstractTransport.java b/core/src/main/java/io/servicecomb/core/transport/AbstractTransport.java deleted file mode 100644 index abcf35b3d69..00000000000 --- a/core/src/main/java/io/servicecomb/core/transport/AbstractTransport.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.transport; - -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import java.util.stream.Collectors; - -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.message.BasicNameValuePair; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.config.DynamicPropertyFactory; - -import io.servicecomb.core.Const; -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Transport; -import io.servicecomb.foundation.common.exceptions.ServiceCombException; -import io.servicecomb.foundation.common.net.NetUtils; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.foundation.vertx.VertxUtils; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.vertx.core.Vertx; - -public abstract class AbstractTransport implements Transport { - private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTransport.class); - - /** - * 用于参数传递:比如向RestServerVerticle传递endpoint地址。 - */ - public static final String ENDPOINT_KEY = "cse.endpoint"; - - private static final long DEFAULT_TIMEOUT_MILLIS = 30000; - - private static Long msReqeustTimeout = null; - - public static long getRequestTimeout() { - if (msReqeustTimeout != null) { - return msReqeustTimeout; - } - - long msTimeout = DynamicPropertyFactory.getInstance() - .getLongProperty("cse.request.timeout", DEFAULT_TIMEOUT_MILLIS) - .get(); - if (msTimeout <= 0) { - msTimeout = DEFAULT_TIMEOUT_MILLIS; - } - - msReqeustTimeout = msTimeout; - return msReqeustTimeout; - } - - // 所有transport使用同一个vertx实例,避免创建太多的线程 - protected Vertx transportVertx = VertxUtils.getOrCreateVertxByName("transport", null); - - protected Endpoint endpoint; - - protected Endpoint publishEndpoint; - - @Override - public Endpoint getPublishEndpoint() { - return publishEndpoint; - } - - @Override - public Endpoint getEndpoint() { - return endpoint; - } - - protected void setListenAddressWithoutSchema(String addressWithoutSchema) { - setListenAddressWithoutSchema(addressWithoutSchema, null); - } - - /** - * 将配置的URI转换为endpoint - * addressWithoutSchema 配置的URI,没有schema部分 - */ - protected void setListenAddressWithoutSchema(String addressWithoutSchema, - Map pairs) { - addressWithoutSchema = genAddressWithoutSchema(addressWithoutSchema, pairs); - - this.endpoint = new Endpoint(this, NetUtils.getRealListenAddress(getName(), addressWithoutSchema)); - if (this.endpoint.getEndpoint() != null) { - this.publishEndpoint = new Endpoint(this, RegistryUtils.getPublishAddress(getName(), - addressWithoutSchema)); - } else { - this.publishEndpoint = null; - } - } - - private String genAddressWithoutSchema(String addressWithoutSchema, Map pairs) { - if (addressWithoutSchema == null || pairs == null || pairs.isEmpty()) { - return addressWithoutSchema; - } - - int idx = addressWithoutSchema.indexOf('?'); - if (idx == -1) { - addressWithoutSchema += "?"; - } else { - addressWithoutSchema += "&"; - } - - String encodedQuery = URLEncodedUtils.format(pairs.entrySet().stream().map(entry -> { - return new BasicNameValuePair(entry.getKey(), entry.getValue()); - }).collect(Collectors.toList()), StandardCharsets.UTF_8.name()); - - if (!RegistryUtils.getServiceRegistry().getFeatures().isCanEncodeEndpoint()) { - addressWithoutSchema = genAddressWithoutSchemaForOldSC(addressWithoutSchema, encodedQuery); - } else { - addressWithoutSchema += encodedQuery; - } - - return addressWithoutSchema; - } - - private String genAddressWithoutSchemaForOldSC(String addressWithoutSchema, String encodedQuery) { - // old service center do not support encodedQuery - // sdk must query service center's version, and determine if encode query - // traced by JAV-307 - try { - LOGGER.warn("Service center do not support encoded query, so we use unencoded query, " - + "this caused not support chinese/space (and maybe other char) in query value."); - String decodedQuery = URLDecoder.decode(encodedQuery, StandardCharsets.UTF_8.name()); - addressWithoutSchema += decodedQuery; - } catch (UnsupportedEncodingException e) { - // never happended - throw new ServiceCombException("Failed to decode query.", e); - } - - try { - // make sure consumer can handle this endpoint - new URI(Const.RESTFUL + "://" + addressWithoutSchema); - } catch (URISyntaxException e) { - throw new ServiceCombException( - "current service center not support encoded endpoint, please do not use chinese or space or anything need to be encoded.", - e); - } - return addressWithoutSchema; - } - - @Override - public Object parseAddress(String address) { - if (address == null) { - return null; - } - return new URIEndpointObject(address); - } -} diff --git a/core/src/main/java/io/servicecomb/core/transport/TransportManager.java b/core/src/main/java/io/servicecomb/core/transport/TransportManager.java deleted file mode 100644 index fb64f516bdc..00000000000 --- a/core/src/main/java/io/servicecomb/core/transport/TransportManager.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.transport; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import javax.inject.Inject; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Transport; -import io.servicecomb.foundation.common.exceptions.ServiceCombException; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.Microservice; - -@Component -public class TransportManager { - private static final Logger LOGGER = LoggerFactory.getLogger(TransportManager.class); - - @Inject - private List transports; - - private Map transportMap = new HashMap<>(); - - public void setTransports(List transports) { - this.transports = transports; - } - - public void init() throws Exception { - buildTransportMap(); - - for (Transport transport : transportMap.values()) { - if (transport.init()) { - Endpoint endpoint = transport.getPublishEndpoint(); - if (endpoint != null && endpoint.getEndpoint() != null) { - LOGGER.info("endpoint to publish: {}", endpoint.getEndpoint()); - Microservice microservice = RegistryUtils.getMicroservice(); - microservice.getIntance().getEndpoints().add(endpoint.getEndpoint()); - } - continue; - } - } - } - - protected void buildTransportMap() { - Map> groups = groupByName(); - - for (Entry> entry : groups.entrySet()) { - List group = entry.getValue(); - - checkTransportGroup(group); - Transport transport = chooseOneTransport(group); - transportMap.put(transport.getName(), transport); - } - } - - protected Transport chooseOneTransport(List group) { - group.sort((t1, t2) -> { - return t1.getOrder() - t2.getOrder(); - }); - - for (Transport transport : group) { - if (transport.canInit()) { - LOGGER.info("choose {} for {}.", transport.getClass().getName(), transport.getName()); - return transport; - } - } - - throw new ServiceCombException( - String.format("all transport named %s refused to init.", group.get(0).getName())); - } - - protected void checkTransportGroup(List group) { - // order value must be different, otherwise, maybe will choose a random transport - Map orderMap = new HashMap<>(); - for (Transport transport : group) { - Transport existTransport = orderMap.putIfAbsent(transport.getOrder(), transport); - if (existTransport != null) { - throw new ServiceCombException(String.format("%s and %s have the same order %d", - existTransport.getClass().getName(), - transport.getClass().getName(), - transport.getOrder())); - } - } - } - - protected Map> groupByName() { - Map> groups = new HashMap<>(); - for (Transport transport : transports) { - List list = groups.computeIfAbsent(transport.getName(), name -> { - return new ArrayList<>(); - }); - list.add(transport); - } - return groups; - } - - public Transport findTransport(String transportName) { - return transportMap.get(transportName); - } -} diff --git a/core/src/main/java/io/servicecomb/core/unittest/UnitTestMeta.java b/core/src/main/java/io/servicecomb/core/unittest/UnitTestMeta.java deleted file mode 100644 index ded46040c86..00000000000 --- a/core/src/main/java/io/servicecomb/core/unittest/UnitTestMeta.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.unittest; - -import java.util.Collections; -import java.util.List; - -import org.mockito.Mockito; -import org.springframework.context.ApplicationContext; - -import io.servicecomb.core.CseContext; -import io.servicecomb.core.Handler; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.definition.loader.SchemaListenerManager; -import io.servicecomb.core.definition.loader.SchemaLoader; -import io.servicecomb.core.definition.schema.ConsumerSchemaFactory; -import io.servicecomb.core.handler.ConsumerHandlerManager; -import io.servicecomb.core.handler.ProducerHandlerManager; -import io.servicecomb.core.handler.config.Config; -import io.servicecomb.core.handler.impl.SimpleLoadBalanceHandler; -import io.servicecomb.core.provider.consumer.ConsumerProviderManager; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; -import io.swagger.models.Swagger; -import mockit.Mock; -import mockit.MockUp; - -public class UnitTestMeta { - private static boolean inited = false; - - private static MicroserviceMetaManager microserviceMetaManager = new MicroserviceMetaManager(); - - private static SchemaListenerManager schemaListenerManager = new SchemaListenerManager(); - - @SuppressWarnings("unchecked") - public static synchronized void init() { - if (inited) { - return; - } - - ConsumerProviderManager consumerProviderManager = new ConsumerProviderManager(); - - ConsumerSchemaFactory consumerSchemaFactory = new ConsumerSchemaFactory() { - @Override - protected Microservice findMicroservice(MicroserviceMeta microserviceMeta, - String microserviceVersionRule) { - return null; - } - }; - consumerSchemaFactory.setMicroserviceMetaManager(microserviceMetaManager); - consumerSchemaFactory.setSchemaListenerManager(schemaListenerManager); - - consumerProviderManager.setConsumerSchemaFactory(consumerSchemaFactory); - - CseContext.getInstance().setConsumerProviderManager(consumerProviderManager); - CseContext.getInstance().setConsumerSchemaFactory(consumerSchemaFactory); - CseContext.getInstance().setSchemaListenerManager(schemaListenerManager); - - Config config = new Config(); - Class cls = SimpleLoadBalanceHandler.class; - config.getHandlerClassMap().put("simpleLB", (Class) cls); - ProducerHandlerManager.INSTANCE.init(config); - ConsumerHandlerManager.INSTANCE.init(config); - - ApplicationContext applicationContext = Mockito.mock(ApplicationContext.class); - Mockito.when(applicationContext.getBean(Mockito.anyString())).thenReturn(null); - BeanUtils.setContext(applicationContext); - inited = true; - } - - static { - init(); - } - - private SchemaLoader schemaLoader = new SchemaLoader() { - public void putSelfBasePathIfAbsent(String microserviceName, String basePath) { - } - }; - - - public UnitTestMeta() { - - new MockUp() { - @Mock - public List getOrCreate(String name) { - return Collections.emptyList(); - } - }; - new MockUp() { - @Mock - public List getOrCreate(String name) { - return Collections.emptyList(); - } - }; - - schemaLoader.setMicroserviceMetaManager(microserviceMetaManager); - } - - - public MicroserviceMetaManager getMicroserviceMetaManager() { - return microserviceMetaManager; - } - - public SchemaMeta getOrCreateSchemaMeta(Class impl) { - return getOrCreateSchemaMeta("app", "test", impl.getName(), impl); - } - - public SchemaMeta getOrCreateSchemaMeta(String appId, String microserviceName, String schemaId, Class impl) { - String longName = appId + ":" + microserviceName; - MicroserviceMeta microserviceMeta = microserviceMetaManager.getOrCreateMicroserviceMeta(longName); - SchemaMeta schemaMeta = microserviceMeta.findSchemaMeta(schemaId); - if (schemaMeta != null) { - return schemaMeta; - } - - Swagger swagger = UnitTestSwaggerUtils.generateSwagger(impl).getSwagger(); - return schemaLoader.registerSchema(microserviceMeta, schemaId, swagger); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/BootListener.java b/core/src/main/java/org/apache/servicecomb/core/BootListener.java new file mode 100644 index 00000000000..ea8314e2e1d --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/BootListener.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.springframework.core.Ordered; + +public interface BootListener extends Ordered { + enum EventType { + BEFORE_FILTER, + AFTER_FILTER, + BEFORE_PRODUCER_PROVIDER, + AFTER_PRODUCER_PROVIDER, + BEFORE_CONSUMER_PROVIDER, + AFTER_CONSUMER_PROVIDER, + BEFORE_TRANSPORT, + AFTER_TRANSPORT, + BEFORE_REGISTRY, + AFTER_REGISTRY, + BEFORE_CLOSE, + AFTER_CLOSE + } + + class BootEvent { + private SCBEngine scbEngine; + + private EventType eventType; + + public BootEvent() { + } + + public BootEvent(SCBEngine scbEngine, EventType eventType) { + this.scbEngine = scbEngine; + this.eventType = eventType; + } + + public SCBEngine getScbEngine() { + return scbEngine; + } + + public BootEvent setScbEngine(SCBEngine scbEngine) { + this.scbEngine = scbEngine; + return this; + } + + public EventType getEventType() { + return eventType; + } + + public BootEvent setEventType(EventType eventType) { + this.eventType = eventType; + return this; + } + } + + @Override + default int getOrder() { + return 0; + } + + default void onBootEvent(BootEvent event) { + switch (event.eventType) { + case BEFORE_FILTER: + onBeforeFilter(event); + return; + case AFTER_FILTER: + onAfterFilter(event); + return; + case BEFORE_PRODUCER_PROVIDER: + onBeforeProducerProvider(event); + return; + case AFTER_PRODUCER_PROVIDER: + onAfterProducerProvider(event); + return; + case BEFORE_CONSUMER_PROVIDER: + onBeforeConsumerProvider(event); + return; + case AFTER_CONSUMER_PROVIDER: + onAfterConsumerProvider(event); + return; + case BEFORE_TRANSPORT: + onBeforeTransport(event); + return; + case AFTER_TRANSPORT: + onAfterTransport(event); + return; + case BEFORE_REGISTRY: + onBeforeRegistry(event); + return; + case AFTER_REGISTRY: + onAfterRegistry(event); + return; + case BEFORE_CLOSE: + onBeforeClose(event); + return; + case AFTER_CLOSE: + onAfterClose(event); + return; + default: + throw new IllegalStateException("unknown boot event type: " + event.eventType); + } + } + + default void onBeforeFilter(BootEvent event) { + + } + + default void onAfterFilter(BootEvent event) { + + } + + default void onBeforeProducerProvider(BootEvent event) { + + } + + default void onAfterProducerProvider(BootEvent event) { + + } + + default void onBeforeConsumerProvider(BootEvent event) { + + } + + default void onAfterConsumerProvider(BootEvent event) { + + } + + default void onBeforeTransport(BootEvent event) { + + } + + default void onAfterTransport(BootEvent event) { + + } + + default void onBeforeRegistry(BootEvent event) { + + } + + default void onAfterRegistry(BootEvent event) { + + } + + default void onBeforeClose(BootEvent event) { + + } + + default void onAfterClose(BootEvent event) { + + } +} 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 new file mode 100644 index 00000000000..01f6f1129d5 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/Endpoint.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.api.DiscoveryInstance; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; + +/** + * Internal representation for microservice instance address. + */ +public class Endpoint { + // 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 DiscoveryInstance instance; + + // 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, DiscoveryInstance instance) { + this.transport = transport; + this.endpoint = endpoint; + this.instance = instance; + this.address = transport.parseAddress(this.endpoint); + } + + public Endpoint(Transport transport, String endpoint, StatefulDiscoveryInstance instance, Object address) { + this.transport = transport; + this.endpoint = endpoint; + this.instance = instance; + this.address = address; + } + + public String getEndpoint() { + return endpoint; + } + + public DiscoveryInstance getMicroserviceInstance() { + return instance; + } + + public Transport getTransport() { + return transport; + } + + public Object getAddress() { + return address; + } + + @Override + public String toString() { + return endpoint; + } + + @Override + public boolean equals(Object o) { + if (o instanceof Endpoint) { + return this.endpoint.equals(((Endpoint) o).getEndpoint()); + } + return false; + } + + @Override + public int hashCode() { + return this.endpoint.hashCode(); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/Invocation.java b/core/src/main/java/org/apache/servicecomb/core/Invocation.java new file mode 100644 index 00000000000..067fe6558f4 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/Invocation.java @@ -0,0 +1,480 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.reflect.Method; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.commons.lang3.StringUtils; +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.event.InvocationBusinessFinishEvent; +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.InvocationStartEvent; +import org.apache.servicecomb.core.event.InvocationStartSendRequestEvent; +import org.apache.servicecomb.core.event.InvocationTimeoutCheckEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.core.provider.consumer.InvokerUtils; +import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; +import org.apache.servicecomb.core.tracing.TraceIdGenerator; +import org.apache.servicecomb.core.tracing.TraceIdLogger; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.common.utils.AsyncUtils; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; +import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +import com.fasterxml.jackson.databind.JavaType; + +public class Invocation extends SwaggerInvocation { + private static final Collection TRACE_ID_GENERATORS = loadTraceIdGenerators(); + + protected static final AtomicLong INVOCATION_ID = new AtomicLong(); + + static Collection loadTraceIdGenerators() { + return SPIServiceUtils.getPriorityHighestServices(TraceIdGenerator::getName, TraceIdGenerator.class); + } + + protected ReferenceConfig referenceConfig; + + private InvocationRuntimeType invocationRuntimeType; + + // 本次调用对应的schemaMeta + private SchemaMeta schemaMeta; + + // 本次调用对应的operationMeta + private OperationMeta operationMeta; + + // loadbalance查询得到的地址,由transport client使用 + // 之所以不放在handlerContext中,是因为这属于核心数据,没必要走那样的机制 + private Endpoint endpoint; + + // 只用于handler之间传递数据,是本地数据 + private final Map handlerContext = localContext; + + // 应答的处理器 + // 同步模式:避免应答在网络线程中处理解码等等业务级逻辑 + private Executor responseExecutor; + + private volatile boolean sync = true; + + private final InvocationStageTrace invocationStageTrace = new InvocationStageTrace(this); + + private HttpServletRequestEx requestEx; + + private volatile boolean finished; + + private volatile long invocationId; + + private TraceIdLogger traceIdLogger; + + private Map invocationArguments = Collections.emptyMap(); + + private Object[] producerArguments; + + private Map swaggerArguments = Collections.emptyMap(); + + public Invocation() { + // An empty invocation, used to mock or some other scenario do not need operation information. + traceIdLogger = new TraceIdLogger(this); + } + + public Invocation(ReferenceConfig referenceConfig, OperationMeta operationMeta, + InvocationRuntimeType invocationRuntimeType, + Map swaggerArguments) { + this.invocationType = InvocationType.CONSUMER; + this.referenceConfig = referenceConfig; + this.invocationRuntimeType = invocationRuntimeType; + init(operationMeta, swaggerArguments); + } + + public Invocation(Endpoint endpoint, OperationMeta operationMeta, Map swaggerArguments) { + this.invocationType = InvocationType.PROVIDER; + this.invocationRuntimeType = operationMeta.buildBaseProviderRuntimeType(); + this.endpoint = endpoint; + init(operationMeta, swaggerArguments); + } + + private void init(OperationMeta operationMeta, Map swaggerArguments) { + this.invocationId = INVOCATION_ID.getAndIncrement(); + this.schemaMeta = operationMeta.getSchemaMeta(); + this.operationMeta = operationMeta; + this.setSwaggerArguments(swaggerArguments); + traceIdLogger = new TraceIdLogger(this); + } + + public String getTransportName() { + if (endpoint == null || endpoint.getTransport() == null) { + return null; + } + return endpoint.getTransport().getName(); + } + + public Transport getTransport() { + if (endpoint == null) { + throw new IllegalStateException( + "Endpoint is empty. Forget to configure \"loadbalance\" in consumer handler chain?"); + } + return endpoint.getTransport(); + } + + public Executor getResponseExecutor() { + return responseExecutor; + } + + public void setResponseExecutor(Executor responseExecutor) { + this.responseExecutor = responseExecutor; + } + + public SchemaMeta getSchemaMeta() { + return schemaMeta; + } + + public OperationMeta getOperationMeta() { + return operationMeta; + } + + public Map getInvocationArguments() { + return this.invocationArguments; + } + + public Map getSwaggerArguments() { + return this.swaggerArguments; + } + + public Object getInvocationArgument(String name) { + return this.invocationArguments.get(name); + } + + public Object getSwaggerArgument(String name) { + return this.swaggerArguments.get(name); + } + + public void setInvocationArguments(Map invocationArguments) { + if (invocationArguments == null) { + // Empty arguments + this.invocationArguments = new HashMap<>(0); + return; + } + this.invocationArguments = invocationArguments; + + buildSwaggerArguments(); + } + + private void buildSwaggerArguments() { + if (!this.invocationRuntimeType.isRawConsumer()) { + this.swaggerArguments = this.invocationRuntimeType.getArgumentsMapper() + .invocationArgumentToSwaggerArguments(this, + this.invocationArguments); + } else { + this.swaggerArguments = invocationArguments; + } + } + + public void setSwaggerArguments(Map swaggerArguments) { + if (swaggerArguments == null) { + // Empty arguments + this.swaggerArguments = new HashMap<>(0); + return; + } + this.swaggerArguments = swaggerArguments; + + buildInvocationArguments(); + } + + private void buildInvocationArguments() { + if (operationMeta.getSwaggerProducerOperation() != null && !isEdge()) { + this.invocationArguments = operationMeta.getSwaggerProducerOperation().getArgumentsMapper() + .swaggerArgumentToInvocationArguments(this, + swaggerArguments); + } else { + this.invocationArguments = swaggerArguments; + } + } + + public Object[] toProducerArguments() { + if (producerArguments != null) { + return producerArguments; + } + + Method method = operationMeta.getSwaggerProducerOperation().getProducerMethod(); + Object[] args = new Object[method.getParameterCount()]; + for (int i = 0; i < method.getParameterCount(); i++) { + args[i] = this.invocationArguments.get(method.getParameters()[i].getName()); + } + return producerArguments = args; + } + + public Endpoint getEndpoint() { + return endpoint; + } + + public void setEndpoint(Endpoint endpoint) { + this.endpoint = endpoint; + } + + public Map getHandlerContext() { + return handlerContext; + } + + public String getSchemaId() { + return schemaMeta.getSchemaId(); + } + + 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(); + } + + public String getRealTransportName() { + return (endpoint != null) ? endpoint.getTransport().getName() : getConfigTransportName(); + } + + public String getMicroserviceName() { + return schemaMeta.getMicroserviceName(); + } + + public String getAppId() { + return schemaMeta.getMicroserviceMeta().getAppId(); + } + + public MicroserviceMeta getMicroserviceMeta() { + return schemaMeta.getMicroserviceMeta(); + } + + public InvocationRuntimeType getInvocationRuntimeType() { + return this.invocationRuntimeType; + } + + public JavaType findResponseType(int statusCode) { + return this.invocationRuntimeType.findResponseType(statusCode); + } + + public void setSuccessResponseType(JavaType javaType) { + this.invocationRuntimeType.setSuccessResponseType(javaType); + } + + @Override + public String getInvocationQualifiedName() { + return invocationType.name() + " " + getRealTransportName() + " " + + getOperationMeta().getMicroserviceQualifiedName(); + } + + public String getMicroserviceQualifiedName() { + return operationMeta.getMicroserviceQualifiedName(); + } + + protected void initTraceId() { + for (TraceIdGenerator traceIdGenerator : TRACE_ID_GENERATORS) { + initTraceId(traceIdGenerator); + } + } + + protected void initTraceId(TraceIdGenerator traceIdGenerator) { + if (!StringUtils.isEmpty(getTraceId(traceIdGenerator.getTraceIdKeyName()))) { + // if invocation context contains traceId, nothing needed to do + return; + } + + if (requestEx == null) { + // it's a new consumer invocation, must generate a traceId + addContext(traceIdGenerator.getTraceIdKeyName(), traceIdGenerator.generate()); + return; + } + + String traceId = requestEx.getHeader(traceIdGenerator.getTraceIdKeyName()); + if (!StringUtils.isEmpty(traceId)) { + // if request header contains traceId, save traceId into invocation context + addContext(traceIdGenerator.getTraceIdKeyName(), traceId); + return; + } + + // if traceId not found, generate a traceId + addContext(traceIdGenerator.getTraceIdKeyName(), traceIdGenerator.generate()); + } + + public void onStart() { + initTraceId(); + EventManager.post(new InvocationStartEvent(this)); + } + + public void onStart(HttpServletRequestEx requestEx) { + this.requestEx = requestEx; + + onStart(); + } + + public void onStartSendRequest() { + EventManager.post(new InvocationStartSendRequestEvent(this)); + } + + public void onBusinessMethodStart() { + invocationStageTrace.startBusinessExecute(); + EventManager.post(new InvocationBusinessMethodStartEvent(this)); + } + + public void onEncodeResponseStart(Response response) { + invocationStageTrace.startProviderEncodeResponse(); + EventManager.post(new InvocationEncodeResponseStartEvent(this, response)); + } + + public void onEncodeResponseFinish() { + invocationStageTrace.finishProviderEncodeResponse(); + } + + public void onBusinessFinish() { + invocationStageTrace.finishBusinessExecute(); + EventManager.post(new InvocationBusinessFinishEvent(this)); + } + + public void onFinish(Response response) { + if (finished) { + // avoid to post repeated event + return; + } + + finished = true; + invocationStageTrace.finish(); + EventManager.post(new InvocationFinishEvent(this, response)); + } + + // for retry, reset invocation and try it again + public void reset() { + finished = false; + } + + public boolean isFinished() { + return finished; + } + + public boolean isSync() { + return sync; + } + + public void setSync(boolean sync) { + this.sync = sync; + } + + public boolean isConsumer() { + return InvocationType.CONSUMER.equals(invocationType); + } + + public boolean isProducer() { + return InvocationType.PROVIDER.equals(invocationType); + } + + public boolean isEdge() { + return InvocationType.EDGE.equals(invocationType); + } + + public void setEdge() { + this.invocationType = InvocationType.EDGE; + } + + public long getInvocationId() { + return invocationId; + } + + public TraceIdLogger getTraceIdLogger() { + return this.traceIdLogger; + } + + public HttpServletRequestEx getRequestEx() { + return requestEx; + } + + public InvocationStageTrace getInvocationStageTrace() { + return invocationStageTrace; + } + + public String getTraceId() { + return getContext(CoreConst.TRACE_ID_NAME); + } + + public String getTraceId(String traceIdName) { + return getContext(traceIdName); + } + + // ensure sync consumer invocation response flow not run in eventLoop + public CompletableFuture optimizeSyncConsumerThread(CompletableFuture future) { + if (sync && !InvokerUtils.isInEventLoop()) { + return AsyncUtils.tryCatchSupplier(() -> InvokerUtils.toSync(future, getWaitTime())); + } + + return future; + } + + public long getWaitTime() { + if (getOperationMeta().getConfig().getMsInvocationTimeout() > 0) { + // if invocation timeout configured, use it. + return getOperationMeta().getConfig().getMsInvocationTimeout(); + } + + // 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 getOperationMeta().getConfig().getMsRequestTimeout() * 2; + } + + /** + * Check if invocation is timeout. + * + * NOTICE: this method only trigger event to ask the target checker to do the real check. So this method + * will only take effect when timeout checker is enabled. + * + * e.g. InvocationTimeoutBootListener.ENABLE_TIMEOUT_CHECK is enabled. + * + * @throws InvocationException if timeout, throw an exception. Will not throw exception twice if this method called + * after timeout. + */ + public void ensureInvocationNotTimeout() throws InvocationException { + EventManager.post(new InvocationTimeoutCheckEvent(this)); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/NonSwaggerInvocation.java b/core/src/main/java/org/apache/servicecomb/core/NonSwaggerInvocation.java new file mode 100644 index 00000000000..e7f903e7552 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/NonSwaggerInvocation.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.core; + +public class NonSwaggerInvocation extends Invocation { + private final String appId; + + private final String microserviceName; + + public NonSwaggerInvocation(String appId, String microserviceName) { + this.appId = appId; + this.microserviceName = microserviceName; + } + + @Override + public String getSchemaId() { + return "third-schema"; + } + + @Override + public String getOperationName() { + return "third-operation"; + } + + @Override + public String getInvocationQualifiedName() { + return microserviceName; + } + + @Override + public String getConfigTransportName() { + return CoreConst.RESTFUL; + } + + @Override + public String getMicroserviceName() { + return microserviceName; + } + + @Override + public String getAppId() { + return appId; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/ProducerProvider.java b/core/src/main/java/org/apache/servicecomb/core/ProducerProvider.java new file mode 100644 index 00000000000..4fbbe799b03 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/ProducerProvider.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.core; + +import java.util.List; + +import org.apache.servicecomb.core.provider.producer.ProducerMeta; + +public interface ProducerProvider { + List init(); + + String getName(); +} diff --git a/core/src/main/java/org/apache/servicecomb/core/SCBApplicationListener.java b/core/src/main/java/org/apache/servicecomb/core/SCBApplicationListener.java new file mode 100644 index 00000000000..3bd5bd5d8f7 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/SCBApplicationListener.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.core; + +import org.apache.servicecomb.config.priority.PriorityPropertyManager; +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.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, 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) { + // same object. avoid initialize many times. + return; + } + this.applicationContext = applicationContext; + BeanUtils.setContext(applicationContext); + HttpClients.load(); + } + + @Override + public void setEnvironment(Environment environment) { + scbEngine.init(); + } + + public void setInitEventClass(Class initEventClass) { + this.initEventClass = initEventClass; + } + + @Override + public int getOrder() { + // should run before default listener, eg: ZuulConfiguration + return -1000; + } + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (initEventClass.isInstance(event)) { + if (applicationContext instanceof AbstractApplicationContext) { + ((AbstractApplicationContext) applicationContext).registerShutdownHook(); + } + + scbEngine.setPriorityPropertyManager(applicationContext.getBean(PriorityPropertyManager.class)); + scbEngine.setFilterChainsManager(applicationContext.getBean(FilterChainsManager.class)); + scbEngine.getProducerProviderManager().getProducerProviderList() + .addAll(applicationContext.getBeansOfType(ProducerProvider.class).values()); + + scbEngine.run(); + } else if (event instanceof ContextClosedEvent) { + if (SCBEngine.getInstance() != null) { + SCBEngine.getInstance().destroy(); + } + } + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java b/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java new file mode 100644 index 00000000000..c69521698af --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java @@ -0,0 +1,519 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.commons.lang3.StringUtils; +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.MicroserviceMeta; +import org.apache.servicecomb.core.definition.MicroserviceVersionsMeta; +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.event.EventManager; +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.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 jakarta.ws.rs.core.Response.Status; + +public class SCBEngine { + private static final Logger LOGGER = LoggerFactory.getLogger(SCBEngine.class); + + public static final String CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC = "servicecomb.boot.turnDown.waitInSeconds"; + + 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; + + private FilterChainsManager filterChainsManager; + + private ProducerProviderManager producerProviderManager; + + private ConsumerProviderManager consumerProviderManager; + + private MicroserviceMeta producerMicroserviceMeta; + + private TransportManager transportManager; + + private List bootListeners; + + private final AtomicLong invocationStartedCounter = new AtomicLong(); + + private final AtomicLong invocationFinishedCounter = new AtomicLong(); + + private volatile SCBStatus status = SCBStatus.DOWN; + + private final EventBus eventBus; + + private ExecutorManager executorManager; + + private PriorityPropertyManager priorityPropertyManager; + + private List bootUpInformationCollectors; + + private final SwaggerEnvironment swaggerEnvironment = new SwaggerEnvironment(); + + private OpenAPIRegistryManager openAPIRegistryManager; + + private RegistrationManager registrationManager; + + private DiscoveryManager discoveryManager; + + private Environment environment; + + private ReferenceConfigManager referenceConfigManager; + + public SCBEngine() { + eventBus = EventManager.getEventBus(); + + eventBus.register(this); + + INSTANCE = this; + + producerProviderManager = new ProducerProviderManager(this); + } + + 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; + } + + @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 BootStrapProperties.readApplication(environment); + } + + public void setStatus(SCBStatus status) { + this.status = status; + } + + public SCBStatus getStatus() { + return status; + } + + public OpenAPIRegistryManager getOpenAPIRegistryManager() { + return this.openAPIRegistryManager; + } + + public FilterChainsManager getFilterChainsManager() { + return filterChainsManager; + } + + public SCBEngine setFilterChainsManager(FilterChainsManager filterChainsManager) { + this.filterChainsManager = filterChainsManager; + return this; + } + + public PriorityPropertyManager getPriorityPropertyManager() { + return priorityPropertyManager; + } + + public SCBEngine setPriorityPropertyManager(PriorityPropertyManager priorityPropertyManager) { + this.priorityPropertyManager = priorityPropertyManager; + return this; + } + + public EventBus getEventBus() { + return eventBus; + } + + public ExecutorManager getExecutorManager() { + return executorManager; + } + + public ProducerProviderManager getProducerProviderManager() { + return producerProviderManager; + } + + public void setProducerProviderManager(ProducerProviderManager producerProviderManager) { + this.producerProviderManager = producerProviderManager; + } + + public ConsumerProviderManager getConsumerProviderManager() { + return consumerProviderManager; + } + + public TransportManager getTransportManager() { + return transportManager; + } + + public SwaggerEnvironment getSwaggerEnvironment() { + return swaggerEnvironment; + } + + public SCBEngine addProducerMeta(String schemaId, Object instance) { + getProducerProviderManager().addProducerMeta(schemaId, instance); + return this; + } + + protected void triggerEvent(EventType eventType) { + BootEvent event = new BootEvent(); + event.setScbEngine(this); + event.setEventType(eventType); + + for (BootListener listener : bootListeners) { + listener.onBootEvent(event); + } + } + + protected void safeTriggerEvent(EventType eventType) { + BootEvent event = new BootEvent(); + event.setScbEngine(this); + event.setEventType(eventType); + + for (BootListener listener : bootListeners) { + try { + listener.onBootEvent(event); + LOGGER.info("BootListener {} succeed to process {}.", listener.getClass().getName(), eventType); + } catch (Throwable e) { + LOGGER.error("BootListener {} failed to process {}.", listener.getClass().getName(), eventType, e); + } + } + } + + @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(); + printServiceInfo(); + } catch (Throwable e) { + LOGGER.error("Failed to start ServiceComb due to errors and close", e); + try { + destroy(); + } catch (Exception exception) { + LOGGER.info("destroy has some error.", exception); + } + status = SCBStatus.FAILED; + throw new IllegalStateException("ServiceComb init failed.", e); + } + } + + return this; + } + + private void printServiceInfo() { + StringBuilder serviceInfo = new StringBuilder(); + serviceInfo.append("Service information is shown below:\n"); + for (BootUpInformationCollector bootUpInformationCollector : bootUpInformationCollectors) { + String info = bootUpInformationCollector.collect(this); + if (StringUtils.isEmpty(info)) { + continue; + } + + serviceInfo.append(info); + if (!info.endsWith("\n")) { + serviceInfo.append('\n'); + } + } + LOGGER.info(serviceInfo.toString()); + } + + private void doRun() throws Exception { + status = SCBStatus.STARTING; + + triggerEvent(EventType.BEFORE_FILTER); + filterChainsManager.init(); + triggerEvent(EventType.AFTER_FILTER); + + createProducerMicroserviceMeta(); + + triggerEvent(EventType.BEFORE_PRODUCER_PROVIDER); + producerProviderManager.init(); + triggerEvent(EventType.AFTER_PRODUCER_PROVIDER); + + triggerEvent(EventType.BEFORE_CONSUMER_PROVIDER); + consumerProviderManager.init(); + triggerEvent(EventType.AFTER_CONSUMER_PROVIDER); + + triggerEvent(EventType.BEFORE_TRANSPORT); + transportManager.init(this); + triggerEvent(EventType.AFTER_TRANSPORT); + + triggerEvent(EventType.BEFORE_REGISTRY); + registrationManager.run(); + discoveryManager.run(); + status = SCBStatus.UP; + triggerEvent(EventType.AFTER_REGISTRY); + + // Keep this message for tests cases work. + LOGGER.warn("ServiceComb is ready."); + } + + private void createProducerMicroserviceMeta() { + 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)); + } + + /** + * not allow throw any exception + * even some step throw exception, must catch it and go on, otherwise shutdown process will be broken. + */ + public synchronized void destroy() { + if (SCBStatus.UP.equals(status) || SCBStatus.STARTING.equals(status)) { + LOGGER.info("ServiceComb is closing now..."); + doDestroy(); + status = SCBStatus.DOWN; + LOGGER.info("ServiceComb had closed"); + } + } + + private void doDestroy() { + //Step 0: turn down the status of this instance in service center, + // so that the consumers can remove this instance record in advance + turnDownInstanceStatus(); + blockShutDownOperationForConsumerRefresh(); + + //Step 1: notify all component stop invoke via BEFORE_CLOSE Event + safeTriggerEvent(EventType.BEFORE_CLOSE); + + //Step 2: forbid create new consumer invocation + status = SCBStatus.STOPPING; + + //Step 3: Unregister microservice instance from Service Center and close vertx + // Forbidden other consumers find me + registrationManager.destroy(); + discoveryManager.destroy(); + + //Step 4: wait all invocation finished + try { + validAllInvocationFinished(); + } catch (InterruptedException e) { + LOGGER.error("wait all invocation finished interrupted", e); + } + + //Step 5: destroy config source + // only be null for some test cases + if (priorityPropertyManager != null) { + priorityPropertyManager.close(); + } + + //Step 6: Stop vertx to prevent blocking exit + // delete the following one line when every refactor is done. + VertxUtils.blockCloseVertxByName("transport"); + + HttpClients.destroy(); + + //Step 7: notify all component do clean works via AFTER_CLOSE Event + safeTriggerEvent(EventType.AFTER_CLOSE); + } + + private void turnDownInstanceStatus() { + try { + registrationManager.updateMicroserviceInstanceStatus(MicroserviceInstanceStatus.DOWN); + } catch (Throwable e) { + LOGGER.warn("turn down instance status fail: {}", e.getMessage()); + } + } + + private void blockShutDownOperationForConsumerRefresh() { + try { + long turnDownWaitSeconds = environment.getProperty(CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC, + long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); + if (turnDownWaitSeconds <= 0) { + return; + } + Thread.sleep(TimeUnit.SECONDS.toMillis(turnDownWaitSeconds)); + } catch (InterruptedException e) { + LOGGER.warn("failed to block the shutdown procedure", e); + } + } + + private void validAllInvocationFinished() throws InterruptedException { + long start = System.currentTimeMillis(); + while (true) { + long remaining = invocationStartedCounter.get() - invocationFinishedCounter.get(); + if (remaining <= 0) { + return; + } + + if (System.currentTimeMillis() - start > TimeUnit.SECONDS.toMillis(30)) { + LOGGER.error("wait for all requests timeout, abandon waiting, remaining requests: {}.", remaining); + return; + } + TimeUnit.SECONDS.sleep(1); + } + } + + public void ensureStatusUp() { + SCBStatus currentStatus = getStatus(); + if (!SCBStatus.UP.equals(currentStatus)) { + String message = + "The request is rejected. Cannot process the request due to STATUS = " + currentStatus; + throw new InvocationException(Status.SERVICE_UNAVAILABLE, new CommonExceptionData(message)); + } + } + + public CompletableFuture getOrCreateReferenceConfigAsync( + String microserviceName) { + return referenceConfigManager.getOrCreateReferenceConfigAsync(this, microserviceName); + } + + public MicroserviceReferenceConfig getOrCreateReferenceConfig( + String microserviceName) { + ensureStatusUp(); + return referenceConfigManager.getOrCreateReferenceConfig(this, microserviceName); + } + + public MicroserviceMeta getProducerMicroserviceMeta() { + return producerMicroserviceMeta; + } + + public void setProducerMicroserviceMeta(MicroserviceMeta producerMicroserviceMeta) { + this.producerMicroserviceMeta = producerMicroserviceMeta; + } + + + public static class CreateMicroserviceMetaEvent { + private final MicroserviceMeta microserviceMeta; + + public CreateMicroserviceMetaEvent(MicroserviceMeta microserviceMeta) { + this.microserviceMeta = microserviceMeta; + } + + public MicroserviceMeta getMicroserviceMeta() { + return this.microserviceMeta; + } + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/SCBStatus.java b/core/src/main/java/org/apache/servicecomb/core/SCBStatus.java new file mode 100644 index 00000000000..62551eb1108 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/SCBStatus.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; + +//DOWN -> STARTING -> UP -> STOPPING -> DOWN +public enum SCBStatus { + //Chassis is Down + DOWN, + //Chassis is Starting (progressing) + STARTING, + //Chassis is Running + UP, + //Chassis is Stopping (progressing) + STOPPING, + //Chassis Init Failed + FAILED +} diff --git a/core/src/main/java/org/apache/servicecomb/core/ServiceCombCoreConfiguration.java b/core/src/main/java/org/apache/servicecomb/core/ServiceCombCoreConfiguration.java new file mode 100644 index 00000000000..8abaceef9b5 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/ServiceCombCoreConfiguration.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.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 SCBApplicationListener scbApplicationListener(SCBEngine scbEngine) { + SCBApplicationListener scbApplicationListener = new SCBApplicationListener(scbEngine); + scbApplicationListener.setInitEventClass(ApplicationReadyEvent.class); + return scbApplicationListener; + } + + @Bean + 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 new file mode 100644 index 00000000000..1c40303fce8 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/Transport.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.core; + +import org.springframework.core.env.Environment; + +public interface Transport { + String getName(); + + default int getOrder() { + return 0; + } + + default boolean canInit() { + return true; + } + + boolean init() throws Exception; + + void setEnvironment(Environment environment); + + /* + * endpoint的格式为 URI,比如rest://192.168.1.1:8080 + */ + Object parseAddress(String endpoint); + + /* + * 本transport的监听地址 + */ + Endpoint getEndpoint(); + + /* + * 用于上报到服务中心,要求是其他节点可访问的地址 + */ + Endpoint getPublishEndpoint() 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 new file mode 100644 index 00000000000..21040a3410f --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBBootstrap.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.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(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 new file mode 100644 index 00000000000..46ccd954155 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBEngineForTest.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.core.bootstrap; + +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.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(Environment environment) { + EmptyFilter emptyFilter = new EmptyFilter(); + emptyFilter.setEnvironment(environment); + setFilterChainsManager(new FilterChainsManager() + .setProviderFilters(List.of(emptyFilter)) + .setConsumerFilters(List.of(emptyFilter)) + .setEdgeFilters(List.of(emptyFilter))); + + PriorityPropertyFactory propertyFactory = new PriorityPropertyFactory(environment); + ConfigObjectFactory configObjectFactory = new ConfigObjectFactory(propertyFactory); + setPriorityPropertyManager(new PriorityPropertyManager(configObjectFactory)); + setEnvironment(environment); + } + + @Override + public synchronized void destroy() { + super.destroy(); + + ReflectUtils.setField(SCBEngine.class, null, "INSTANCE", null); + + EventManager.eventBus = new SimpleEventBus(); + } +} 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 new file mode 100644 index 00000000000..4ebae5180f4 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/bootup/BootUpInformationCollector.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.core.bootup; + +import org.apache.servicecomb.core.SCBEngine; +import org.springframework.core.Ordered; + +public interface BootUpInformationCollector extends Ordered { + default String collect(SCBEngine engine) { + return collect(); + } + + String collect(); +} 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/FilterChainCollector.java b/core/src/main/java/org/apache/servicecomb/core/bootup/FilterChainCollector.java new file mode 100644 index 00000000000..80289d22ce3 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/bootup/FilterChainCollector.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.bootup; + +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.filter.FilterChainsManager; + +public class FilterChainCollector implements BootUpInformationCollector { + @Override + public String collect(SCBEngine engine) { + FilterChainsManager mgr = engine.getFilterChainsManager(); + return mgr.collectResolvedChains(); + } + + @Override + public String collect() { + return null; + } + + @Override + public int getOrder() { + return 300; + } +} 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 new file mode 100644 index 00000000000..fb13033ec99 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/bootup/ServiceInformationCollector.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.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.info() + "\n" + discoveryManager.info(); + } + + @Override + public int getOrder() { + return 200; + } +} 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 new file mode 100644 index 00000000000..78f24e67f78 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/definition/ConsumerMicroserviceVersionsMeta.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.definition; + +import org.apache.servicecomb.core.SCBEngine; + +public class ConsumerMicroserviceVersionsMeta extends MicroserviceVersionsMeta { + 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 new file mode 100644 index 00000000000..9b164223af7 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/definition/CoreMetaUtils.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.definition; + +public final class CoreMetaUtils { + + public static final String SWAGGER_PRODUCER = "scb_swagger-producer"; + + private CoreMetaUtils() { + } +} 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 new file mode 100644 index 00000000000..356201b70d7 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/definition/InvocationRuntimeType.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.definition; + +import java.lang.reflect.Method; +import java.lang.reflect.Type; + +import jakarta.ws.rs.core.Response.Status; + +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.response.ResponsesMeta; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * An InvocationRuntimeType indicates the associated java type information of this invocation. + * + * For producer, java type information NOT be changed for each invocation. + * + * For Consumer, java type information depend on method signature, or not available when in edge or + * invoked by raw type way like RestTemplate or InvokerUtils. + */ +public class InvocationRuntimeType { + private Class associatedClass; + + private Method associatedMethod; + + private final ResponsesMeta responsesMeta; + + private ArgumentsMapper argumentsMapper; + + public InvocationRuntimeType(Class associatedClass, Method associatedMethod, ResponsesMeta responsesMeta, + ArgumentsMapper argumentsMapper) { + this.associatedClass = associatedClass; + this.associatedMethod = associatedMethod; + this.argumentsMapper = argumentsMapper; + this.responsesMeta = responsesMeta; + } + + public InvocationRuntimeType(ResponsesMeta responsesMeta) { + this.responsesMeta = responsesMeta; + } + + public Class getAssociatedClass() { + return this.associatedClass; + } + + public Method getAssociatedMethod() { + return this.associatedMethod; + } + + public ArgumentsMapper getArgumentsMapper() { + return this.argumentsMapper; + } + + public JavaType findResponseType(int statusCode) { + return responsesMeta.findResponseType(statusCode); + } + + public void setSuccessResponseType(JavaType javaType) { + if (javaType != null) { + // when javaType is null , using swagger type, do not override + responsesMeta.setResponseType(Status.OK.getStatusCode(), javaType); + } + } + + public void setSuccessResponseType(Type type) { + if (type != null) { + // when javaType is null , using swagger type, do not override + responsesMeta.setResponseType(Status.OK.getStatusCode(), TypeFactory.defaultInstance().constructType(type)); + } + } + + public void setAssociatedClass(Class associatedClass) { + this.associatedClass = associatedClass; + } + + public void setAssociatedMethod(Method associatedMethod) { + this.associatedMethod = associatedMethod; + } + + public void setArgumentsMapper(ArgumentsMapper argumentsMapper) { + this.argumentsMapper = argumentsMapper; + } + + public boolean isRawConsumer() { + return this.associatedClass == null; + } +} 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 new file mode 100644 index 00000000000..9476a34229b --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceMeta.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.definition; + +import java.util.Collection; +import java.util.HashMap; +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 io.swagger.v3.oas.models.OpenAPI; + +public class MicroserviceMeta { + private final SCBEngine scbEngine; + + private MicroserviceVersionsMeta microserviceVersionsMeta; + + private final String appId; + + private final String microserviceName; + + // key is schemaId, this is all schemas + private final Map schemaMetas = 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 consumerFilterChain = FilterNode.EMPTY; + + private FilterNode providerFilterChain = FilterNode.EMPTY; + + private FilterNode edgeFilterChain = FilterNode.EMPTY; + + private final VendorExtensions vendorExtensions = new VendorExtensions(); + + public MicroserviceMeta(SCBEngine scbEngine, String application, String serviceName, boolean consumer) { + this.scbEngine = scbEngine; + this.appId = application; + this.microserviceName = serviceName; + this.consumer = consumer; + } + + public MicroserviceVersionsMeta getMicroserviceVersionsMeta() { + return microserviceVersionsMeta; + } + + public void setMicroserviceVersionsMeta(MicroserviceVersionsMeta microserviceVersionsMeta) { + this.microserviceVersionsMeta = microserviceVersionsMeta; + } + + public SCBEngine getScbEngine() { + return scbEngine; + } + + public boolean isConsumer() { + return consumer; + } + + public String getMicroserviceName() { + return microserviceName; + } + + public String getAppId() { + return appId; + } + + public SchemaMeta registerSchemaMeta(String schemaId, OpenAPI swagger) { + SchemaMeta schemaMeta = new SchemaMeta(this, schemaId, swagger); + + if (schemaMetas.putIfAbsent(schemaMeta.getSchemaId(), schemaMeta) != null) { + throw new IllegalStateException(String.format( + "failed to register SchemaMeta caused by duplicated schemaId, appId=%s, microserviceName=%s, schemaId=%s.", + appId, microserviceName, schemaMeta.getSchemaId())); + } + + schemaMeta.getOperations().values() + .forEach(operationMeta -> operationMetas.put(operationMeta.getMicroserviceQualifiedName(), operationMeta)); + + return schemaMeta; + } + + public Map operationMetas() { + return operationMetas; + } + + public Collection getOperations() { + return operationMetas.values(); + } + + public SchemaMeta ensureFindSchemaMeta(String schemaId) { + SchemaMeta schemaMeta = schemaMetas.get(schemaId); + if (schemaMeta == null) { + throw new IllegalStateException(String.format( + "failed to find SchemaMeta by schemaId, appId=%s, microserviceName=%s, schemaId=%s.", + appId, microserviceName, schemaId)); + } + + return schemaMeta; + } + + public SchemaMeta findSchemaMeta(String schemaId) { + return schemaMetas.get(schemaId); + } + + public Map getSchemaMetas() { + return schemaMetas; + } + + public void putExtData(String key, Object data) { + vendorExtensions.put(key, data); + } + + public T getExtData(String key) { + return vendorExtensions.get(key); + } + + 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 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 new file mode 100644 index 00000000000..9e98bc2db51 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceVersionsMeta.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.core.definition; + +import java.util.Map; + +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; + +public class MicroserviceVersionsMeta { + protected final SCBEngine scbEngine; + + // key is operationMeta.getMicroserviceQualifiedName() + private final Map configs = new ConcurrentHashMapEx<>(); + + public MicroserviceVersionsMeta(SCBEngine scbEngine) { + this.scbEngine = scbEngine; + } + + public OperationConfig getOrCreateOperationConfig(OperationMeta operationMeta) { + return configs.computeIfAbsent(operationMeta.getMicroserviceQualifiedName(), + name -> createOperationConfig(operationMeta)); + } + + private OperationConfig createOperationConfig(OperationMeta operationMeta) { + boolean consumer = operationMeta.getMicroserviceMeta().isConsumer(); + return scbEngine.getPriorityPropertyManager().createConfigObject( + OperationConfig.class, + "op-any-priority", consumer ? + OperationConfig.CONSUMER_OP_ANY_PRIORITY : OperationConfig.PRODUCER_OP_ANY_PRIORITY, + "consumer-op-any_priority", OperationConfig.CONSUMER_OP_ANY_PRIORITY, + "producer-op-any_priority", OperationConfig.PRODUCER_OP_ANY_PRIORITY, + + "op-priority", consumer ? + OperationConfig.CONSUMER_OP_PRIORITY : OperationConfig.PRODUCER_OP_PRIORITY, + "consumer-op-priority", OperationConfig.CONSUMER_OP_PRIORITY, + "producer-op-priority", OperationConfig.PRODUCER_OP_PRIORITY, + + "consumer-producer", consumer ? "Consumer" : "Provider", + "consumer-provider", consumer ? "Consumer" : "Provider", + + "service", operationMeta.getMicroserviceName(), + "schema", operationMeta.getSchemaId(), + "operation", operationMeta.getOperationId()); + } +} 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 new file mode 100644 index 00000000000..bc289521a31 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/definition/OperationConfig.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.config.inject.InjectProperties; +import org.apache.servicecomb.config.inject.InjectProperty; +import org.apache.servicecomb.core.CoreConst; + +@InjectProperties(prefix = "servicecomb") +public class OperationConfig { + public static final List CONSUMER_OP_ANY_PRIORITY = Arrays.asList( + "${service}.${schema}.${operation}", + "${service}.${schema}", + "${service}"); + + public static final List PRODUCER_OP_ANY_PRIORITY = Arrays.asList( + "${schema}.${operation}", + "${schema}"); + + public static final List CONSUMER_OP_PRIORITY = Arrays.asList( + ".${service}.${schema}.${operation}", + ".${service}.${schema}", + ".${service}", + ""); + + public static final List PRODUCER_OP_PRIORITY = Arrays.asList( + ".${schema}.${operation}", + ".${schema}", + ""); + + @InjectProperty(keys = {"metrics.${consumer-producer}.invocation.slow.enabled${op-priority}", + "${consumer-producer}.invocation.slow.enabled${op-priority}"}, defaultValue = "false") + private boolean slowInvocationEnabled; + + @InjectProperty(keys = {"metrics.${consumer-producer}.invocation.slow.msTime${op-priority}", + "${consumer-producer}.invocation.slow.msTime${op-priority}"}, defaultValue = "1000") + private long msSlowInvocation; + + private long nanoSlowInvocation; + + /** + * consumer request timeout + */ + @InjectProperty(keys = {"request.${op-any-priority}.timeout", "request.timeout"}, defaultValue = "30000") + private long msRequestTimeout; + + /** + * Invocation timeout. + */ + @InjectProperty(keys = {"invocation.${op-any-priority}.timeout", "invocation.timeout"}, defaultValue = "-1") + private long msInvocationTimeout; + + private long nanoInvocationTimeout; + + /** + * whether to remove certain headers from the 3rd party invocations + */ + @InjectProperty(keys = {"request.clientRequestHeaderFilterEnabled${consumer-op-priority}"}, defaultValue = "false") + private boolean clientRequestHeaderFilterEnabled = false; + + /** + * producer wait in thread pool timeout + */ + private final Map nanoRequestWaitInPoolTimeoutByTransport = new HashMap<>(); + + @InjectProperty(keys = "Provider.requestWaitInPoolTimeout${op-priority}", defaultValue = "30000") + private long msDefaultRequestWaitInPoolTimeout; + + private long nanoDefaultRequestWaitInPoolTimeout; + + @InjectProperty(keys = { + "Provider.requestWaitInPoolTimeout${op-priority}", + "highway.server.requestWaitInPoolTimeout"}, defaultValue = "30000") + private long msHighwayRequestWaitInPoolTimeout; + + private long nanoHighwayRequestWaitInPoolTimeout; + + @InjectProperty(keys = { + "Provider.requestWaitInPoolTimeout${op-priority}", + "rest.server.requestWaitInPoolTimeout"}, defaultValue = "30000") + private long msRestRequestWaitInPoolTimeout; + + private long nanoRestRequestWaitInPoolTimeout; + + @InjectProperty(keys = { + "operation${op-priority}.transport", // Deprecated + "references.transport${op-priority}" + }) + private String transport; + + @InjectProperty(keys = {"governance.${op-any-priority}.matchType", "governance.matchType"}, defaultValue = "rest") + private String governanceMatchType; + + public boolean isSlowInvocationEnabled() { + return slowInvocationEnabled; + } + + public void setSlowInvocationEnabled(boolean slowInvocationEnabled) { + this.slowInvocationEnabled = slowInvocationEnabled; + } + + public long getMsSlowInvocation() { + return msSlowInvocation; + } + + public void setMsSlowInvocation(long msSlowInvocation) { + this.msSlowInvocation = msSlowInvocation; + this.nanoSlowInvocation = TimeUnit.MILLISECONDS.toNanos(msSlowInvocation); + } + + public long getNanoSlowInvocation() { + return nanoSlowInvocation; + } + + public long getMsRequestTimeout() { + return msRequestTimeout; + } + + public void setMsRequestTimeout(long msRequestTimeout) { + this.msRequestTimeout = msRequestTimeout; + } + + public long getNanoRequestWaitInPoolTimeout(String transport) { + return nanoRequestWaitInPoolTimeoutByTransport.getOrDefault(transport, nanoDefaultRequestWaitInPoolTimeout); + } + + public void registerRequestWaitInPoolTimeout(String transport, long msTimeout) { + nanoRequestWaitInPoolTimeoutByTransport.put(transport, TimeUnit.MILLISECONDS.toNanos(msTimeout)); + } + + public long getMsDefaultRequestWaitInPoolTimeout() { + return msDefaultRequestWaitInPoolTimeout; + } + + public void setMsDefaultRequestWaitInPoolTimeout(long msDefaultRequestWaitInPoolTimeout) { + this.msDefaultRequestWaitInPoolTimeout = msDefaultRequestWaitInPoolTimeout; + this.nanoDefaultRequestWaitInPoolTimeout = TimeUnit.MILLISECONDS.toNanos(msDefaultRequestWaitInPoolTimeout); + } + + public long getNanoDefaultRequestWaitInPoolTimeout() { + return nanoDefaultRequestWaitInPoolTimeout; + } + + public long getMsHighwayRequestWaitInPoolTimeout() { + return msHighwayRequestWaitInPoolTimeout; + } + + public void setMsHighwayRequestWaitInPoolTimeout(long msHighwayRequestWaitInPoolTimeout) { + this.msHighwayRequestWaitInPoolTimeout = msHighwayRequestWaitInPoolTimeout; + this.nanoHighwayRequestWaitInPoolTimeout = TimeUnit.MILLISECONDS.toNanos(msHighwayRequestWaitInPoolTimeout); + registerRequestWaitInPoolTimeout(CoreConst.HIGHWAY, msHighwayRequestWaitInPoolTimeout); + } + + public long getNanoHighwayRequestWaitInPoolTimeout() { + return nanoHighwayRequestWaitInPoolTimeout; + } + + public long getMsRestRequestWaitInPoolTimeout() { + return msRestRequestWaitInPoolTimeout; + } + + public void setMsRestRequestWaitInPoolTimeout(long msRestRequestWaitInPoolTimeout) { + this.msRestRequestWaitInPoolTimeout = msRestRequestWaitInPoolTimeout; + this.nanoRestRequestWaitInPoolTimeout = TimeUnit.MILLISECONDS.toNanos(msRestRequestWaitInPoolTimeout); + registerRequestWaitInPoolTimeout(CoreConst.RESTFUL, msRestRequestWaitInPoolTimeout); + } + + public long getNanoRestRequestWaitInPoolTimeout() { + return nanoRestRequestWaitInPoolTimeout; + } + + public long getMsInvocationTimeout() { + return msInvocationTimeout; + } + + public void setMsInvocationTimeout(long msInvocationTimeout) { + this.msInvocationTimeout = msInvocationTimeout; + this.nanoInvocationTimeout = TimeUnit.MILLISECONDS.toNanos(msInvocationTimeout); + } + + public String getGovernanceMatchType() { + return governanceMatchType; + } + + public void setGovernanceMatchType(String governanceMatchType) { + this.governanceMatchType = governanceMatchType; + } + + public long getNanoInvocationTimeout() { + return this.nanoInvocationTimeout; + } + + public boolean isClientRequestHeaderFilterEnabled() { + return clientRequestHeaderFilterEnabled; + } + + public void setClientRequestHeaderFilterEnabled(boolean clientRequestHeaderFilterEnabled) { + this.clientRequestHeaderFilterEnabled = clientRequestHeaderFilterEnabled; + } + + public String getTransport() { + return transport; + } + + public void setTransport(String transport) { + if (transport == null) { + 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 new file mode 100644 index 00000000000..003daaffab1 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/definition/OperationMeta.java @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.concurrent.Executor; + +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.v3.oas.models.Operation; + + +public class OperationMeta { + private SchemaMeta schemaMeta; + + // schemaId.operation + private String schemaQualifiedName; + + // microserviceName.schemaId.operation + private String microserviceQualifiedName; + + private String httpMethod; + + private String operationPath; + + private Operation swaggerOperation; + + // run in this executor + private Executor executor; + + private final ResponsesMeta responsesMeta = new ResponsesMeta(); + + private final VendorExtensions vendorExtensions = new VendorExtensions(); + + public OperationMeta init(SchemaMeta schemaMeta, SwaggerOperation swaggerOperation) { + this.schemaMeta = schemaMeta; + this.schemaQualifiedName = schemaMeta.getSchemaId() + "." + swaggerOperation.getOperationId(); + this.microserviceQualifiedName = + schemaMeta.getMicroserviceQualifiedName() + "." + swaggerOperation.getOperationId(); + this.httpMethod = swaggerOperation.getHttpMethod().name(); + this.operationPath = swaggerOperation.getPath(); + this.swaggerOperation = swaggerOperation.getOperation(); + this.executor = schemaMeta.getMicroserviceMeta().getScbEngine().getExecutorManager().findExecutor(this); + this.responsesMeta.init(schemaMeta.getSwagger(), swaggerOperation.getOperation()); + + return this; + } + + public void setSwaggerProducerOperation(SwaggerProducerOperation swaggerProducerOperation) { + this.putExtData(CoreConst.PRODUCER_OPERATION, swaggerProducerOperation); + } + + public SwaggerProducerOperation getSwaggerProducerOperation() { + return (SwaggerProducerOperation) this.getExtData(CoreConst.PRODUCER_OPERATION); + } + + public OperationConfig getConfig() { + return schemaMeta.getMicroserviceMeta().getMicroserviceVersionsMeta().getOrCreateOperationConfig(this); + } + + public String getHttpMethod() { + return httpMethod; + } + + public String getOperationPath() { + return operationPath; + } + + public Operation getSwaggerOperation() { + return swaggerOperation; + } + + public ResponsesMeta getResponsesMeta() { + // TODO : this method now called by highway, and highway always use swagger type + // in the future improvement , highway can use runtime type and this method can be removed + 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); + return result; + } + + public MicroserviceMeta getMicroserviceMeta() { + return schemaMeta.getMicroserviceMeta(); + } + + public SchemaMeta getSchemaMeta() { + return schemaMeta; + } + + public String getSchemaQualifiedName() { + return schemaQualifiedName; + } + + public String getMicroserviceQualifiedName() { + return microserviceQualifiedName; + } + + public String getMicroserviceName() { + return schemaMeta.getMicroserviceName(); + } + + public String getSchemaId() { + return schemaMeta.getSchemaId(); + } + + public String getOperationId() { + return swaggerOperation.getOperationId(); + } + + // invoker make sure idx is valid + public String getParamName(int idx) { + return swaggerOperation.getParameters().get(idx).getName(); + } + + public void putExtData(String key, Object data) { + vendorExtensions.put(key, data); + } + + public T getExtData(String key) { + return vendorExtensions.get(key); + } + + public VendorExtensions getVendorExtensions() { + return vendorExtensions; + } + + public Executor getExecutor() { + return executor; + } + + public void setExecutor(Executor executor) { + this.executor = executor; + } + + public InvocationRuntimeType buildBaseProviderRuntimeType() { + SwaggerProducerOperation swaggerProducerOperation = this.getSwaggerProducerOperation(); + return new InvocationRuntimeType(swaggerProducerOperation.getProducerClass(), + swaggerProducerOperation.getProducerMethod(), this.cloneResponseMeta(), + swaggerProducerOperation.getArgumentsMapper()); + } + + public InvocationRuntimeType buildBaseConsumerRuntimeType() { + return new InvocationRuntimeType(this.cloneResponseMeta()); + } +} 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 new file mode 100644 index 00000000000..623ec695b0c --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/definition/SchemaMeta.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.VendorExtensions; +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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 OpenAPI swagger; + + private final String schemaId; + + // microserviceName.schemaId + private final String microserviceQualifiedName; + + private final Map operations = new HashMap<>(); + + private final VendorExtensions vendorExtensions = new VendorExtensions(); + + public SchemaMeta(MicroserviceMeta microserviceMeta, String schemaId, OpenAPI swagger) { + this.microserviceMeta = microserviceMeta; + this.schemaId = schemaId; + this.swagger = swagger; + this.microserviceQualifiedName = microserviceMeta.getMicroserviceName() + "." + schemaId; + + try { + initOperationMetas(); + } catch (Throwable e) { + LOGGER.error("Unhandled exception to {}.", microserviceQualifiedName, e); + throw e; + } + } + + private SchemaMeta initOperationMetas() { + SwaggerOperations swaggerOperations = new SwaggerOperations(swagger); + for (SwaggerOperation swaggerOperation : swaggerOperations.getOperations().values()) { + operations.put(swaggerOperation.getOperationId(), new OperationMeta().init(this, swaggerOperation)); + } + return this; + } + + public MicroserviceMeta getMicroserviceMeta() { + return microserviceMeta; + } + + public OpenAPI getSwagger() { + return swagger; + } + + public String getAppId() { + return microserviceMeta.getAppId(); + } + + public String getMicroserviceName() { + return microserviceMeta.getMicroserviceName(); + } + + public String getSchemaId() { + return schemaId; + } + + public String getMicroserviceQualifiedName() { + return microserviceQualifiedName; + } + + public Map getOperations() { + return operations; + } + + public void putExtData(String key, Object data) { + vendorExtensions.put(key, data); + } + + public T getExtData(String key) { + return vendorExtensions.get(key); + } + + public OperationMeta findOperation(String operationId) { + return operations.get(operationId); + } + + public OperationMeta ensureFindOperation(String operationId) { + OperationMeta value = operations.get(operationId); + if (value == null) { + throw new IllegalStateException(String + .format("Can not find OperationMeta, microserviceName=%s, schemaId=%s, operationId=%s.", + getMicroserviceName(), getSchemaId(), operationId)); + } + + return value; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationBaseEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationBaseEvent.java new file mode 100644 index 00000000000..09e2949b319 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/event/InvocationBaseEvent.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.core.event; + +import org.apache.servicecomb.core.Invocation; + +public class InvocationBaseEvent { + private final Invocation invocation; + + public InvocationBaseEvent(Invocation invocation) { + this.invocation = invocation; + } + + public Invocation getInvocation() { + return invocation; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationBusinessFinishEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationBusinessFinishEvent.java new file mode 100644 index 00000000000..2c3fe135836 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/event/InvocationBusinessFinishEvent.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.core.event; + +import org.apache.servicecomb.core.Invocation; + +public class InvocationBusinessFinishEvent extends InvocationBaseEvent { + public InvocationBusinessFinishEvent(Invocation invocation) { + super(invocation); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationBusinessMethodFinishEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationBusinessMethodFinishEvent.java new file mode 100644 index 00000000000..c4419afb84d --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/event/InvocationBusinessMethodFinishEvent.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.event; + +import org.apache.servicecomb.core.Invocation; + +public class InvocationBusinessMethodFinishEvent extends InvocationBaseEvent { + public InvocationBusinessMethodFinishEvent(Invocation invocation) { + super(invocation); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationBusinessMethodStartEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationBusinessMethodStartEvent.java new file mode 100644 index 00000000000..0594ae98afe --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/event/InvocationBusinessMethodStartEvent.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.event; + +import org.apache.servicecomb.core.Invocation; + +public class InvocationBusinessMethodStartEvent extends InvocationBaseEvent { + public InvocationBusinessMethodStartEvent(Invocation invocation) { + super(invocation); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationEncodeResponseStartEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationEncodeResponseStartEvent.java new file mode 100644 index 00000000000..53ffc12e0e8 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/event/InvocationEncodeResponseStartEvent.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.core.event; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.swagger.invocation.Response; + +public class InvocationEncodeResponseStartEvent extends InvocationWithResponseEvent { + public InvocationEncodeResponseStartEvent(Invocation invocation, Response response) { + super(invocation, response); + } +} 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 new file mode 100644 index 00000000000..21a20226d01 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/event/InvocationFinishEvent.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.core.event; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.swagger.invocation.Response; + +public class InvocationFinishEvent extends InvocationWithResponseEvent { + public InvocationFinishEvent(Invocation invocation, Response response) { + super(invocation, response); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java new file mode 100644 index 00000000000..2caf8c373d4 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.event; + +import org.apache.servicecomb.core.Invocation; + +public class InvocationStartEvent extends InvocationBaseEvent { + public InvocationStartEvent(Invocation invocation) { + super(invocation); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartSendRequestEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartSendRequestEvent.java new file mode 100644 index 00000000000..9a8770ea8ec --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartSendRequestEvent.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.core.event; + +import org.apache.servicecomb.core.Invocation; + +public class InvocationStartSendRequestEvent extends InvocationBaseEvent { + public InvocationStartSendRequestEvent(Invocation invocation) { + super(invocation); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationTimeoutCheckEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationTimeoutCheckEvent.java new file mode 100644 index 00000000000..3dc563161e8 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/event/InvocationTimeoutCheckEvent.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.core.event; + +import org.apache.servicecomb.core.Invocation; + +public class InvocationTimeoutCheckEvent extends InvocationBaseEvent { + public InvocationTimeoutCheckEvent(Invocation invocation) { + super(invocation); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationWithResponseEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationWithResponseEvent.java new file mode 100644 index 00000000000..3ecbc7d8d00 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/event/InvocationWithResponseEvent.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.core.event; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.swagger.invocation.Response; + +public class InvocationWithResponseEvent extends InvocationBaseEvent { + protected Response response; + + public InvocationWithResponseEvent(Invocation invocation, Response response) { + super(invocation); + this.response = response; + } + + public Response getResponse() { + return response; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/event/ServerAccessLogEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/ServerAccessLogEvent.java new file mode 100644 index 00000000000..63e14bf0f21 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/event/ServerAccessLogEvent.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.core.event; + +import io.vertx.ext.web.RoutingContext; + +public class ServerAccessLogEvent { + private long milliStartTime; + + private long milliEndTime; + + private RoutingContext routingContext; + + /** + * If client send request via a short-lived connection, the connection may be closed before the corresponding + * access log is generated, and then we can not get valid host ip + * may get "0.0.0.0" as result. So we need to get local address before the connection is closed. + */ + private String localAddress; + + public ServerAccessLogEvent() { + } + + public long getMilliStartTime() { + return milliStartTime; + } + + public ServerAccessLogEvent setMilliStartTime(long milliStartTime) { + this.milliStartTime = milliStartTime; + return this; + } + + public long getMilliEndTime() { + return milliEndTime; + } + + public ServerAccessLogEvent setMilliEndTime(long milliEndTime) { + this.milliEndTime = milliEndTime; + return this; + } + + public RoutingContext getRoutingContext() { + return routingContext; + } + + public ServerAccessLogEvent setRoutingContext(RoutingContext routingContext) { + this.routingContext = routingContext; + return this; + } + + public String getLocalAddress() { + return localAddress; + } + + public ServerAccessLogEvent setLocalAddress(String localAddress) { + this.localAddress = localAddress; + return this; + } +} 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 new file mode 100644 index 00000000000..856888764a1 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/CoreExceptionConfiguration.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.core.exception; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class CoreExceptionConfiguration { + @Bean + public Exceptions scbExceptions() { + return new Exceptions(); + } + + @Bean + public DefaultExceptionProcessor scbDefaultExceptionProcessor() { + return new DefaultExceptionProcessor(); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/CseException.java b/core/src/main/java/org/apache/servicecomb/core/exception/CseException.java new file mode 100644 index 00000000000..3666e7b305e --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/CseException.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.exception; + +public class CseException extends RuntimeException { + + private static final long serialVersionUID = 8027482777502649656L; + + private final String code; + + public CseException(String code, String message) { + super(message); + this.code = code; + } + + public CseException(String code, String message, Throwable cause) { + super(message, cause); + this.code = code; + } + + public String getCode() { + return code; + } + + @Override + public String toString() { + return "ServiceDefinitionException Code:" + code + ", Message:" + getMessage(); + } +} 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 new file mode 100644 index 00000000000..e8817af521d --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java @@ -0,0 +1,215 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +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; +import org.apache.servicecomb.swagger.invocation.Response; +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 org.springframework.beans.factory.annotation.Autowired; + +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); + + public static final int ORDER = Integer.MAX_VALUE; + + @JsonIgnore + @SuppressWarnings("unchecked") + private final List> converters = SPIServiceUtils + .getOrLoadSortedService(ExceptionConverter.class).stream() + .map(converter -> (ExceptionConverter) converter) + .collect(Collectors.toList()); + + @InjectProperty(keys = "print-stack-trace", defaultValue = "true") + protected boolean printStackTrace; + + @InjectProperty(keys = "print-rate-limit", defaultValue = "false") + protected boolean printRateLimit; + + private final Map, ExceptionConverter> converterCache = new ConcurrentHashMapEx<>(); + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public boolean isPrintStackTrace() { + return printStackTrace; + } + + public DefaultExceptionProcessor setPrintStackTrace(boolean printStackTrace) { + this.printStackTrace = printStackTrace; + return this; + } + + public boolean isPrintRateLimit() { + return printRateLimit; + } + + public DefaultExceptionProcessor setPrintRateLimit(boolean printRateLimit) { + this.printRateLimit = printRateLimit; + return this; + } + + @SuppressWarnings("unchecked") + @Autowired(required = false) + public DefaultExceptionProcessor setConverters(List> converters) { + converters.forEach(converter -> this.converters.add((ExceptionConverter) converter)); + this.converters.sort(Comparator.comparingInt(ExceptionConverter::getOrder)); + this.converterCache.clear(); + return this; + } + + @Override + public InvocationException convert(Invocation invocation, Throwable throwable) { + StatusType genericStatus = CONSUMER.equals(invocation.getInvocationType()) ? BAD_REQUEST : INTERNAL_SERVER_ERROR; + return convert(invocation, throwable, genericStatus); + } + + @Override + public InvocationException convert(Invocation invocation, Throwable throwable, StatusType genericStatus) { + Throwable unwrapped = ExceptionFactory.unwrap(throwable); + try { + 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 :{}" + + "converter exception:{}", + getStackTrace(throwable), + getStackTrace(e)); + return new InvocationException(INTERNAL_SERVER_ERROR, + new CommonExceptionData(GENERIC_SERVER, INTERNAL_SERVER_ERROR.getReasonPhrase())); + } + } + + private ExceptionConverter findConverter(Throwable throwable) { + for (ExceptionConverter converter : converters) { + if (converter.canConvert(throwable)) { + return converter; + } + } + + throw new IllegalStateException("never happened: can not find converter for " + throwable.getClass().getName()); + } + + @Override + public Response toConsumerResponse(Invocation invocation, Throwable throwable) { + InvocationException exception = convert(invocation, throwable, BAD_REQUEST); + logConsumerException(invocation, exception); + return Response.failResp(exception); + } + + @Override + public void logConsumerException(Invocation invocation, InvocationException exception) { + if (isIgnoreLog(invocation, exception)) { + return; + } + + if (isPrintStackTrace()) { + invocation.getTraceIdLogger().error("{} Failed to invoke {}, endpoint={}.", + TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), + invocation.getMicroserviceQualifiedName(), + invocation.getEndpoint(), + exception); + return; + } + + invocation.getTraceIdLogger().error("{} Failed to invoke {}, endpoint={}, message={}.", + TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), + invocation.getMicroserviceQualifiedName(), + invocation.getEndpoint(), + ExceptionUtils.getExceptionMessageWithoutTrace(exception)); + } + + @Override + public boolean isIgnoreLog(Invocation invocation, InvocationException exception) { + if (!isPrintRateLimit() && exception.getStatusCode() == TOO_MANY_REQUESTS.getStatusCode()) { + return true; + } + + return false; + } + + @Override + public Response toProducerResponse(Invocation invocation, Throwable exception) { + InvocationException invocationException = convert(invocation, exception, INTERNAL_SERVER_ERROR); + if (invocation != null) { + logProducerException(invocation, invocationException); + } + return Response.createFail(invocationException); + } + + @Override + public void logProducerException(Invocation invocation, InvocationException exception) { + if (isIgnoreLog(invocation, exception)) { + return; + } + + if (isPrintStackTrace()) { + invocation.getTraceIdLogger().error("{} Failed to process {} invocation, operation={}.", + TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), + invocation.getInvocationType(), invocation.getMicroserviceQualifiedName(), exception); + return; + } + + 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/ExceptionCodes.java b/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionCodes.java new file mode 100644 index 00000000000..4c2c50885cb --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionCodes.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.core.exception; + +public interface ExceptionCodes { + String GENERIC_CLIENT = "SCB.00000000"; + String LB_ADDRESS_NOT_FOUND = "SCB.00000001"; + String NOT_DEFINED_ANY_SCHEMA = "SCB.00000002"; + String DEFAULT_VALIDATE = "SCB.00000003"; + String INVOCATION_TIMEOUT = "SCB.00000004"; + + String GENERIC_SERVER = "SCB.50000000"; +} 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 new file mode 100644 index 00000000000..b6424564747 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionConverter.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.core.exception; + +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 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()); + } + + static String getGenericCode(StatusType status) { + return isClient(status) ? GENERIC_CLIENT : GENERIC_SERVER; + } + + /** + * + * @param throwable exception will be converted + * @return can convert the exception + */ + boolean canConvert(Throwable throwable); + + /** + * + * @param invocation related invocation, will be null only when failed to prepare invocation + * @param throwable exception will be converted + * @param genericStatus if can not determine the status type, then use this input value + * @return converted invocation exception + */ + 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 new file mode 100644 index 00000000000..ffb3619bb41 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionProcessor.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.core.exception; + +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 + */ +public interface ExceptionProcessor extends Ordered { + @Override + default int getOrder() { + return 0; + } + + boolean isPrintStackTrace(); + + InvocationException convert(Invocation invocation, Throwable throwable); + + InvocationException convert(Invocation invocation, Throwable throwable, StatusType genericStatus); + + boolean isIgnoreLog(Invocation invocation, InvocationException exception); + + Response toConsumerResponse(Invocation invocation, Throwable throwable); + + void logConsumerException(Invocation invocation, InvocationException exception); + + Response toProducerResponse(Invocation invocation, Throwable exception); + + void logProducerException(Invocation invocation, InvocationException exception); +} diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionUtils.java b/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionUtils.java new file mode 100644 index 00000000000..016c8719e94 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/ExceptionUtils.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.exception; + +import org.apache.servicecomb.foundation.common.RegisterManager; +import org.apache.servicecomb.foundation.common.utils.FortifyUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ExceptionUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionUtils.class); + + // 异常码 + private static final String ERROR_DESC_MGR_MSG = "error desc mgr"; + + protected static final RegisterManager ERROR_DESC_MGR = new RegisterManager<>(ERROR_DESC_MGR_MSG); + + private static final String SERVICECOMB_HANDLER_REF_NOT_EXIST = "servicecomb.handler.ref.not.exist"; + + private static final String SERVICECOMB_PRODUCER_OPERATION_NOT_EXIST = "servicecomb.producer.operation.not.exist"; + + private static final String SERVICECOMB_LB_NO_AVAILABLE_ADDRESS = "servicecomb.lb.no.available.address"; + + static { + ERROR_DESC_MGR.register(SERVICECOMB_HANDLER_REF_NOT_EXIST, "Handler not exist, id=%s"); + ERROR_DESC_MGR.register(SERVICECOMB_PRODUCER_OPERATION_NOT_EXIST, + "Producer operation not exist, schemaId=%s, operationName=%s"); + ERROR_DESC_MGR.register(SERVICECOMB_LB_NO_AVAILABLE_ADDRESS, + "No available address found. microserviceName=%s, version=%s, discoveryGroupName=%s"); + } + + protected ExceptionUtils() { + } + + // TODO:应该改为protected,不允许随便调,所有异常,都必须是强类型的 + public static CseException createCseException(String code, Object... args) { + String msg = String.format(ERROR_DESC_MGR.ensureFindValue(code), args); + + CseException exception = new CseException(code, msg); + LOGGER.error(FortifyUtils.getErrorInfo(exception)); + return exception; + } + + public static CseException createCseException(String code, Throwable cause, Object... args) { + String msg = String.format(ERROR_DESC_MGR.ensureFindValue(code), args); + + CseException exception = new CseException(code, msg, cause); + LOGGER.error(FortifyUtils.getErrorInfo(exception)); + return exception; + } + + public static CseException producerOperationNotExist(String schemaId, String operationName) { + return createCseException(SERVICECOMB_PRODUCER_OPERATION_NOT_EXIST, + schemaId, + operationName); + } + + public static CseException handlerRefNotExist(String id) { + return createCseException(SERVICECOMB_HANDLER_REF_NOT_EXIST, id); + } + + public static CseException lbAddressNotFound(String microserviceName, String microserviceVersionRule, + String discoveryGroupName) { + return createCseException(SERVICECOMB_LB_NO_AVAILABLE_ADDRESS, + microserviceName, + microserviceVersionRule, + discoveryGroupName); + } +} 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 new file mode 100644 index 00000000000..0fb8ad418a1 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/Exceptions.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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; + +import java.util.Comparator; +import java.util.List; + +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(); + + @Autowired(required = false) + public void setProcessor(List processors) { + // never be null, "orElse" just to avoid compile warning + processor = processors.stream() + .min(Comparator.comparingInt(ExceptionProcessor::getOrder)) + .orElse(new DefaultExceptionProcessor()); + } + + public static Throwable unwrapIncludeInvocationException(Throwable throwable) { + return ExceptionFactory.unwrapIncludeInvocationException(throwable); + } + + public static T unwrap(Throwable throwable) { + return ExceptionFactory.unwrap(throwable); + } + + public static InvocationException create(StatusType status, Object errorData) { + return new InvocationException(status, errorData); + } + + public static InvocationException create(StatusType status, String code, String msg) { + return new InvocationException(status, code, msg); + } + + private static InvocationException create(StatusType status, String code, String msg, Throwable cause) { + return new InvocationException(status, code, msg, cause); + } + + public static InvocationException consumer(String code, String msg) { + return create(BAD_REQUEST, code, msg); + } + + public static InvocationException consumer(String code, String msg, Throwable cause) { + if (cause instanceof InvocationException) { + return (InvocationException) cause; + } + return create(BAD_REQUEST, code, msg, cause); + } + + public static InvocationException genericConsumer(String msg) { + return consumer(GENERIC_CLIENT, msg); + } + + public static InvocationException genericConsumer(String msg, Throwable cause) { + return consumer(GENERIC_CLIENT, msg, cause); + } + + public static InvocationException producer(String code, String msg) { + return create(INTERNAL_SERVER_ERROR, code, msg); + } + + public static InvocationException producer(String code, String msg, Throwable cause) { + return create(INTERNAL_SERVER_ERROR, code, msg, cause); + } + + public static InvocationException genericProducer(String msg) { + return producer(GENERIC_SERVER, msg); + } + + public static InvocationException genericProducer(String msg, Throwable cause) { + return producer(GENERIC_SERVER, msg, cause); + } + + public static StatusType getGenericStatus(Invocation invocation) { + return CONSUMER.equals(invocation.getInvocationType()) ? BAD_REQUEST : INTERNAL_SERVER_ERROR; + } + + public static Response toConsumerResponse(Invocation invocation, Throwable throwable) { + return processor.toConsumerResponse(invocation, throwable); + } + + public static Response toProducerResponse(Invocation invocation, Throwable exception) { + return processor.toProducerResponse(invocation, exception); + } + + public static InvocationException convert(Invocation invocation, Throwable throwable) { + return processor.convert(invocation, throwable); + } + + public static InvocationException convert(Invocation invocation, Throwable throwable, + StatusType genericStatus) { + return processor.convert(invocation, throwable, genericStatus); + } + + public static boolean isPrintInvocationStackTrace() { + return processor.isPrintStackTrace(); + } +} 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 new file mode 100644 index 00000000000..72507d0a3ba --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/ConnectTimeoutExceptionConverter.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.core.exception.converter; + +import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; + +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 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); + + public static final int ORDER = Short.MAX_VALUE; + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public boolean canConvert(Throwable throwable) { + return throwable instanceof ConnectTimeoutException; + } + + @Override + public InvocationException convert(Invocation invocation, ConnectTimeoutException throwable, + StatusType genericStatus) { + // throwable.getMessage: + // connection timed out: /1.1.1.1:8080 + // should not copy the message to invocationException to avoid leak server ip address + LOGGER.info("connection timed out, Details: {}.", throwable.getMessage()); + + return new InvocationException(INTERNAL_SERVER_ERROR, + ExceptionConverter.getGenericCode(genericStatus), "connection timed out.", + throwable); + } +} 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 new file mode 100644 index 00000000000..2898d4914ae --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/ConstraintViolationExceptionConverter.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.converter; + +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 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 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"; + + public ConstraintViolationExceptionConverter() { + } + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public boolean canConvert(Throwable throwable) { + return throwable instanceof ConstraintViolationException; + } + + @Override + public InvocationException convert(Invocation invocation, ConstraintViolationException throwable, + StatusType genericStatus) { + List details = throwable.getConstraintViolations().stream() + .map(violation -> new ValidateDetail(violation.getPropertyPath().toString(), violation.getMessage())) + .collect(Collectors.toList()); + + 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 new file mode 100644 index 00000000000..d0338d43628 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/DefaultExceptionConverter.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.core.exception.converter; + +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; + +/** + *
+ *   Only very few exceptions carry sensitive data
+ *   If discard all exception messages for these very few exceptions, it will cause difficult to locate problems
+ *
+ *   so default to remain the messages, and log the exception
+ *
+ *   if want to change these:
+ *   1. customize a new converter
+ *   2. disabled log for this class by log configuration
+ * 
+ */ +public class DefaultExceptionConverter implements ExceptionConverter { + public static final int ORDER = Integer.MAX_VALUE; + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public boolean canConvert(Throwable throwable) { + return true; + } + + @Override + 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 new file mode 100644 index 00000000000..f80f1a82519 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/IllegalArgumentExceptionConverter.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.exception.converter; + +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; + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public boolean canConvert(Throwable throwable) { + return throwable instanceof IllegalArgumentException; + } + + @Override + 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 new file mode 100644 index 00000000000..830aec3190c --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/InvocationExceptionConverter.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.exception.converter; + +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; + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public boolean canConvert(Throwable throwable) { + return throwable instanceof InvocationException; + } + + @Override + 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 new file mode 100644 index 00000000000..9a69f4c988c --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/ServiceCombExceptionConverter.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.core.exception.converter; + +import java.util.concurrent.TimeoutException; + +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; + + private TimeoutExceptionConverter timeoutExceptionConverter = new TimeoutExceptionConverter(); + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public boolean canConvert(Throwable throwable) { + return throwable instanceof ServiceCombException; + } + + @Override + public InvocationException convert(Invocation invocation, ServiceCombException throwable, + StatusType genericStatus) { + if (throwable.getCause() instanceof TimeoutException) { + return timeoutExceptionConverter.convert(invocation, (TimeoutException) throwable.getCause(), genericStatus); + } + + return new InvocationException(Status.INTERNAL_SERVER_ERROR, + ExceptionConverter.getGenericCode(genericStatus), throwable.getMessage(), + throwable); + } +} 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 new file mode 100644 index 00000000000..31d48a82933 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/TimeoutExceptionConverter.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.core.exception.converter; + +import static jakarta.ws.rs.core.Response.Status.REQUEST_TIMEOUT; + +import java.util.concurrent.TimeoutException; + +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); + + public static final int ORDER = Short.MAX_VALUE; + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public boolean canConvert(Throwable throwable) { + return throwable instanceof TimeoutException; + } + + @Override + 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 + // should not copy the message to invocationException to avoid leak server ip address + LOGGER.info("Request timeout, Details: {}.", throwable.getMessage()); + + return new InvocationException(REQUEST_TIMEOUT, + ExceptionConverter.getGenericCode(genericStatus), "Request Timeout.", + throwable); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/converter/ValidateDetail.java b/core/src/main/java/org/apache/servicecomb/core/exception/converter/ValidateDetail.java new file mode 100644 index 00000000000..06140be7ca7 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/exception/converter/ValidateDetail.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.core.exception.converter; + +public class ValidateDetail { + private String propertyPath; + + private String message; + + public ValidateDetail() { + } + + public ValidateDetail(String propertyPath, String message) { + this.propertyPath = propertyPath; + this.message = message; + } + + public String getPropertyPath() { + return propertyPath; + } + + public ValidateDetail setPropertyPath(String propertyPath) { + this.propertyPath = propertyPath; + return this; + } + + public String getMessage() { + return message; + } + + public ValidateDetail setMessage(String message) { + this.message = message; + return this; + } +} 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 new file mode 100644 index 00000000000..f03547c171a --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.executor; + +import java.util.Map; +import java.util.concurrent.Executor; + +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; + +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_THREAD_POOL = "servicecomb.executor.groupThreadPool"; + + public static final String EXECUTOR_REACTIVE = "servicecomb.executor.reactive"; + + 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) { + throw new IllegalStateException(String.format( + "duplicated executor, id=%s, old executor=%s, new executor=%s", + id, existing.getClass().getName(), executor.getClass().getName())); + } + } + + // 只会在初始化时执行,一点点重复的查找,没必要做缓存 + public Executor findExecutor(OperationMeta operationMeta) { + return findExecutor(operationMeta, null); + } + + public Executor findExecutor(OperationMeta operationMeta, Executor defaultOperationExecutor) { + // operation级别 + Executor executor = findByKey(KEY_EXECUTORS_PREFIX + operationMeta.getMicroserviceQualifiedName()); + if (executor != null) { + return executor; + } + + executor = findByKey(KEY_EXECUTORS_PREFIX + operationMeta.getSchemaQualifiedName()); + if (executor != null) { + return executor; + } + + if (defaultOperationExecutor != null) { + return defaultOperationExecutor; + } + + // schema级别 + executor = findByKey( + KEY_EXECUTORS_PREFIX + operationMeta.getMicroserviceName() + '.' + operationMeta.getSchemaId()); + if (executor != null) { + return executor; + } + + executor = findByKey(KEY_EXECUTORS_PREFIX + operationMeta.getSchemaId()); + if (executor != null) { + return executor; + } + + // microservice级别 + executor = findByKey(KEY_EXECUTORS_PREFIX + operationMeta.getMicroserviceName()); + if (executor != null) { + return executor; + } + + // 寻找config-key指定的default + executor = findByKey(KEY_EXECUTORS_DEFAULT); + if (executor != null) { + return executor; + } + + return findExecutorById(EXECUTOR_DEFAULT); + } + + private Executor findByKey(String configKey) { + String id = environment.getProperty(configKey); + if (id == null) { + return null; + } + + return findExecutorById(id); + } + + public Executor findExecutorById(String id) { + Executor executor = executors.get(id); + if (executor != null) { + return executor; + } + + return BeanUtils.getBean(id); + } +} 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 new file mode 100644 index 00000000000..13562914fac --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.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.core.executor; + +import java.io.Closeable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +public class GroupExecutor implements Executor, Closeable { + private static final Logger LOGGER = LoggerFactory.getLogger(GroupExecutor.class); + + public static final String KEY_GROUP = "servicecomb.executor.default.group"; + + // Deprecated + public static final String KEY_OLD_MAX_THREAD = "servicecomb.executor.default.thread-per-group"; + + public static final String KEY_CORE_THREADS = "servicecomb.executor.default.coreThreads-per-group"; + + public static final String KEY_MAX_THREADS = "servicecomb.executor.default.maxThreads-per-group"; + + public static final String KEY_MAX_IDLE_SECOND = "servicecomb.executor.default.maxIdleSecond-per-group"; + + public static final String KEY_MAX_QUEUE_SIZE = "servicecomb.executor.default.maxQueueSize-per-group"; + + private static final AtomicBoolean LOG_PRINTED = new AtomicBoolean(); + + private final Environment environment; + + protected String groupName; + + protected int groupCount; + + protected int coreThreads; + + protected int maxThreads; + + protected int maxIdleInSecond; + + protected int maxQueueSize; + + // to avoid multiple network thread conflicted when put tasks to executor queue + private final List executorList = new ArrayList<>(); + + // for bind network thread to one executor + // it's impossible that has too many network thread, so index will not too big that less than 0 + private final AtomicInteger index = new AtomicInteger(); + + private final Map threadExecutorMap = new ConcurrentHashMapEx<>(); + + public GroupExecutor(Environment environment) { + this.environment = environment; + } + + public GroupExecutor init() { + return init("group"); + } + + public GroupExecutor init(String groupName) { + this.groupName = groupName; + initConfig(); + + for (int groupIdx = 0; groupIdx < groupCount; groupIdx++) { + GroupThreadFactory factory = new GroupThreadFactory(groupName + groupIdx); + + ThreadPoolExecutorEx executor = new ThreadPoolExecutorEx(coreThreads, + maxThreads, + maxIdleInSecond, + TimeUnit.SECONDS, + new LinkedBlockingQueueEx(maxQueueSize), + factory); + executorList.add(executor); + } + + return this; + } + + public synchronized void initConfig() { + if (LOG_PRINTED.compareAndSet(false, true)) { + LOGGER.info("thread pool rules:\n" + + "1.use core threads.\n" + + "2.if all core threads are busy, then create new thread.\n" + + "3.if thread count reach the max limitation, then queue the request.\n" + + "4.if queue is full, and threads count is max, then reject the request."); + } + + groupCount = environment.getProperty(KEY_GROUP, int.class, 2); + coreThreads = environment.getProperty(KEY_CORE_THREADS, int.class, 25); + + maxThreads = environment.getProperty(KEY_MAX_THREADS, int.class, -1); + if (maxThreads <= 0) { + 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 { + maxThreads = 100; + } + } + if (coreThreads > maxThreads) { + LOGGER.warn("coreThreads is bigger than maxThreads, change from {} to {}.", coreThreads, maxThreads); + coreThreads = maxThreads; + } + + 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={}.", + groupName, groupCount, coreThreads, maxThreads, maxIdleInSecond, maxQueueSize); + } + + public List getExecutorList() { + return executorList; + } + + @Override + public void execute(Runnable command) { + long threadId = Thread.currentThread().getId(); + Executor executor = threadExecutorMap.computeIfAbsent(threadId, this::chooseExecutor); + + executor.execute(command); + } + + private Executor chooseExecutor(long threadId) { + int idx = index.getAndIncrement() % executorList.size(); + return executorList.get(idx); + } + + @Override + public void close() { + for (ExecutorService executorService : executorList) { + executorService.shutdown(); + } + executorList.clear(); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/executor/GroupThreadFactory.java b/core/src/main/java/org/apache/servicecomb/core/executor/GroupThreadFactory.java new file mode 100644 index 00000000000..e6dbfb27227 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/executor/GroupThreadFactory.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.core.executor; + +import java.util.Objects; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +public class GroupThreadFactory implements ThreadFactory { + private static final AtomicInteger poolNumber = new AtomicInteger(1); + + private final ThreadGroup group; + + private final AtomicInteger threadNumber = new AtomicInteger(1); + + private final String namePrefix; + + public GroupThreadFactory(String prefix) { + Objects.requireNonNull(prefix); + + group = Thread.currentThread().getThreadGroup(); + + namePrefix = prefix + "-" + poolNumber.getAndIncrement() + "-thread-"; + } + + public Thread newThread(Runnable r) { + Thread t = new Thread(group, r, + namePrefix + threadNumber.getAndIncrement(), + 0); + if (t.isDaemon()) { + t.setDaemon(false); + } + if (t.getPriority() != Thread.NORM_PRIORITY) { + t.setPriority(Thread.NORM_PRIORITY); + } + return t; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/executor/LinkedBlockingQueueEx.java b/core/src/main/java/org/apache/servicecomb/core/executor/LinkedBlockingQueueEx.java new file mode 100644 index 00000000000..aba525f73cd --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/executor/LinkedBlockingQueueEx.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.core.executor; + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; + +public class LinkedBlockingQueueEx extends LinkedBlockingQueue { + private static final long serialVersionUID = -1L; + + private transient volatile ThreadPoolExecutorEx owner = null; + + public LinkedBlockingQueueEx(int capacity) { + super(capacity); + } + + public void setOwner(ThreadPoolExecutorEx owner) { + this.owner = owner; + } + + @Override + public boolean offer(Runnable runnable) { + // task can come before owner available + if (owner == null) { + return super.offer(runnable); + } + // can not create more thread, just queue the task + if (owner.getPoolSize() == owner.getMaximumPoolSize()) { + return super.offer(runnable); + } + // no need to create more thread, just queue the task + if (owner.getNotFinished() <= owner.getPoolSize()) { + return super.offer(runnable); + } + // all threads are busy, and can create new thread, not queue the task + if (owner.getPoolSize() < owner.getMaximumPoolSize()) { + return false; + } + return super.offer(runnable); + } + + /* + * when task is rejected (thread pool if full), force the item onto queue. + */ + public boolean force(Runnable runnable) { + if (owner == null || owner.isShutdown()) { + throw new RejectedExecutionException("queue is not running."); + } + return super.offer(runnable); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/executor/ReactiveExecutor.java b/core/src/main/java/org/apache/servicecomb/core/executor/ReactiveExecutor.java new file mode 100644 index 00000000000..b062492de03 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/executor/ReactiveExecutor.java @@ -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. + */ + +package org.apache.servicecomb.core.executor; + +import java.io.Closeable; +import java.util.concurrent.Executor; + +/** + * 用于在verticle中就地执行,不做多余的调度,这是性能最高的一种模型 + */ +public class ReactiveExecutor implements Executor, Closeable { + + @Override + public void execute(Runnable command) { + command.run(); + } + + @Override + public void close() { + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/executor/ThreadPoolExecutorEx.java b/core/src/main/java/org/apache/servicecomb/core/executor/ThreadPoolExecutorEx.java new file mode 100644 index 00000000000..7249e6e7e18 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/executor/ThreadPoolExecutorEx.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.executor; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +public class ThreadPoolExecutorEx extends ThreadPoolExecutor { + private final AtomicInteger submittedCount = new AtomicInteger(); + + private final AtomicInteger finishedCount = new AtomicInteger(); + + private final AtomicInteger rejectedCount = new AtomicInteger(); + + public ThreadPoolExecutorEx(int coreThreads, int maxThreads, int maxIdleInSecond, TimeUnit timeUnit, + BlockingQueue queue, ThreadFactory threadFactory) { + super(coreThreads, maxThreads, maxIdleInSecond, timeUnit, queue, threadFactory); + if (queue instanceof LinkedBlockingQueueEx) { + ((LinkedBlockingQueueEx) queue).setOwner(this); + } + setRejectedExecutionHandler(this::rejectedExecution); + } + + @Override + public void execute(Runnable command) { + submittedCount.incrementAndGet(); + try { + super.execute(command); + } catch (RejectedExecutionException e) { + if (getQueue() instanceof LinkedBlockingQueueEx) { + final LinkedBlockingQueueEx queue = (LinkedBlockingQueueEx) getQueue(); + if (!queue.force(command)) { + throw new RejectedExecutionException("thread pool queue is full"); + } + } else { + throw e; + } + } + } + + public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { + rejectedCount.incrementAndGet(); + finishedCount.incrementAndGet(); + + throw new RejectedExecutionException("Task " + r.toString() + + " rejected from " + + e.toString()); + } + + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + finishedCount.incrementAndGet(); + } + + public int getNotFinished() { + return submittedCount.get() - finishedCount.get(); + } + + public int getRejectedCount() { + return rejectedCount.get(); + } +} 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 new file mode 100644 index 00000000000..de13aa0ac2e --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.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 ConsumerFilter extends Filter { + +} 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 new file mode 100644 index 00000000000..0d9ce402624 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.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.core.filter; + +import org.apache.servicecomb.core.filter.impl.ContextMapperFilter; +import org.apache.servicecomb.core.filter.impl.ParameterValidatorFilter; +import org.apache.servicecomb.core.filter.impl.ProviderOperationFilter; +import org.apache.servicecomb.core.filter.impl.RetryFilter; +import org.apache.servicecomb.core.filter.impl.ScheduleFilter; +import org.apache.servicecomb.governance.handler.MapperHandler; +import org.apache.servicecomb.governance.handler.RetryHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class CoreFilterConfiguration { + @Bean + public ProviderOperationFilter scbProducerOperationFilter() { + return new ProviderOperationFilter(); + } + + @Bean + public ScheduleFilter scbScheduleFilter() { + return new ScheduleFilter(); + } + + @Bean + 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 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 new file mode 100644 index 00000000000..99ed798e686 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/Filter.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.filter; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.swagger.invocation.Response; +import org.springframework.core.Ordered; + +/** + *
+ *  Filters are the basics of how an invocation is executed.
+ *
+ * thread rule:
+ *   assume a provider filter chains is: f1, f2, schedule, f3, f4
+ *
+ *   schedule is a builtIn filter, which will dispatch invocations to operation related threadPool
+ *
+ *   f1 and f2 are before "schedule" filter, default to run in eventLoop
+ *   if developers customize filters and switch to other threads
+ *   it's better to switch back to eventLoop, unless you know what you are doing
+ *
+ *   f3 and f4 are after "schedule" filter, default thread depend on controller's method signature
+ *     1. if controller method not return CompletableFuture
+ *        then will run in a real threadPool
+ *     2. if controller method return CompletableFuture
+ *        then will still run in eventLoop
+ *   so filters after "schedule" filter, are more complex than filters before "schedule"
+ *   if developers need to do some BLOCK logic, MUST use different Strategy when running in different thread:
+ *     1. threadPool: run do BLOCK logic directly
+ *     2. eventLoop: MUST submit to a threadPool, and then switch back
+ *        (reactive golden rule)
+ * 
+ */ +public interface Filter extends Ordered { + int PROVIDER_SCHEDULE_FILTER_ORDER = 0; + + int CONSUMER_LOAD_BALANCE_ORDER = 0; + + default boolean enabledForTransport(String transport) { + return true; + } + + default boolean enabledForMicroservice(String application, String serviceName) { + return true; + } + + default int getOrder(String application, String serviceName) { + return 0; + } + + default int getOrder() { + return 0; + } + + default String getName() { + throw new IllegalStateException("must provide unique filter name."); + } + + String getNameWithOrder(); + + /** + * + * @param invocation invocation + * @param nextNode node filter node + * @return response future
+ * even Response can express fail data
+ * but Response only express success data in filter chain
+ * 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 + */ + 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 new file mode 100644 index 00000000000..2cc6281d018 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.filter; + +import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; +import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.deleteLast; + +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Autowired; + +public class FilterChainsManager { + private InvocationFilterChains consumerChains; + + private InvocationFilterChains providerChains; + + private InvocationFilterChains edgeChains; + + @Autowired + public FilterChainsManager setEdgeFilters(List filters) { + edgeChains = new InvocationFilterChains(filters); + return this; + } + + @Autowired + public FilterChainsManager setConsumerFilters(List filters) { + consumerChains = new InvocationFilterChains(filters); + return this; + } + + @Autowired + public FilterChainsManager setProviderFilters(List filters) { + providerChains = new InvocationFilterChains(filters); + return this; + } + + public FilterChainsManager init() { + return this; + } + + public FilterNode findConsumerChain(String application, String serviceName) { + return consumerChains.findChain(application, serviceName); + } + + 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)); + + appendLine(sb, "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) { + return chains.getFilters().stream() + .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 new file mode 100644 index 00000000000..44a5b34d901 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.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.core.filter; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.common.utils.AsyncUtils; +import org.apache.servicecomb.swagger.invocation.Response; + +public class FilterNode { + public static final FilterNode EMPTY = new FilterNode(null) { + @Override + public CompletableFuture onFilter(Invocation invocation) { + return CompletableFuture.completedFuture(Response.ok(null)); + } + }; + + public static FilterNode buildChain(Filter... filters) { + return buildChain(Arrays.asList(filters)); + } + + public static FilterNode buildChain(List filters) { + List filterNodes = filters.stream() + .map(FilterNode::new).toList(); + + for (int idx = 0; idx < filterNodes.size() - 1; idx++) { + FilterNode currentNode = filterNodes.get(idx); + FilterNode nextNode = filterNodes.get(idx + 1); + currentNode.setNextNode(nextNode); + } + + return filterNodes.get(0); + } + + private final Filter filter; + + private FilterNode nextNode; + + public FilterNode(Filter filter) { + this.filter = filter; + } + + private void setNextNode(FilterNode nextNode) { + this.nextNode = nextNode; + } + + public CompletableFuture onFilter(Invocation invocation) { + // 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); + } + + 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 new file mode 100644 index 00000000000..6dde66c3354 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.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.core.filter; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; + +public class InvocationFilterChains { + + private final List filters; + + private final Map> microserviceChains = new ConcurrentHashMapEx<>(); + + public InvocationFilterChains(List filters) { + this.filters = filters; + } + + public List getFilters() { + return filters; + } + + 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/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 new file mode 100644 index 00000000000..d2b8eb82319 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.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.core.filter.impl; + +import java.util.concurrent.CompletableFuture; + +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.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.swagger.invocation.Response; + +// just for test +public class EmptyFilter extends AbstractFilter implements ProviderFilter, ConsumerFilter, EdgeFilter { + @Override + public String getName() { + return "empty"; + } + + @Override + 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/JacksonPropertyNodeNameProvider.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/JacksonPropertyNodeNameProvider.java new file mode 100644 index 00000000000..f617f5fe567 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/JacksonPropertyNodeNameProvider.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.core.filter.impl; + +import org.hibernate.validator.spi.nodenameprovider.JavaBeanProperty; +import org.hibernate.validator.spi.nodenameprovider.Property; +import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; + +import io.vertx.core.json.jackson.DatabindCodec; + +/** + * hibernate validator will cache the resolved data
+ * no need to worry about performance problem + */ +public class JacksonPropertyNodeNameProvider implements PropertyNodeNameProvider { + @Override + public String getName(Property property) { + if (property instanceof JavaBeanProperty) { + return getJavaBeanPropertyName((JavaBeanProperty) property); + } + + return property.getName(); + } + + private String getJavaBeanPropertyName(JavaBeanProperty property) { + ObjectMapper objectMapper = DatabindCodec.mapper(); + JavaType type = objectMapper.constructType(property.getDeclaringClass()); + BeanDescription desc = objectMapper.getSerializationConfig().introspect(type); + + return desc.findProperties() + .stream() + .filter(prop -> prop.getInternalName().equals(property.getName())) + .map(BeanPropertyDefinition::getName) + .findFirst() + .orElse(property.getName()); + } +} 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 new file mode 100644 index 00000000000..b4bf065792f --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java @@ -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. + */ +package org.apache.servicecomb.core.filter.impl; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.foundation.common.utils.AsyncUtils; +import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; +import org.apache.servicecomb.swagger.invocation.Response; +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator; +import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator; +import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; + +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; + } + } + + private static final Logger LOGGER = LoggerFactory.getLogger(ParameterValidatorFilter.class); + + public static final String NAME = "validator"; + + public static final String HIBERNATE_VALIDATE_PREFIX = "hibernate.validator"; + + public static final String ENABLE_EL = "servicecomb.filters.validation.useResourceBundleMessageInterpolator"; + + protected ExecutableValidator validator; + + @Override + public String getName() { + return NAME; + } + + @Override + 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() { + Configuration validatorConfiguration = Validation.byProvider(HibernateValidator.class) + .configure() + .propertyNodeNameProvider(new JacksonPropertyNodeNameProvider()) + .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() { + if (useResourceBundleMessageInterpolator()) { + return new ResourceBundleMessageInterpolator(); + } + return new ParameterMessageInterpolator(); + } + + private boolean useResourceBundleMessageInterpolator() { + return environment.getProperty(ENABLE_EL, boolean.class, false); + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + Set> violations = doValidate(invocation); + if (violations.size() > 0) { + LOGGER.error("Parameter validation failed : " + violations); + return AsyncUtils.completeExceptionally(new ConstraintViolationException(violations)); + } + + return nextNode.onFilter(invocation); + } + + protected Set> doValidate(Invocation invocation) { + SwaggerProducerOperation producerOperation = invocation.getOperationMeta().getSwaggerProducerOperation(); + return validator.validateParameters(producerOperation.getProducerInstance(), producerOperation.getProducerMethod(), + invocation.toProducerArguments(), Default.class); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java 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 new file mode 100644 index 00000000000..341c01fa877 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ScheduleFilter.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.filter.impl; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.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 extends AbstractFilter implements ProviderFilter { + public static final String NAME = "schedule"; + + @Override + public String getName() { + return NAME; + } + + @Override + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode next) { + 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.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 (invocation.getInvocationStageTrace().calcQueue() > nanoTimeout) { + throw new InvocationException(Status.REQUEST_TIMEOUT, "Request in the queue timed out."); + } + } +} 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 new file mode 100644 index 00000000000..948c12bb46a --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/governance/CoreGovernanceConfiguration.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.core.governance; + +import org.apache.servicecomb.core.SCBEngine; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class CoreGovernanceConfiguration { + @Bean + public ServiceCombCircuitBreakerExtension serviceCombCircuitBreakerExtension() { + return new ServiceCombCircuitBreakerExtension(); + } + + @Bean + public ServiceCombInstanceIsolationExtension serviceCombInstanceIsolationExtension() { + return new ServiceCombInstanceIsolationExtension(); + } + + @Bean + public ServiceCombMicroserviceMeta serviceCombMicroserviceMeta(SCBEngine scbEngine) { + return new ServiceCombMicroserviceMeta(scbEngine); + } + + @Bean + public ServiceCombRetryExtension serviceCombRetryExtension() { + return new ServiceCombRetryExtension(); + } + + @Bean + public ServiceCombConfigurationEventAdapter serviceCombConfigurationEventAdapter() { + return new ServiceCombConfigurationEventAdapter(); + } +} 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 new file mode 100644 index 00000000000..e0e65b9eec8 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/governance/GovernanceConfiguration.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.core.governance; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; + +public class GovernanceConfiguration { + public static final String ROOT = "servicecomb.loadbalance."; + + // retry configurations + public static final String RETRY_ENABLED = "retryEnabled"; + + public static final String RETRY_ON_NEXT = "retryOnNext"; + + public static final String RETRY_ON_SAME = "retryOnSame"; + + public static boolean isRetryEnabled(String microservice) { + String p = getStringProperty("false", + ROOT + microservice + "." + RETRY_ENABLED, + ROOT + RETRY_ENABLED); + return Boolean.parseBoolean(p); + } + + public static int getRetryNextServer(String microservice) { + return getRetryServer(microservice, RETRY_ON_NEXT); + } + + public static int getRetrySameServer(String microservice) { + return getRetryServer(microservice, RETRY_ON_SAME); + } + + private static int getRetryServer(String microservice, String retryType) { + final int defaultValue = 0; + String p = getStringProperty("0", + ROOT + microservice + "." + retryType, + ROOT + retryType); + try { + int result = Integer.parseInt(p); + if (result > 0) { + return result; + } + return defaultValue; + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public static String getStringProperty(String defaultValue, String... keys) { + String property; + for (String key : keys) { + property = LegacyPropertyFactory.getStringProperty(key); + if (property != null) { + return property; + } + } + return defaultValue; + } +} 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 new file mode 100644 index 00000000000..15d58d66610 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.governance; + +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 { + private final Invocation invocation; + + private GovernanceRequestExtractorImpl(Invocation invocation) { + this.invocation = invocation; + } + + @Override + public String apiPath() { + if (MatchType.REST.equalsIgnoreCase(invocation.getOperationMeta().getConfig().getGovernanceMatchType())) { + if (!invocation.isProducer()) { + return SwaggerUtils.concatAbsolutePath(invocation.getSchemaMeta().getSwagger(), + invocation.getOperationMeta().getOperationPath()); + } + // not highway + if (invocation.getRequestEx() != null) { + return invocation.getRequestEx().getRequestURI(); + } + } + + if (!invocation.isProducer()) { + return invocation.getOperationMeta().getMicroserviceQualifiedName(); + } + return invocation.getOperationMeta().getSchemaQualifiedName(); + } + + @Override + public String method() { + return invocation.getOperationMeta().getHttpMethod(); + } + + @Override + public String header(String key) { + Map arguments = invocation.getSwaggerArguments(); + if (arguments != null && arguments.get(key) != null) { + return arguments.get(key).toString(); + } + + if (invocation.getContext(key) != null) { + return invocation.getContext(key); + } + + if (invocation.getRequestEx() != null) { + return invocation.getRequestEx().getHeader(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.isProducer()) { + if (invocation.getEndpoint() != null && invocation.getEndpoint().getMicroserviceInstance() != null) { + return invocation.getEndpoint().getMicroserviceInstance().getInstanceId(); + } + } + return null; + } + + @Override + public String serviceName() { + if (!invocation.isProducer()) { + return invocation.getMicroserviceName(); + } + return null; + } + + @Override + public Object sourceRequest() { + return invocation; + } + } + + public static final String REST = "rest"; + + public static final String RPC = "rpc"; + + public static GovernanceRequestExtractor createGovHttpRequest(Invocation invocation) { + return new GovernanceRequestExtractorImpl(invocation); + } +} 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 new file mode 100644 index 00000000000..47792792e14 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.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.core.governance; + +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; + + private int triedCount; + + private final int retryOnSame; + + public RetryContext(int retryOnSame) { + this.retryOnSame = retryOnSame; + this.retry = false; + this.triedCount = 0; + } + + public boolean isRetry() { + return retry; + } + + public void incrementRetry() { + this.retry = true; + this.triedCount++; + } + + public boolean trySameServer() { + return triedCount <= retryOnSame; + } +} 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 new file mode 100644 index 00000000000..9177ca71820 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.core.governance; + +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.InvocationException; + +public class ServiceCombCircuitBreakerExtension extends AbstractCircuitBreakerExtension { + @Override + protected String extractStatusCode(Object result) { + if (!(result instanceof Response)) { + return null; + } + Response resp = (Response) result; + if (resp.isFailed()) { + if (resp.getResult() instanceof InvocationException) { + InvocationException e = resp.getResult(); + return String.valueOf(e.getStatusCode()); + } + } + return String.valueOf(resp.getStatusCode()); + } + + @Override + public boolean isFailedResult(List statusList, Throwable e) { + if (e instanceof InvocationException) { + InvocationException invocationException = (InvocationException) e; + return statusList.contains(String.valueOf(invocationException.getStatusCode())); + } + 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 new file mode 100644 index 00000000000..e5ee61cd586 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombConfigurationEventAdapter.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.core.governance; + + +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; + +import com.google.common.eventbus.Subscribe; + +public class ServiceCombConfigurationEventAdapter { + public ServiceCombConfigurationEventAdapter() { + EventManager.register(this); + } + + @Subscribe + public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { + GovernanceConfigurationChangedEvent newEvent = new GovernanceConfigurationChangedEvent(event.getChanged()); + GovernanceEventManager.post(newEvent); + } +} 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 new file mode 100644 index 00000000000..2bbf6d3c87d --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java @@ -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. + */ + +package org.apache.servicecomb.core.governance; + +import java.util.List; + +import org.apache.servicecomb.governance.handler.ext.AbstractInstanceIsolationExtension; + +public class ServiceCombInstanceIsolationExtension extends AbstractInstanceIsolationExtension { + private final ServiceCombRetryExtension retryExtension = new ServiceCombRetryExtension(); + + @Override + protected String extractStatusCode(Object result) { + return retryExtension.extractStatusCode(result); + } + + @Override + 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 new file mode 100644 index 00000000000..3893e3e4207 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombMicroserviceMeta.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.core.governance; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.governance.MicroserviceMeta; + +public class ServiceCombMicroserviceMeta implements MicroserviceMeta { + private SCBEngine scbEngine; + + public ServiceCombMicroserviceMeta(SCBEngine scbEngine) { + this.scbEngine = scbEngine; + } + + @Override + public String getName() { + return BootStrapProperties.readServiceName(scbEngine.getEnvironment()); + } + + @Override + public String 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 new file mode 100644 index 00000000000..871a03bfa30 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.core.governance; + +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.InvocationException; + +public class ServiceCombRetryExtension extends AbstractRetryExtension { + @Override + protected String extractStatusCode(Object result) { + if (!(result instanceof Response)) { + return null; + } + Response resp = (Response) result; + if (resp.isFailed()) { + if (resp.getResult() instanceof InvocationException) { + InvocationException e = resp.getResult(); + return String.valueOf(e.getStatusCode()); + } + } + return String.valueOf(resp.getStatusCode()); + } + + @Override + public boolean isFailedResult(List statusList, Throwable e) { + if (e instanceof InvocationException) { + InvocationException invocationException = (InvocationException) e; + return statusList.contains(String.valueOf(invocationException.getStatusCode())); + } + 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 new file mode 100644 index 00000000000..abfe2f05330 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/CoreInvocationConfiguration.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.core.invocation; + +import java.util.List; + +import org.apache.servicecomb.core.invocation.timeout.PassingTimeStrategy; +import org.apache.servicecomb.core.invocation.timeout.ProcessingTimeStrategy; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import com.google.common.eventbus.EventBus; + +@Configuration +public class CoreInvocationConfiguration { + @Bean + public InvocationTimeoutBootListener scbInvocationTimeoutBootListener(EventBus eventBus, + List strategies, + Environment environment) { + return new InvocationTimeoutBootListener(eventBus, strategies, environment); + } + + @Bean + public PassingTimeStrategy scbPassingTimeStrategy() { + return new PassingTimeStrategy(); + } + + @Bean + 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 new file mode 100644 index 00000000000..323ad924a09 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.invocation; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.Invocation; + +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 new file mode 100644 index 00000000000..776fdb32568 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationFactory.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.core.invocation; + +import java.util.Map; + +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; + +public final class InvocationFactory { + private InvocationFactory() { + } + + public static Invocation forConsumer(ReferenceConfig referenceConfig, OperationMeta operationMeta, + InvocationRuntimeType invocationRuntimeType, Map swaggerArguments) { + Invocation invocation = new Invocation(referenceConfig, + operationMeta, + invocationRuntimeType, + swaggerArguments); + return setSrcMicroservice(invocation); + } + + public static Invocation setSrcMicroservice(Invocation invocation) { + 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(CoreConst.SRC_SERVICE_ID, + SCBEngine.getInstance().getRegistrationManager().getServiceId("sc-registration")); + } + if (addSourceInstanceId()) { + invocation.addContext(CoreConst.SRC_INSTANCE_ID, + SCBEngine.getInstance().getRegistrationManager().getInstanceId("sc-registration")); + } + return invocation; + } + + public static boolean addSourceServiceId() { + return SCBEngine.getInstance().getEnvironment(). + getProperty("servicecomb.context.source.serviceId", boolean.class, true); + } + + public static boolean addSourceInstanceId() { + return SCBEngine.getInstance().getEnvironment(). + getProperty("servicecomb.context.source.instanceId", boolean.class, true); + } + + /* + * transport server收到请求时,创建invocation + */ + public static Invocation forProvider(Endpoint endpoint, + OperationMeta operationMeta, + Map swaggerArguments) { + SCBEngine.getInstance().ensureStatusUp(); + return new Invocation(endpoint, + operationMeta, + swaggerArguments); + } +} 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 new file mode 100644 index 00000000000..f9d0166a458 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationStageTrace.java @@ -0,0 +1,351 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.invocation; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.core.Invocation; + +/** + *
+ * for consumer:
+ *
+ *  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:
+ *
+ *  *  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 class Stage { + private long beginTime; + + private long endTime; + + public long getBeginTime() { + return beginTime; + } + + public long getEndTime() { + return endTime; + } + } + + public static final String STAGE_TOTAL = "total"; + + public static final String STAGE_PREPARE = "prepare"; + + public static final String STAGE_PROVIDER_QUEUE = "queue"; + + public static final String STAGE_PROVIDER_DECODE_REQUEST = "provider-decode"; + + public static final String STAGE_PROVIDER_ENCODE_RESPONSE = "provider-encode"; + + public static final String STAGE_PROVIDER_SEND = "provider-send"; + + public static final String STAGE_PROVIDER_BUSINESS = "execute"; + + public static final String STAGE_CONSUMER_CONNECTION = "connection"; + + public static final String STAGE_CONSUMER_ENCODE_REQUEST = "consumer-encode"; + + public static final String STAGE_CONSUMER_DECODE_RESPONSE = "consumer-decode"; + + public static final String STAGE_CONSUMER_SEND = "consumer-send"; + + public static final String STAGE_CONSUMER_WAIT = "wait"; + + private final Invocation invocation; + + // 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 finish; + + private long startCreateInvocation; + + private long finishCreateInvocation; + + private long startProviderQueue; + + private long finishProviderQueue; + + private long startConsumerConnection; + + private long finishConsumerConnection; + + private long startProviderDecodeRequest; + + private long finishProviderDecodeRequest; + + private long startProviderEncodeResponse; + + private long finishProviderEncodeResponse; + + private long startConsumerEncodeRequest; + + private long finishConsumerEncodeRequest; + + private long startConsumerDecodeResponse; + + private long finishConsumerDecodeResponse; + + private long startProviderSendResponse; + + private long finishProviderSendResponse; + + private long startConsumerSendRequest; + + private long finishConsumerSendRequest; + + private long startBusinessExecute; + + private long finishBusinessExecute; + + private long startWaitResponse; + + private long finishWaitResponse; + + // invocation stage can not be used in concurrent access + private final Map stages = new HashMap<>(); + + public InvocationStageTrace(Invocation invocation) { + this.invocation = invocation; + } + + 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 recordStageEnd(String realStageName) { + Stage stage = stages.get(realStageName); + stage.endTime = nanoTime(); + } + + public Map getStages() { + return stages; + } + + public void finish() { + this.finish = nanoTime(); + } + + public void startCreateInvocation(long nano) { + this.startCreateInvocation = nano; + this.startInMillis = millisTime(); + this.start = nanoTime(); + } + + public void finishCreateInvocation() { + this.finishCreateInvocation = nanoTime(); + } + + public long calcPrepare() { + return calc(finishCreateInvocation, startCreateInvocation); + } + + public void startProviderQueue() { + this.startProviderQueue = nanoTime(); + } + + public void finishProviderQueue() { + this.finishProviderQueue = nanoTime(); + } + + public long calcQueue() { + return calc(finishProviderQueue, startProviderQueue); + } + + public void startProviderDecodeRequest() { + this.startProviderDecodeRequest = nanoTime(); + } + + public void finishProviderDecodeRequest() { + this.finishProviderDecodeRequest = nanoTime(); + } + + public long calcProviderDecodeRequest() { + return calc(finishProviderDecodeRequest, startProviderDecodeRequest); + } + + public void startProviderEncodeResponse() { + this.startProviderEncodeResponse = nanoTime(); + } + + public void finishProviderEncodeResponse() { + this.finishProviderEncodeResponse = nanoTime(); + } + + public long calcProviderEncodeResponse() { + return calc(finishProviderEncodeResponse, startProviderEncodeResponse); + } + + public void startConsumerEncodeRequest() { + this.startConsumerEncodeRequest = nanoTime(); + } + + public void finishConsumerEncodeRequest() { + this.finishConsumerEncodeRequest = nanoTime(); + } + + public long calcConsumerEncodeRequest() { + return calc(finishConsumerEncodeRequest, startConsumerEncodeRequest); + } + + public void startConsumerDecodeResponse() { + this.startConsumerDecodeResponse = nanoTime(); + } + + public void finishConsumerDecodeResponse() { + this.finishConsumerDecodeResponse = nanoTime(); + } + + public long calcConsumerDecodeResponse() { + return calc(finishConsumerDecodeResponse, startConsumerDecodeResponse); + } + + public void startProviderSendResponse() { + this.startProviderSendResponse = nanoTime(); + } + + public void finishProviderSendResponse() { + this.finishProviderSendResponse = nanoTime(); + } + + public long calcProviderSendResponse() { + return calc(finishProviderSendResponse, startProviderSendResponse); + } + + public void startBusinessExecute() { + this.startBusinessExecute = nanoTime(); + } + + public void finishBusinessExecute() { + this.finishBusinessExecute = nanoTime(); + } + + public long calcBusinessExecute() { + return calc(finishBusinessExecute, startBusinessExecute); + } + + public void startConsumerConnection() { + this.startConsumerConnection = nanoTime(); + } + + public void finishConsumerConnection() { + this.finishConsumerConnection = nanoTime(); + } + + public long calcConnection() { + return calc(finishConsumerConnection, startConsumerConnection); + } + + public void startConsumerSendRequest() { + this.startConsumerSendRequest = nanoTime(); + } + + public void finishConsumerSendRequest() { + this.finishConsumerSendRequest = nanoTime(); + } + + public long calcConsumerSendRequest() { + return calc(finishConsumerSendRequest, startConsumerSendRequest); + } + + public void startWaitResponse() { + this.startWaitResponse = nanoTime(); + } + + public void finishWaitResponse() { + this.finishWaitResponse = nanoTime(); + } + + public long calcWait() { + return calc(finishWaitResponse, startWaitResponse); + } + + public long calcTotal() { + return calc(finish, this.startCreateInvocation); + } + + public long getStartInMillis() { + return this.startInMillis; + } + + public long getStart() { + return this.start; + } + + public static long calc(long finish, long start) { + if (finish == 0 || start == 0) { + return 0; + } + + return finish - start; + } + + /* + * Holder for testing purpose + */ + protected long nanoTime() { + return System.nanoTime(); + } + + 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 new file mode 100644 index 00000000000..6a5e929a8bd --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutBootListener.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.core.invocation; + +import java.util.List; + +import org.apache.servicecomb.core.event.InvocationBusinessFinishEvent; +import org.apache.servicecomb.core.event.InvocationBusinessMethodStartEvent; +import org.apache.servicecomb.core.event.InvocationStartEvent; +import org.apache.servicecomb.core.event.InvocationStartSendRequestEvent; +import org.apache.servicecomb.core.event.InvocationTimeoutCheckEvent; +import org.apache.servicecomb.core.invocation.timeout.PassingTimeStrategy; +import org.apache.servicecomb.foundation.common.event.EnableExceptionPropagation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + +@SuppressWarnings({"unused"}) +public final class InvocationTimeoutBootListener { + private static final Logger LOGGER = LoggerFactory.getLogger(InvocationTimeoutBootListener.class); + + public static final String PREFIX = "servicecomb.invocation.timeout.check"; + + public static final String STRATEGY = PREFIX + ".strategy"; + + public static final String ENABLED = PREFIX + ".enabled"; + + private final InvocationTimeoutStrategy strategy; + + public InvocationTimeoutBootListener(EventBus eventBus, List strategies, + Environment environment) { + if (!environment.getProperty(ENABLED, boolean.class, false)) { + strategy = null; + return; + } + + String strategyName = environment.getProperty(STRATEGY, PassingTimeStrategy.NAME); + // if strategyName is wrong, then just throw exception + strategy = strategies.stream() + .filter(invocationTimeoutStrategy -> strategyName.equals(invocationTimeoutStrategy.name())) + .findFirst() + .orElseThrow(() -> new IllegalStateException("can not find InvocationTimeoutStrategy, name=" + strategyName)); + eventBus.register(this); + } + + @Subscribe + @EnableExceptionPropagation + public void onInvocationTimeoutCheckEvent(InvocationTimeoutCheckEvent event) { + strategy.checkTimeout(event.getInvocation()); + } + + @Subscribe + public void onInvocationStartEvent(InvocationStartEvent event) { + strategy.start(event.getInvocation()); + } + + @Subscribe + @EnableExceptionPropagation + public void onInvocationBusinessMethodStartEvent(InvocationBusinessMethodStartEvent event) { + strategy.startBusinessMethod(event.getInvocation()); + } + + @Subscribe + @EnableExceptionPropagation + public void onInvocationBusinessFinishEvent(InvocationBusinessFinishEvent event) { + strategy.finishBusinessMethod(event.getInvocation()); + } + + @Subscribe + @EnableExceptionPropagation + public void onInvocationStartSendRequestEvent(InvocationStartSendRequestEvent event) { + strategy.beforeSendRequest(event.getInvocation()); + } +} 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 new file mode 100644 index 00000000000..c530acfca29 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutStrategy.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.core.invocation; + +import static jakarta.ws.rs.core.Response.Status.REQUEST_TIMEOUT; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.exception.ExceptionCodes; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +public interface InvocationTimeoutStrategy { + // indicate whether invocation already timeout inside a process + // null is not timeout + // other value is timeout + String CHAIN_ALREADY_TIMED_OUT = "x-scb-chain-timed-out"; + + String name(); + + void start(Invocation invocation); + + default void startRunInExecutor(Invocation invocation) { + checkTimeout(invocation); + } + + default void startBusinessMethod(Invocation invocation) { + checkTimeout(invocation); + } + + default void finishBusinessMethod(Invocation invocation) { + checkTimeout(invocation); + } + + default void beforeSendRequest(Invocation invocation) { + checkTimeout(invocation); + } + + default void checkTimeout(Invocation invocation) { + long nanoInvocationTimeout = invocation.getOperationMeta().getConfig().getNanoInvocationTimeout(); + if (nanoInvocationTimeout <= 0 || alreadyTimeout(invocation)) { + return; + } + + long nanoTime = calculateElapsedNanoTime(invocation); + if (nanoTime <= nanoInvocationTimeout) { + return; + } + + invocation.addLocalContext(CHAIN_ALREADY_TIMED_OUT, true); + throw new InvocationException(REQUEST_TIMEOUT, ExceptionCodes.INVOCATION_TIMEOUT, "Invocation Timeout."); + } + + default boolean alreadyTimeout(Invocation invocation) { + return invocation.getLocalContext(CHAIN_ALREADY_TIMED_OUT) != null; + } + + long calculateElapsedNanoTime(Invocation 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 new file mode 100644 index 00000000000..ec589f37733 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.invocation; + +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; +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); + + private final long startTime = System.nanoTime(); + + private final InvocationCreator invocationCreator; + + protected final HttpServletRequestEx requestEx; + + protected final HttpServletResponseEx responseEx; + + public ProducerInvocationFlow(InvocationCreator invocationCreator) { + this(invocationCreator, null, null); + } + + public ProducerInvocationFlow(InvocationCreator invocationCreator, + HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { + this.invocationCreator = invocationCreator; + this.requestEx = requestEx; + this.responseEx = responseEx; + } + + public void run() { + CompletableFuture.completedFuture(null) + .thenCompose(v -> invocationCreator.createAsync()) + .exceptionally(this::sendCreateInvocationException) + .thenAccept(this::tryRunInvocation); + } + + private void tryRunInvocation(Invocation invocation) { + if (invocation == null) { + return; + } + 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; + } + 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 finishInvocation(Invocation invocation, Response response) { + invocation.onFinish(response); + } + + protected abstract Invocation sendCreateInvocationException(Throwable throwable); + + protected abstract void endResponse(Invocation invocation, Response response); +} diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointCacheUtils.java b/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointCacheUtils.java new file mode 100644 index 00000000000..141a6e9063b --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointCacheUtils.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.core.invocation.endpoint; + +import org.apache.servicecomb.core.Endpoint; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +public final class EndpointCacheUtils { + private static final LoadingCache CACHE = CacheBuilder.newBuilder() + .maximumSize(10000) + .build(new CacheLoader() { + @Override + public Endpoint load(String uri) { + return EndpointCacheUtils.create(uri); + } + }); + + /** + * @param uri https://www.abc.com:12345 + * @return endpoint + */ + public static Endpoint getOrCreate(String uri) { + return CACHE.getUnchecked(uri); + } + + public static Endpoint create(String uri) { + return EndpointUtils.parse(EndpointUtils.formatFromUri(uri)); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointContextRegister.java b/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointContextRegister.java new file mode 100644 index 00000000000..ff0da7e2398 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointContextRegister.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.core.invocation.endpoint; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.swagger.generator.SwaggerContextRegister; + +public class EndpointContextRegister implements SwaggerContextRegister { + @Override + public Type getContextType() { + return Endpoint.class; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointMapper.java b/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointMapper.java new file mode 100644 index 00000000000..a792b9ccd9f --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointMapper.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.invocation.endpoint; + +import java.util.Map; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerArgumentMapper; + +public class EndpointMapper extends ConsumerArgumentMapper { + private final String invocationArgumentName; + + public EndpointMapper(String invocationArgumentName) { + this.invocationArgumentName = invocationArgumentName; + } + + @Override + public void invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, + Map swaggerArguments, Map invocationArguments) { + Invocation invocation = (Invocation) swaggerInvocation; + invocation.setEndpoint((Endpoint) invocationArguments.get(invocationArgumentName)); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointMapperFactory.java b/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointMapperFactory.java new file mode 100644 index 00000000000..ab1cebd11e7 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointMapperFactory.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.core.invocation.endpoint; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; +import org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory; + +public class EndpointMapperFactory implements ConsumerContextArgumentMapperFactory { + @Override + public Class getContextClass() { + return Endpoint.class; + } + + @Override + public ArgumentMapper create(String invocationArgumentName, String swaggerArgumentName) { + return new EndpointMapper(invocationArgumentName); + } +} 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 new file mode 100644 index 00000000000..2d8b37c997a --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtils.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.invocation.endpoint; + +import static com.google.common.collect.ImmutableMap.of; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; + +import org.apache.http.client.utils.URIBuilder; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.core.exception.Exceptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class EndpointUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(EndpointUtils.class); + + private static final String HTTP = "http"; + + private static final String HTTPS = "https"; + + private static final String H2C = "h2c"; + + private static final String H2 = "h2"; + + private static final String HTTP2 = "http2"; + + private static final int DEFAULT_HTTP_PORT = 80; + + private static final int DEFAULT_HTTPS_PORT = 443; + + private static class SchemeMeta { + String protocol; + + boolean ssl; + + int defaultPort; + + public SchemeMeta(String protocol, boolean ssl, int defaultPort) { + this.protocol = protocol; + this.ssl = ssl; + this.defaultPort = defaultPort; + } + } + + private static final Map SCHEME_META_MAP = of( + HTTP, new SchemeMeta(null, false, DEFAULT_HTTP_PORT), + HTTPS, new SchemeMeta(null, true, DEFAULT_HTTPS_PORT), + H2C, new SchemeMeta(HTTP2, false, DEFAULT_HTTP_PORT), + H2, new SchemeMeta(HTTP2, true, DEFAULT_HTTPS_PORT) + ); + + private EndpointUtils() { + } + + /** + * + * @param uriEndpoint eg: rest://xxx?sslEnabled=true + * @return Endpoint object + */ + public static Endpoint parse(String uriEndpoint) { + URI uri = URI.create(uriEndpoint); + Transport transport = SCBEngine.getInstance().getTransportManager().findTransport(uri.getScheme()); + if (transport == null) { + LOGGER.error("not deployed transport, uri={}.", uriEndpoint); + throw Exceptions.genericConsumer("the endpoint's transport is not found."); + } + + return new Endpoint(transport, uriEndpoint); + } + + /** + * {@code + * http://xxx -> rest://xxx + * https://xxx -> rest://xxx?sslEnabled=true + * + * h2c://xxx -> rest://xxx?protocol=http2 + * h2://xxx -> rest://xxx?sslEnabled=true&protocol=http2 + * + * 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. + **/ + public static String formatFromUri(String inputUri) { + try { + return doFormatFromUri(inputUri); + } catch (URISyntaxException e) { + throw new IllegalStateException("failed to convert uri to endpoint.", e); + } + } + + private static String doFormatFromUri(String inputUri) throws URISyntaxException { + URIBuilder builder = new URIBuilder(inputUri); + if (builder.getScheme() == null) { + builder.setScheme(H2C); + builder.setHost(extractHostFromPath(builder)); + builder.setPath(null); + } + + SchemeMeta schemeMeta = SCHEME_META_MAP.get(builder.getScheme()); + if (schemeMeta == null) { + return inputUri; + } + + return format(builder, schemeMeta); + } + + private static String extractHostFromPath(URIBuilder builder) { + String path = builder.getPath(); + if (path == null) { + return null; + } + if (path.startsWith("/")) { + path = path.substring(1); + } + if (path.contains("/")) { + path = path.substring(0, path.indexOf("/")); + } + return path; + } + + private static String format(URIBuilder builder, SchemeMeta schemeMeta) throws URISyntaxException { + if (schemeMeta.ssl) { + builder.addParameter("sslEnabled", "true"); + } + if (schemeMeta.protocol != null) { + builder.addParameter("protocol", schemeMeta.protocol); + } + if (builder.getPort() == -1) { + builder.setPort(schemeMeta.defaultPort); + } + + return builder.setScheme("rest").build().toString(); + } +} 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 new file mode 100644 index 00000000000..6d55c7a7f51 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategy.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.core.invocation.timeout; + +import java.time.Clock; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.invocation.InvocationTimeoutStrategy; + +/** + *
+ * based on time synchronization
+ * any time to calculate timeout: now - start time of invocation chain
+ *
+ * consumer: c
+ * producer: p
+ * ---------------------------------------------------------------
+ * | process 2                                                   |
+ * |                 c-send(T5)                       c-send(T8) |
+ * |                    ↑                               ↑      |
+ * | p-start(T3) → c-start(T4) → p-operation(T6) → c-start(T7)|
+ * -----↑--------------------------------------------------------
+ *      ↑
+ * -----↑-----------------
+ * |    ↑     process 1  |
+ * |  c-send(T2)          |
+ * |    ↑                |
+ * |  c-start(T1)         |
+ * ------------------------
+ *
+ * T2 timeout: T2 - T1
+ * T3 timeout: T3 - T1
+ * T4 timeout: T4 - T1
+ * ......
+ * 
+ */ +public class PassingTimeStrategy implements InvocationTimeoutStrategy { + public static final String NAME = "passing-time"; + + // milliseconds + // depend on time synchronization + // transfer between processes + public static final String CHAIN_START_TIME = "x-scb-chain-start"; + + private Clock clock = Clock.systemDefaultZone(); + + public PassingTimeStrategy setClock(Clock clock) { + this.clock = clock; + return this; + } + + @Override + public String name() { + return NAME; + } + + @Override + public void start(Invocation invocation) { + if (invocation.getLocalContext(CHAIN_START_TIME) != null) { + return; + } + + long startTimeMillis = invocation.getInvocationStageTrace().getStartInMillis(); + String contextChainStartTime = invocation.getContext(CHAIN_START_TIME); + if (StringUtils.isEmpty(contextChainStartTime)) { + invocation.addContext(CHAIN_START_TIME, String.valueOf(startTimeMillis)); + invocation.addLocalContext(CHAIN_START_TIME, startTimeMillis); + return; + } + + long chainStartTime = NumberUtils.toLong(contextChainStartTime, startTimeMillis); + invocation.addLocalContext(CHAIN_START_TIME, chainStartTime); + } + + @Override + public long calculateElapsedNanoTime(Invocation invocation) { + long passingTimeMillis = clock.millis() - invocation.getLocalContext(CHAIN_START_TIME); + return TimeUnit.MILLISECONDS.toNanos(passingTimeMillis); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategy.java b/core/src/main/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategy.java new file mode 100644 index 00000000000..4c2c021a42b --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategy.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.core.invocation.timeout; + +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.invocation.InvocationTimeoutStrategy; + +import com.google.common.base.Ticker; + +/** + *
+ * Cumulative Processing Time
+ * not depend on time synchronization
+ * but lost network and framework outside of servicecomb processing time
+ *
+ * consumer: c
+ * producer: p
+ * ---------------------------------------------------------------
+ * | process 2                                                   |
+ * |                 c-send(T5)                       c-send(T8) |
+ * |                    ↑                               ↑      |
+ * | p-start(T3) → c-start(T4) → p-operation(T6) → c-start(T7)|
+ * -----↑--------------------------------------------------------
+ *      ↑
+ * -----↑-----------------
+ * |    ↑     process 1  |
+ * |  c-send(T2)          |
+ * |    ↑                |
+ * |  c-start(T1)         |
+ * ------------------------
+ *
+ * T2 timeout: T2 - T1
+ * T3 timeout: (T2 - T1) + (T3 - T3)
+ * T4 timeout: (T2 - T1) + (T4 - T3)
+ * T5 timeout: (T2 - T1) + (T5 - T3)
+ * T6 timeout: (T2 - T1) + (T6 - T3)
+ * T7 timeout: (T2 - T1) + (T7 - T3)
+ * T8 timeout: (T2 - T1) + (T8 - T3)
+ * ......
+ * 
+ */ +public class ProcessingTimeStrategy implements InvocationTimeoutStrategy { + public static final String NAME = "processing-time"; + + // nanoseconds + // used inside one process + // not depend on time synchronization + public static final String CHAIN_START_TIME = "x-scb-process-chain-start"; + + // nanoseconds + // processing time of all previous process + // transfer between processes + public static final String CHAIN_PROCESSING = "x-scb-processing-time"; + + private Ticker ticker = Ticker.systemTicker(); + + public ProcessingTimeStrategy setTicker(Ticker ticker) { + this.ticker = ticker; + return this; + } + + @Override + public String name() { + return NAME; + } + + @Override + public void start(Invocation invocation) { + initProcessChainStart(invocation); + initChainProcessing(invocation); + } + + private void initProcessChainStart(Invocation invocation) { + if (invocation.getLocalContext(CHAIN_START_TIME) != null) { + return; + } + + invocation.addLocalContext(CHAIN_START_TIME, invocation.getInvocationStageTrace().getStart()); + } + + private void initChainProcessing(Invocation invocation) { + if (invocation.getLocalContext(CHAIN_PROCESSING) != null) { + return; + } + + String contextChainProcessing = invocation.getContext(CHAIN_PROCESSING); + long chainProcessingTime = NumberUtils.toLong(contextChainProcessing, 0L); + invocation.addLocalContext(CHAIN_PROCESSING, chainProcessingTime); + } + + @Override + public void beforeSendRequest(Invocation invocation) { + InvocationTimeoutStrategy.super.beforeSendRequest(invocation); + + long processingTime = calculateElapsedNanoTime(invocation); + invocation.addContext(CHAIN_PROCESSING, Long.toString(processingTime)); + } + + @Override + public long calculateElapsedNanoTime(Invocation invocation) { + long chainStartTime = invocation.getLocalContext(CHAIN_START_TIME); + long previousProcessingTime = invocation.getLocalContext(CHAIN_PROCESSING); + return ticker.read() - chainStartTime + previousProcessingTime; + } +} 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/ConsumerProviderManager.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ConsumerProviderManager.java new file mode 100644 index 00000000000..615063bd3b4 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ConsumerProviderManager.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.core.provider.consumer; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +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 static final Logger LOGGER = LoggerFactory.getLogger(ConsumerProviderManager.class); + + 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 { + 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 new file mode 100644 index 00000000000..152f43a5034 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java @@ -0,0 +1,191 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.apache.servicecomb.core.exception.Exceptions.toConsumerResponse; + +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +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.MicroserviceMeta; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.definition.SchemaMeta; +import org.apache.servicecomb.core.invocation.InvocationFactory; +import org.apache.servicecomb.foundation.common.utils.AsyncUtils; +import org.apache.servicecomb.swagger.invocation.AsyncResponse; +import org.apache.servicecomb.swagger.invocation.Response; +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 io.vertx.core.Context; +import jakarta.ws.rs.core.Response.Status; + +public final class InvokerUtils { + @SuppressWarnings({"unchecked"}) + public static T syncInvoke(String microserviceName, String transport, + String schemaId, String operationId, Map swaggerArguments, Type responseType) { + Invocation invocation = createInvocation(microserviceName, transport, schemaId, operationId, + swaggerArguments, responseType); + return (T) syncInvoke(invocation); + } + + public static void reactiveInvoke(String microserviceName, String transport, + String schemaId, String operationId, Map swaggerArguments, Type responseType, + AsyncResponse asyncResp) { + 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, + schemaId, operationId, swaggerArguments, responseType); + } + + public static void reactiveInvoke(String microserviceName, String schemaId, String operationId, + Map swaggerArguments, Type responseType, + AsyncResponse asyncResp) { + reactiveInvoke(microserviceName, null, + schemaId, operationId, swaggerArguments, responseType, asyncResp); + } + + public static Invocation createInvocation(String microserviceName, String transport, + String schemaId, String operationId, Map swaggerArguments, Type responseType) { + long startCreateInvocation = System.nanoTime(); + MicroserviceReferenceConfig microserviceReferenceConfig = SCBEngine.getInstance() + .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); + 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, String, Map, Type)} instead. + * + */ + @Deprecated + public static Object syncInvoke(String microserviceName, String transport, + String schemaId, String operationId, Map swaggerArguments) { + return syncInvoke(microserviceName, transport, schemaId, operationId, swaggerArguments, + null); + } + + /** + * This is an internal API, caller make sure already invoked SCBEngine.ensureStatusUp + */ + public static Object syncInvoke(Invocation invocation) throws InvocationException { + Response response = innerSyncInvoke(invocation); + if (response.isSucceed()) { + return response.getResult(); + } + throw ExceptionFactory.convertConsumerException(response.getResult()); + } + + public static boolean isInEventLoop() { + return Context.isOnEventLoopThread(); + } + + /** + * This is an internal API, caller make sure already invoked SCBEngine.ensureStatusUp + */ + public static Response innerSyncInvoke(Invocation invocation) { + 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()); + } + + /** + * This is an internal API, caller make sure already invoked SCBEngine.ensureStatusUp + */ + public static void reactiveInvoke(Invocation invocation, AsyncResponse asyncResp) { + invoke(invocation).whenComplete((r, e) -> { + if (e == null) { + asyncResp.complete(r); + } else { + asyncResp.consumerFail(e); + } + }); + } + + public static boolean isSyncMethod(Method method) { + return !isAsyncMethod(method); + } + + public static boolean isAsyncMethod(Method method) { + // currently only support CompletableFuture for async method definition + return method.getReturnType().equals(CompletableFuture.class); + } + + public static T toSync(CompletableFuture future, long waitInMillis) { + try { + if (waitInMillis > 0) { + return future.get(waitInMillis, TimeUnit.MILLISECONDS); + } + return future.get(); + } catch (ExecutionException executionException) { + throw AsyncUtils.rethrow(executionException.getCause()); + } catch (TimeoutException timeoutException) { + throw new InvocationException(Status.REQUEST_TIMEOUT, + new CommonExceptionData("Invocation Timeout."), timeoutException); + } catch (Throwable e) { + throw AsyncUtils.rethrow(e); + } + } + + /** + * This method is used in new Filter implementation to replace Handler + * NOTE: this method should never throw exception directly + */ + public static CompletableFuture invoke(Invocation invocation) { + 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 new file mode 100644 index 00000000000..60948bd130d --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/MicroserviceReferenceConfig.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.definition.MicroserviceMeta; +import org.apache.servicecomb.core.definition.OperationMeta; + +/** + * microservice meta data for consumer. + */ +public class MicroserviceReferenceConfig { + private final String appId; + + private final String microserviceName; + + private final MicroserviceMeta microserviceMeta; + + public MicroserviceReferenceConfig( + String appId, + String microserviceName, + MicroserviceMeta microserviceMeta) { + this.appId = appId; + this.microserviceName = microserviceName; + this.microserviceMeta = microserviceMeta; + } + + 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.", + appId, + microserviceName)); + } + + return microserviceMeta; + } + + public ReferenceConfig createReferenceConfig(OperationMeta operationMeta) { + return createReferenceConfig(null, operationMeta); + } + + public ReferenceConfig createReferenceConfig(String transport, OperationMeta operationMeta) { + if (transport == null) { + transport = operationMeta.getConfig().getTransport(); + } + final ReferenceConfig referenceConfig = new ReferenceConfig(transport); + return referenceConfig; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReactiveResponseExecutor.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReactiveResponseExecutor.java new file mode 100644 index 00000000000..2c94d08c31a --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReactiveResponseExecutor.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.core.provider.consumer; + +import java.util.concurrent.Executor; + +/** + * reactive场景,就地执行即可 + */ +public class ReactiveResponseExecutor implements Executor { + @Override + public void execute(Runnable cmd) { + cmd.run(); + } +} 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 new file mode 100644 index 00000000000..3b6ad24e610 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfig.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.core.provider.consumer; + +// operation level, to keep compatible, not change name +public class ReferenceConfig { + protected String transport; + + public ReferenceConfig(String transport) { + this.transport = transport; + } + + public String getTransport() { + return transport; + } + + public void setTransport(String transport) { + this.transport = transport; + } +} 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 new file mode 100644 index 00000000000..8e86d9d7f90 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/SyncResponseExecutor.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.core.provider.consumer; + +import static jakarta.ws.rs.core.Response.Status.REQUEST_TIMEOUT; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.exception.ExceptionCodes; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +/** + * 业务线程在阻塞等待着,不必另起线程 + * 将应答流程包装为Runnable,先唤醒业务线程,再在业务线程中执行runnable + */ +public class SyncResponseExecutor implements Executor { + private final CountDownLatch latch; + + private Runnable cmd; + + private Response response; + + public SyncResponseExecutor() { + latch = new CountDownLatch(1); + } + + @Override + public void execute(Runnable cmd) { + this.cmd = cmd; + + // one network thread, many connections, then this notify will be performance bottlenecks + // if save to a queue, and other thread(s) to invoke countDown, will get good performance + // but if have multiple network thread, this "optimization" will reduce performance + // now not change this. + latch.countDown(); + } + + public Response waitResponse(Invocation invocation) throws InvocationException { + guardedWait(invocation); + + // cmd为null,是没走execute,直接返回的场景 + if (cmd != null) { + cmd.run(); + } + + return response; + } + + public void setResponse(Response response) { + this.response = response; + if (cmd == null) { + // 1. 走到这里,没有cmd,说明没走到网络线程,直接就返回了。 + // 2. 或者在网络线程中没使用execute的方式返回,这会导致返回流程在网络线程中执行,虽然不合适,但是也不应该导致业务线程无法唤醒 + latch.countDown(); + } + } + + private void guardedWait(Invocation invocation) throws InvocationException { + long wait = getWaitTime(invocation); + try { + if (wait <= 0) { + latch.await(); + return; + } + if (latch.await(wait, TimeUnit.MILLISECONDS)) { + return; + } + } catch (InterruptedException e) { + //ignore + } + throw new InvocationException(REQUEST_TIMEOUT, ExceptionCodes.INVOCATION_TIMEOUT, "Invocation Timeout."); + } + + private long getWaitTime(Invocation invocation) { + if (invocation.getOperationMeta().getConfig().getMsInvocationTimeout() > 0) { + // if invocation timeout configured, use it. + return invocation.getOperationMeta().getConfig().getMsInvocationTimeout(); + } + + // In invocation handlers, may call other microservices, invocation + // timeout may be much longer than request timeout. + // 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/AbstractProducerProvider.java b/core/src/main/java/org/apache/servicecomb/core/provider/producer/AbstractProducerProvider.java new file mode 100644 index 00000000000..0014729a90d --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/producer/AbstractProducerProvider.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.core.provider.producer; + +import org.apache.servicecomb.core.ProducerProvider; + +public abstract class AbstractProducerProvider implements ProducerProvider { +} 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 new file mode 100644 index 00000000000..7dc291157d3 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerBootListener.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.core.provider.producer; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ExecutorService; + +import org.apache.commons.io.FileUtils; +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.IOUtils; +import org.apache.servicecomb.registry.RegistrationManager; +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 io.swagger.v3.oas.models.OpenAPI; + +public class ProducerBootListener implements BootListener { + private static final Logger LOGGER = LoggerFactory.getLogger(ProducerBootListener.class); + + private static final String PATTERN = File.separator + "microservices" + + File.separator + "%s" + File.separator + "%s.yaml"; + + 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 = 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; + MicroserviceMeta microserviceMeta = event.getScbEngine().getProducerMicroserviceMeta(); + for (SchemaMeta schemaMeta : microserviceMeta.getSchemaMetas().values()) { + OpenAPI swagger = schemaMeta.getSwagger(); + String content = SwaggerUtils.swaggerToString(swagger); + if (exportToFile) { + exportToFile(String.format(filePath, + BootStrapProperties.readServiceName(environment), schemaMeta.getSchemaId()), content); + } else { + LOGGER.info("generate swagger for {}/{}/{}, swagger: {}", + microserviceMeta.getAppId(), + microserviceMeta.getMicroserviceName(), + schemaMeta.getSchemaId(), + content); + } + this.registrationManager.addSchema(schemaMeta.getSchemaId(), content); + } + } + + + // bug: can not close all thread for edge + @Override + public void onAfterClose(BootEvent event) { + MicroserviceMeta microserviceMeta = event.getScbEngine().getProducerMicroserviceMeta(); + if (microserviceMeta == null) { + return; + } + + for (OperationMeta operationMeta : microserviceMeta.getOperations()) { + if (operationMeta.getExecutor() instanceof ExecutorService) { + ((ExecutorService) operationMeta.getExecutor()).shutdown(); + continue; + } + + if (operationMeta.getExecutor() instanceof Closeable) { + IOUtils.closeQuietly((Closeable) operationMeta.getExecutor()); + continue; + } + + LOGGER.warn("Executor {} do not support close or shutdown, it may block service shutdown.", + operationMeta.getExecutor().getClass().getName()); + } + } + + private void exportToFile(String fileName, String content) { + File file = new File(fileName); + if (!file.getParentFile().exists()) { + if (!file.getParentFile().mkdirs()) { + LOGGER.error("create file directory failed"); + return; + } + } + if (file.exists()) { + file.delete(); + } + try { + file.createNewFile(); + FileUtils.writeStringToFile(file, content, StandardCharsets.UTF_8, false); + file.setReadOnly(); + } catch (IOException e) { + LOGGER.error("export swagger content to file failed, message: {}", e.getMessage()); + } + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerMeta.java b/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerMeta.java new file mode 100644 index 00000000000..87a14817476 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerMeta.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.core.provider.producer; + +public class ProducerMeta { + private String schemaId; + + private Object instance; + + private Class schemaInterface; + + public ProducerMeta() { + } + + public ProducerMeta(String schemaId, Object instance) { + this.schemaId = schemaId; + this.instance = instance; + } + + public String getSchemaId() { + return schemaId; + } + + public void setSchemaId(String schemaId) { + this.schemaId = schemaId; + } + + public Object getInstance() { + return instance; + } + + public void setInstance(Object instance) { + this.instance = instance; + } + + public Class getSchemaInterface() { + return schemaInterface; + } + + public void setSchemaInterface(Class schemaInterface) { + this.schemaInterface = schemaInterface; + } +} 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 new file mode 100644 index 00000000000..391bc6a4723 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.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.provider.producer; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +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; +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.executor.ExecutorManager; +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 io.swagger.v3.oas.models.OpenAPI; + +public class ProducerProviderManager { + private static final Logger LOGGER = LoggerFactory.getLogger(ProducerProviderManager.class); + + private final List producerProviderList = new ArrayList<>( + SPIServiceUtils.getOrLoadSortedService(ProducerProvider.class)); + + private final SCBEngine scbEngine; + + private final List producerMetas = new ArrayList<>(); + + public ProducerProviderManager(SCBEngine scbEngine) { + this.scbEngine = scbEngine; + } + + public List getProducerProviderList() { + return producerProviderList; + } + + public void init() { + registerProducerMetas(producerMetas); + + for (ProducerProvider provider : producerProviderList) { + List producerMetas = provider.init(); + if (producerMetas == null) { + LOGGER.warn("ProducerProvider {} not provide any producer.", provider.getClass().getName()); + continue; + } + + registerProducerMetas(producerMetas); + } + } + + public void addProducerMeta(String schemaId, Object instance) { + addProducerMeta(new ProducerMeta(schemaId, instance)); + } + + public void addProducerMeta(ProducerMeta producerMeta) { + producerMetas.add(producerMeta); + } + + private void registerProducerMetas(List producerMetas) { + for (ProducerMeta producerMeta : producerMetas) { + registerSchema(producerMeta.getSchemaId(), producerMeta.getSchemaInterface(), producerMeta.getInstance()); + } + } + + public SchemaMeta registerSchema(String schemaId, Object instance) { + return registerSchema(schemaId, null, instance); + } + + public SchemaMeta registerSchema(String schemaId, Class schemaInterface, Object instance) { + MicroserviceMeta producerMicroserviceMeta = scbEngine.getProducerMicroserviceMeta(); + SwaggerProducer swaggerProducer = scbEngine.getSwaggerEnvironment() + .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); + for (SwaggerProducerOperation producerOperation : swaggerProducer.getAllOperations()) { + OperationMeta operationMeta = schemaMeta.ensureFindOperation(producerOperation.getOperationId()); + operationMeta.setSwaggerProducerOperation(producerOperation); + + if (CompletableFuture.class.equals(producerOperation.getProducerMethod().getReturnType())) { + operationMeta.setExecutor(scbEngine.getExecutorManager().findExecutor(operationMeta, reactiveExecutor)); + } + } + + return schemaMeta; + } + + // 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.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) && !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/discovery/EndpointDiscoveryFilter.java b/core/src/main/java/org/apache/servicecomb/core/registry/discovery/EndpointDiscoveryFilter.java new file mode 100644 index 00000000000..666626b0697 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/registry/discovery/EndpointDiscoveryFilter.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.core.registry.discovery; + +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.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; + } + + @Override + protected String findTransportName(DiscoveryContext context, DiscoveryTreeNode parent) { + Invocation invocation = context.getInputParameters(); + return invocation.getConfigTransportName(); + } + + @Override + protected Object createEndpoint(DiscoveryContext context, String transportName, String endpoint, + StatefulDiscoveryInstance instance) { + Transport transport = scbEngine.getTransportManager().findTransport(transportName); + if (transport == null) { + LOGGER.info("not deployed transport {}, ignore {}.", transportName, endpoint); + return null; + } + + return new Endpoint(transport, endpoint, instance); + } +} 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 new file mode 100644 index 00000000000..6f888c813e7 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/tracing/BraveTraceIdGenerator.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.core.tracing; + +import org.apache.servicecomb.core.CoreConst; + +import brave.internal.Platform; + +public class BraveTraceIdGenerator implements TraceIdGenerator { + @Override + public String getTraceIdKeyName() { + return CoreConst.TRACE_ID_NAME; + } + + @Override + public String generate() { + return Long.toHexString(Platform.get().nextTraceIdHigh()); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/tracing/ScbMarker.java b/core/src/main/java/org/apache/servicecomb/core/tracing/ScbMarker.java new file mode 100644 index 00000000000..a7d4bdaddc6 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/tracing/ScbMarker.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.core.tracing; + +import org.apache.servicecomb.foundation.common.log.AbstractMarker; + +public class ScbMarker extends AbstractMarker { + private static final long serialVersionUID = -1L; + + private static final String MARKER_NAME = "SERVICECOMB_MARKER"; + + public ScbMarker() { + + } + + @Override + public final String getName() { + return MARKER_NAME; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdGenerator.java b/core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdGenerator.java new file mode 100644 index 00000000000..7f807be7a3c --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdGenerator.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.core.tracing; + +public interface TraceIdGenerator { + default int getOrder() { + return 1000; + } + + /** + *
+   *   for generators have the same name, will only use the minimum order instance
+   *   not use getTraceIdKeyName to control this logic, because most customers not want to generate multiple traceIds
+   * 
+ * @return generator name + */ + default String getName() { + return "default"; + } + + /** + * + * @return trance id key name + *
+   * default value is X-B3-TraceId to work with zipkin
+   * 
+ */ + String getTraceIdKeyName(); + + String generate(); +} 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 new file mode 100644 index 00000000000..3819bb90218 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdLogger.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.core.tracing; + +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(); + + public static final String KEY_TRACE_ID = "SERVICECOMB_TRACE_ID"; + + private final Invocation invocation; + + public TraceIdLogger(Invocation invocation) { + this.invocation = invocation; + } + + public Invocation getInvocation() { + return invocation; + } + + public static String constructSource(String source) { + return "[" + source + "(" + + Thread.currentThread().getStackTrace()[2].getLineNumber() + ")]"; + } + + public final String getName() { + return invocation.getTraceId(); + } + + public void error(String format, Object... arguments) { + MDC.put(KEY_TRACE_ID, getName()); + LOGGER.error(MARKER, format, arguments); + MDC.remove(KEY_TRACE_ID); + } + + public void warn(String format, Object... arguments) { + MDC.put(KEY_TRACE_ID, getName()); + LOGGER.warn(MARKER, format, arguments); + MDC.remove(KEY_TRACE_ID); + } + + public void info(String format, Object... arguments) { + MDC.put(KEY_TRACE_ID, getName()); + LOGGER.info(MARKER, format, arguments); + MDC.remove(KEY_TRACE_ID); + } + + public void debug(String format, Object... arguments) { + MDC.put(KEY_TRACE_ID, getName()); + 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 new file mode 100644 index 00000000000..5c1ee3cc177 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/transport/AbstractTransport.java @@ -0,0 +1,191 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.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; + + protected Endpoint endpoint; + + protected Endpoint publishEndpoint; + + protected Environment environment; + + @Override + public Endpoint getPublishEndpoint() { + return publishEndpoint; + } + + @Override + 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); + } + + /* + * 将配置的URI转换为endpoint + * addressWithoutSchema 配置的URI,没有schema部分 + */ + protected void setListenAddressWithoutSchema(String addressWithoutSchema, + Map pairs) { + addressWithoutSchema = genAddressWithoutSchema(addressWithoutSchema, pairs); + + this.endpoint = new Endpoint(this, NetUtils.getRealListenAddress(getName(), addressWithoutSchema)); + if (this.endpoint.getEndpoint() != null) { + this.publishEndpoint = new Endpoint(this, getPublishAddress(getName(), + addressWithoutSchema)); + } else { + this.publishEndpoint = null; + } + } + + private String genAddressWithoutSchema(String addressWithoutSchema, Map pairs) { + if (addressWithoutSchema == null || pairs == null || pairs.isEmpty()) { + return addressWithoutSchema; + } + + int idx = addressWithoutSchema.indexOf('?'); + if (idx == -1) { + addressWithoutSchema += "?"; + } else { + addressWithoutSchema += "&"; + } + + String encodedQuery = URLEncodedUtils.format( + pairs.entrySet().stream().map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()), StandardCharsets.UTF_8.name()); + + addressWithoutSchema += encodedQuery; + + return addressWithoutSchema; + } + + @Override + public Object parseAddress(String address) { + if (address == null) { + return null; + } + 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 new file mode 100644 index 00000000000..64ed82a5c93 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/transport/TransportManager.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.core.transport; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +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); + + private final List transports = new ArrayList<>(SPIServiceUtils.getOrLoadSortedService(Transport.class)); + + private final Map transportMap = new HashMap<>(); + + public Map getTransportMap() { + return transportMap; + } + + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + public void clearTransportBeforeInit() { + transports.clear(); + } + + public void addTransportBeforeInit(Transport transport) { + this.transports.add(transport); + } + + public void addTransportsBeforeInit(List transports) { + this.transports.addAll(transports); + } + + public void init(SCBEngine scbEngine) throws Exception { + initEnvironment(); + buildTransportMap(); + + for (Transport transport : transportMap.values()) { + if (transport.init()) { + Endpoint endpoint = transport.getPublishEndpoint(); + if (endpoint != null && endpoint.getEndpoint() != null) { + LOGGER.info("endpoint to publish: {}", endpoint.getEndpoint()); + scbEngine.getRegistrationManager().addEndpoint(endpoint.getEndpoint()); + } + continue; + } + } + } + + private void initEnvironment() { + for (Transport transport : transports) { + transport.setEnvironment(environment); + } + } + + protected void buildTransportMap() { + Map> groups = groupByName(); + + for (Entry> entry : groups.entrySet()) { + List group = entry.getValue(); + + checkTransportGroup(group); + Transport transport = chooseOneTransport(group); + transportMap.put(transport.getName(), transport); + } + } + + protected Transport chooseOneTransport(List group) { + group.sort(Comparator.comparingInt(Transport::getOrder)); + + for (Transport transport : group) { + if (transport.canInit()) { + LOGGER.info("choose {} for {}.", transport.getClass().getName(), transport.getName()); + return transport; + } + } + + throw new ServiceCombException( + String.format("all transport named %s refused to init.", group.get(0).getName())); + } + + protected void checkTransportGroup(List group) { + // order value must be different, otherwise, maybe will choose a random transport + Map orderMap = new HashMap<>(); + for (Transport transport : group) { + Transport existTransport = orderMap.putIfAbsent(transport.getOrder(), transport); + if (existTransport != null) { + throw new ServiceCombException(String.format("%s and %s have the same order %d", + existTransport.getClass().getName(), + transport.getClass().getName(), + transport.getOrder())); + } + } + } + + protected Map> groupByName() { + Map> groups = new HashMap<>(); + for (Transport transport : transports) { + List list = groups.computeIfAbsent(transport.getName(), name -> new ArrayList<>()); + list.add(transport); + } + return groups; + } + + public Transport findTransport(String transportName) { + return transportMap.get(transportName); + } +} 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.exception.ExceptionConverter b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter new file mode 100644 index 00000000000..e170e610bf4 --- /dev/null +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter @@ -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. +# + +org.apache.servicecomb.core.exception.converter.ConstraintViolationExceptionConverter +org.apache.servicecomb.core.exception.converter.InvocationExceptionConverter +org.apache.servicecomb.core.exception.converter.IllegalArgumentExceptionConverter +org.apache.servicecomb.core.exception.converter.TimeoutExceptionConverter +org.apache.servicecomb.core.exception.converter.ConnectTimeoutExceptionConverter +org.apache.servicecomb.core.exception.converter.DefaultExceptionConverter +org.apache.servicecomb.core.exception.converter.ServiceCombExceptionConverter 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 new file mode 100644 index 00000000000..d21d7c1fe15 --- /dev/null +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider @@ -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.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 new file mode 100644 index 00000000000..8e61e1f2b81 --- /dev/null +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.tracing.TraceIdGenerator @@ -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.tracing.BraveTraceIdGenerator 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 new file mode 100644 index 00000000000..1356e33ecf8 --- /dev/null +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister @@ -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.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 new file mode 100644 index 00000000000..6eb082fc473 --- /dev/null +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory @@ -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.invocation.endpoint.EndpointMapperFactory 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 9f102a6eeba..00000000000 --- a/core/src/main/resources/META-INF/spring/cse.bean.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file 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/config/config.cse.inc.xml b/core/src/main/resources/config/config.cse.inc.xml deleted file mode 100644 index 4dedd85ae2b..00000000000 --- a/core/src/main/resources/config/config.cse.inc.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - classpath*:config/cse.properties - - \ No newline at end of file diff --git a/core/src/main/resources/config/cse.handler.xml b/core/src/main/resources/config/cse.handler.xml deleted file mode 100644 index 5a281603762..00000000000 --- a/core/src/main/resources/config/cse.handler.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - \ No newline at end of file diff --git a/core/src/main/resources/microservice.yaml b/core/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..f77c5ce5601 --- /dev/null +++ b/core/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb-config-order: -500 diff --git a/core/src/test/java/io/servicecomb/core/Config.java b/core/src/test/java/io/servicecomb/core/Config.java deleted file mode 100644 index 22719ecfa49..00000000000 --- a/core/src/test/java/io/servicecomb/core/Config.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -public class Config { - private static int clientThread; - - private static String transport; - - private static String mode; - - public static String getTransport() { - return transport; - } - - public static void setTransport(String transport) { - Config.transport = transport; - } - - public static int getClientThread() { - return clientThread; - } - - public static void setClientThread(int clientThread) { - Config.clientThread = clientThread; - } - - public static String getMode() { - return mode; - } - - public static void setMode(String mode) { - Config.mode = mode; - } -} diff --git a/core/src/test/java/io/servicecomb/core/TestConfig.java b/core/src/test/java/io/servicecomb/core/TestConfig.java deleted file mode 100644 index 49c1947644b..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestConfig.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.Response.StatusType; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.config.ConfigurationSpringInitializer; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.SwaggerInvocation; -import io.servicecomb.swagger.invocation.context.ContextUtils; -import io.servicecomb.swagger.invocation.context.HttpStatus; -import io.servicecomb.swagger.invocation.context.InvocationContext; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -public class TestConfig { - class MyConfigurationSpringInitializer extends ConfigurationSpringInitializer { - Properties p; - - MyConfigurationSpringInitializer(Properties p) { - this.p = p; - } - - public void test() throws Exception { - this.loadProperties(p); - } - } - - @Test - public void testConstants() { - Assert.assertEquals("x-cse-context", Const.CSE_CONTEXT); - Assert.assertEquals("rest", Const.RESTFUL); - Assert.assertEquals("", Const.ANY_TRANSPORT); - Assert.assertEquals("latest", Const.VERSION_RULE_LATEST); - Assert.assertEquals("latest", Const.DEFAULT_VERSION_RULE); - } - - @Test - public void testHttpResponse() { - String objectString = new String("Unit Testing"); - Response oResponse = Response.success(objectString, Status.OK); - - Assert.assertEquals(true, oResponse.isSuccessed()); - - oResponse = Response.succResp(objectString); - Assert.assertEquals(true, oResponse.isSuccessed()); - Assert.assertEquals(200, oResponse.getStatusCode()); - - Throwable oThrowable = new Throwable("Error"); - - oResponse = Response.consumerFailResp(oThrowable); - Assert.assertEquals(true, oResponse.isFailed()); - - oResponse = Response.providerFailResp(oThrowable); - Assert.assertEquals(true, oResponse.isFailed()); - } - - @Test - public void testHttpStatus() { - StatusType oStatus = new HttpStatus(204, "InternalServerError"); - Assert.assertEquals("InternalServerError", oStatus.getReasonPhrase()); - } - - @Test - public void testContextUtils() { - ThreadLocal contextMgr = new ThreadLocal<>(); - Assert.assertEquals(contextMgr.get(), ContextUtils.getInvocationContext()); - - SwaggerInvocation invocation = new SwaggerInvocation(); - invocation.addContext("test1", "testObject"); - - Assert.assertEquals("testObject", invocation.getContext("test1")); - - Map context = new HashMap<>(); - context.put("test2", new String("testObject")); - invocation.setContext(context); - Assert.assertEquals(context, invocation.getContext()); - - invocation.setStatus(Status.OK); - Assert.assertEquals(200, invocation.getStatus().getStatusCode()); - - invocation.setStatus(204); - Assert.assertEquals(204, invocation.getStatus().getStatusCode()); - - invocation.setStatus(Status.OK); - Assert.assertEquals((Status.OK).getStatusCode(), invocation.getStatus().getStatusCode()); - - invocation.setStatus(203, "Done"); - Assert.assertEquals(203, invocation.getStatus().getStatusCode()); - - ContextUtils.setInvocationContext(invocation); - Assert.assertEquals(invocation, ContextUtils.getInvocationContext()); - - ContextUtils.removeInvocationContext(); - Assert.assertEquals(null, ContextUtils.getInvocationContext()); - } - - @Test - public void testResponse() { - Response response = Response.create(400, "test", null); - InvocationException exception = response.getResult(); - Assert.assertEquals(null, exception.getErrorData()); - - response = Response.create(400, "test", "errorData"); - exception = response.getResult(); - Assert.assertEquals("errorData", exception.getErrorData()); - } - - @Test - public void testConfigurationSpringInitializer() throws Exception { - Properties p = new Properties(); - MyConfigurationSpringInitializer oConf = new MyConfigurationSpringInitializer(p); - oConf.setConfigId("testkey:testvalue,testkey2:testvalue2"); - boolean failed = false; - try { - oConf.test(); - } catch (Exception e) { - System.out.println(e.getMessage()); - Assert.assertEquals(e.getMessage().contains("can not find config for testkey:testvalue"), true); - failed = true; - } - Assert.assertEquals(failed, true); - } -} diff --git a/core/src/test/java/io/servicecomb/core/TestConsumer.java b/core/src/test/java/io/servicecomb/core/TestConsumer.java deleted file mode 100644 index 627efe18d2d..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestConsumer.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.provider.consumer.ConsumerProviderManager; -import io.servicecomb.core.provider.consumer.InvokerUtils; -import io.servicecomb.core.provider.consumer.SyncResponseExecutor; -import io.servicecomb.foundation.common.RegisterManager; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.Response; - -public class TestConsumer { - - @SuppressWarnings({"rawtypes", "unchecked"}) - @Test - public void testConsumerProviderManager() { - ConsumerProviderManager oConsumerProviderManager = new ConsumerProviderManager(); - RegisterManager oRegisterManager = new RegisterManager("cse consumer provider manager"); - oRegisterManager.register("cse.references.cse consumer provider manager", - "cse consumer provider manager"); - boolean validAssert = true; - try { - oConsumerProviderManager.getReferenceConfig("consumer provider manager"); - } catch (Exception e) { - Assert.assertNotEquals(null, e); - validAssert = false; - } catch (Throwable ee) { - Assert.assertNotEquals(null, ee); - validAssert = false; - } - Assert.assertFalse(validAssert); - } - - @Test - public void testReferenceConfig() throws InterruptedException { - Map oMap = new ConcurrentHashMap<>(); - oMap.put("test1", "value1"); - RegisterManager oManager = new RegisterManager<>("test"); - oManager.register("test1", "value1"); - - SyncResponseExecutor oExecutor = new SyncResponseExecutor(); - oExecutor.execute(new Runnable() { - - @Override - public void run() { - oExecutor.setResponse(Response.succResp("success")); - } - }); - Assert.assertEquals(true, oExecutor.waitResponse().isSuccessed()); - } - - @Test - public void testInvokerUtils() { - Invocation oInvocation = Mockito.mock(Invocation.class); - OperationMeta oOperationMeta = Mockito.mock(OperationMeta.class); - Mockito.when(oOperationMeta.isSync()).thenReturn(false); - Mockito.when(oInvocation.getOperationMeta()).thenReturn(oOperationMeta); - InvokerUtils.reactiveInvoke(oInvocation, Mockito.mock(AsyncResponse.class)); - boolean validReactiveInvoke = true; - try { - InvokerUtils.reactiveInvoke(null, null); - } catch (Exception e) { - Assert.assertEquals(java.lang.NullPointerException.class, e.getClass()); - validReactiveInvoke = false; - } - Assert.assertFalse(validReactiveInvoke); - boolean validInvokeIsNull = true; - try { - InvokerUtils.invoke(null); - } catch (Exception e) { - Assert.assertEquals(java.lang.NullPointerException.class, e.getClass()); - validInvokeIsNull = false; - } - Assert.assertFalse(validInvokeIsNull); - boolean validInvoke = true; - try { - InvokerUtils.invoke(oInvocation); - } catch (Exception e) { - Assert.assertEquals(java.lang.NullPointerException.class, e.getClass()); - validInvoke = false; - } - Assert.assertFalse(validInvoke); - } - - @Test - public void testInvocation() { - OperationMeta oOperationMeta = Mockito.mock(OperationMeta.class); - SchemaMeta oSchemaMeta = Mockito.mock(SchemaMeta.class); - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - List oHandlerList = new ArrayList<>(); - - Mockito.when(oSchemaMeta.getProviderHandlerChain()).thenReturn(oHandlerList); - Mockito.when(oSchemaMeta.getMicroserviceName()).thenReturn("TMK"); - Mockito.when(oOperationMeta.getSchemaMeta()).thenReturn(oSchemaMeta); - Endpoint oEndpoint = Mockito.mock(Endpoint.class); - Transport oTransport = Mockito.mock(Transport.class); - Mockito.when(oEndpoint.getTransport()).thenReturn(oTransport); - Mockito.when(oOperationMeta.getOperationId()).thenReturn("TMK"); - - Invocation oInvocation = new Invocation(oEndpoint, oOperationMeta, null); - Assert.assertNotNull(oInvocation.getTransport()); - Assert.assertNotNull(oInvocation.getInvocationType()); - oInvocation.setResponseExecutor(Mockito.mock(Executor.class)); - Assert.assertNotNull(oInvocation.getResponseExecutor()); - Assert.assertNotNull(oInvocation.getSchemaMeta()); - Assert.assertNotNull(oInvocation.getOperationMeta()); - Assert.assertNull(oInvocation.getArgs()); - Assert.assertNotNull(oInvocation.getEndpoint()); - oInvocation.setEndpoint(null); - Map map = oInvocation.getContext(); - Assert.assertNotNull(map); - String str = oInvocation.getSchemaId(); - Assert.assertEquals(null, str); - String str1 = oInvocation.getMicroserviceName(); - Assert.assertEquals("TMK", str1); - Map mapp = oInvocation.getHandlerContext(); - Assert.assertNotNull(mapp); - Assert.assertEquals(true, oInvocation.getHandlerIndex() >= 0); - oInvocation.setHandlerIndex(8); - Assert.assertEquals("TMK", oInvocation.getOperationName()); - Assert.assertEquals("TMK", oInvocation.getMicroserviceName()); - - boolean validAssert; - - try { - - validAssert = true; - - oInvocation.next(asyncResp); - } catch (Exception e) { - validAssert = false; - } - Assert.assertFalse(validAssert); - } -} diff --git a/core/src/test/java/io/servicecomb/core/TestCseApplicationListener.java b/core/src/test/java/io/servicecomb/core/TestCseApplicationListener.java deleted file mode 100644 index 328092103d0..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestCseApplicationListener.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Test; -import org.springframework.context.event.ContextClosedEvent; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.context.support.AbstractApplicationContext; - -import io.servicecomb.core.provider.consumer.ConsumerProviderManager; -import io.servicecomb.core.provider.producer.ProducerProviderManager; -import io.servicecomb.core.transport.TransportManager; -import io.servicecomb.foundation.common.utils.ReflectUtils; -import io.servicecomb.serviceregistry.RegistryUtils; -import mockit.Expectations; -import mockit.Injectable; -import mockit.Mocked; - -public class TestCseApplicationListener { - @Test - public void testCseApplicationListenerNormal(@Injectable ContextRefreshedEvent event, - @Injectable AbstractApplicationContext context, - @Injectable BootListener listener, - @Injectable ProducerProviderManager producerProviderManager, - @Injectable ConsumerProviderManager consumerProviderManager, - @Injectable TransportManager transportManager, - @Mocked RegistryUtils ru) throws Exception { - Map listeners = new HashMap<>(); - listeners.put("test", listener); - - new Expectations() { - { - context.getBeansOfType(BootListener.class); - result = listeners; - } - }; - - CseApplicationListener cal = new CseApplicationListener(); - cal.setInitEventClass(ContextRefreshedEvent.class); - cal.setApplicationContext(context); - ReflectUtils.setField(cal, "producerProviderManager", producerProviderManager); - ReflectUtils.setField(cal, "consumerProviderManager", consumerProviderManager); - ReflectUtils.setField(cal, "transportManager", transportManager); - - cal.onApplicationEvent(event); - } - - @Test - public void testCseApplicationListenerThrowException(@Injectable ContextRefreshedEvent event, - @Injectable AbstractApplicationContext context, - @Injectable BootListener listener, - @Injectable ProducerProviderManager producerProviderManager, - @Mocked RegistryUtils ru) throws Exception { - Map listeners = new HashMap<>(); - listeners.put("test", listener); - - new Expectations() { - { - context.getBeansOfType(BootListener.class); - result = listeners; - } - }; - CseApplicationListener cal = new CseApplicationListener(); - cal.setApplicationContext(context); - ReflectUtils.setField(cal, "producerProviderManager", producerProviderManager); - cal.onApplicationEvent(event); - } - - @Test - public void testCseApplicationListenerParentNotnull(@Injectable ContextRefreshedEvent event, - @Injectable AbstractApplicationContext context, - @Injectable AbstractApplicationContext pContext, - @Mocked RegistryUtils ru) throws Exception { - - CseApplicationListener cal = new CseApplicationListener(); - cal.setApplicationContext(context); - cal.onApplicationEvent(event); - } - - @Test - public void testCseApplicationListenerShutdown(@Injectable ContextClosedEvent event, - @Mocked RegistryUtils ru) throws Exception { - new Expectations() { - { - RegistryUtils.destory(); - } - }; - CseApplicationListener cal = new CseApplicationListener(); - cal.onApplicationEvent(event); - } -} diff --git a/core/src/test/java/io/servicecomb/core/TestDefinition.java b/core/src/test/java/io/servicecomb/core/TestDefinition.java deleted file mode 100644 index 8e3c3478998..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestDefinition.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.definition.CommonService; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.swagger.models.Info; -import io.swagger.models.Swagger; - -public class TestDefinition { - @Test - public void testCommonService() { - CommonService oCommonService = new CommonService<>(); - - boolean validCreateOperation = true; - try { - oCommonService.createOperationMgr("test1"); - } catch (Exception e) { - validCreateOperation = false; - } - Assert.assertTrue(validCreateOperation); - oCommonService.setName("test1"); - boolean validRegOperation = true; - try { - oCommonService.regOperation("oName1", "op1"); - } catch (Exception e) { - validRegOperation = false; - } - Assert.assertTrue(validRegOperation); - - Assert.assertEquals(1, oCommonService.getOperations().size()); - Assert.assertEquals("test1", oCommonService.getName()); - Assert.assertEquals("op1", oCommonService.findOperation("oName1")); - Assert.assertEquals("op1", oCommonService.ensureFindOperation("oName1")); - } - - @Test - public void testMicroServiceMeta() { - MicroserviceMeta oMicroMeta = new MicroserviceMeta("app:micro1"); - Assert.assertEquals(0, oMicroMeta.getSchemaMetas().size()); - Assert.assertEquals(0, oMicroMeta.getOperations().size()); - Assert.assertEquals("micro1", oMicroMeta.getShortName()); - Assert.assertEquals("app:micro1", oMicroMeta.getName()); - try { - oMicroMeta.putExtData("key1", new String("value1")); - Assert.assertNotEquals(null, oMicroMeta.getExtData("key1")); - } catch (Exception e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testMicroserviceMetaManager() throws Exception { - MicroserviceMetaManager microserviceMetaManager = new MicroserviceMetaManager(); - microserviceMetaManager.getOrCreateMicroserviceMeta("app:testname"); - - Assert.assertEquals("microservice meta manager", microserviceMetaManager.getName()); - Assert.assertEquals("Not allow regsiter repeat data, name=%s, key=%s", - microserviceMetaManager.getRegisterErrorFmt()); - Assert.assertEquals(0, microserviceMetaManager.getAllSchemaMeta("app:testname").size()); - - Swagger oSwagger = new Swagger(); - Info oInfo = new Info(); - oInfo.setVendorExtension("x-java-interface", "java.lang.String"); - oSwagger.setInfo(oInfo); - Assert.assertEquals("java.lang.String", (ClassUtils.getJavaInterface(oSwagger)).getName()); - oInfo.setVendorExtension("x-java-class", "java.lang.String"); - } -} diff --git a/core/src/test/java/io/servicecomb/core/TestEndpoint.java b/core/src/test/java/io/servicecomb/core/TestEndpoint.java deleted file mode 100644 index 99049b69ef3..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestEndpoint.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import org.junit.Assert; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Mocked; - -public class TestEndpoint { - @Test - public void testEndpoint(@Mocked Transport transport) { - new Expectations() { - { - transport.parseAddress("rest://123.6.6.6:8080"); - result = "rest://123.6.6.6:8080"; - } - }; - Endpoint endpoint = new Endpoint(transport, "rest://123.6.6.6:8080"); - Assert.assertEquals(endpoint.getAddress(), "rest://123.6.6.6:8080"); - Assert.assertEquals(endpoint.getEndpoint(), "rest://123.6.6.6:8080"); - Assert.assertEquals(endpoint.getTransport(), transport); - Assert.assertEquals(endpoint.toString(), "rest://123.6.6.6:8080"); - } -} diff --git a/core/src/test/java/io/servicecomb/core/TestException.java b/core/src/test/java/io/servicecomb/core/TestException.java deleted file mode 100644 index 6b26a95235c..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestException.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.exception.CseException; -import io.servicecomb.core.exception.ExceptionUtils; - -public class TestException { - @Test - public void testCseException() { - CseException oExeception = new CseException("500", "InternalServerError"); - Assert.assertEquals("500", oExeception.getCode()); - Assert.assertEquals("ServiceDefinitionException Code:500, Message:InternalServerError", - oExeception.toString()); - - oExeception = new CseException("503", "OwnException", new Throwable()); - Assert.assertEquals("503", oExeception.getCode()); - } - - @Test - public void testExceptionUtils() { - CseException oExeception = ExceptionUtils.createCseException("cse.handler.ref.not.exist", new String("test")); - Assert.assertEquals("cse.handler.ref.not.exist", oExeception.getCode()); - - oExeception = - ExceptionUtils.createCseException("cse.handler.ref.not.exist", new Throwable(), new String("test")); - Assert.assertEquals("cse.handler.ref.not.exist", oExeception.getCode()); - - oExeception = ExceptionUtils.producerOperationNotExist("cse.error", "unit-testing"); - Assert.assertEquals("cse.producer.operation.not.exist", oExeception.getCode()); - - oExeception = ExceptionUtils.operationIdInvalid("cse.double.error", "what path are you talking about"); - Assert.assertEquals("cse.schema.operation.id.invalid", oExeception.getCode()); - - oExeception = ExceptionUtils.handlerRefNotExist("cse.double.error"); - Assert.assertEquals("cse.handler.ref.not.exist", oExeception.getCode()); - - oExeception = ExceptionUtils.lbAddressNotFound("microServiceName", "my rule my world", "transportChannel"); - Assert.assertEquals("cse.lb.no.available.address", oExeception.getCode()); - } -} diff --git a/core/src/test/java/io/servicecomb/core/TestExecutors.java b/core/src/test/java/io/servicecomb/core/TestExecutors.java deleted file mode 100644 index ff100374b53..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestExecutors.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executor; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.executor.FixedThreadExecutor; -import io.servicecomb.core.executor.ReactiveExecutor; -import mockit.Deencapsulation; - -public class TestExecutors { - - String strThreadTest = "default"; - - @Test - public void testFixedThreadExecutor() { - FixedThreadExecutor oFixedThreadExecutor = new FixedThreadExecutor(); - oFixedThreadExecutor.execute(new Runnable() { - - @Override - public void run() { - - } - }); - Map threadExectorMap = Deencapsulation.getField(oFixedThreadExecutor, "threadExectorMap"); - Assert.assertEquals(true, (threadExectorMap.size() > 0)); - - List executorList = Deencapsulation.getField(oFixedThreadExecutor, "executorList"); - Assert.assertEquals(true, (executorList.size() > 1)); - - ReactiveExecutor oReactiveExecutor = new ReactiveExecutor(); - oReactiveExecutor.execute(new Runnable() { - - @Override - public void run() { - strThreadTest = "thread Ran"; - } - }); - Assert.assertEquals("thread Ran", strThreadTest); - } -} diff --git a/core/src/test/java/io/servicecomb/core/TestHandler.java b/core/src/test/java/io/servicecomb/core/TestHandler.java deleted file mode 100644 index 158dc72e6b3..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestHandler.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.handler.ConsumerHandlerManager; -import io.servicecomb.core.handler.HandlerConfigUtils; - -public class TestHandler { - - @Test - public void testAbstractHandlerManager() throws Exception { - HandlerConfigUtils.init(); - Assert.assertNotEquals(null, ConsumerHandlerManager.INSTANCE.getOrCreate("test")); - } -} diff --git a/core/src/test/java/io/servicecomb/core/TestInvocationFactory.java b/core/src/test/java/io/servicecomb/core/TestInvocationFactory.java deleted file mode 100644 index 7f5967d6e47..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestInvocationFactory.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.invocation.InvocationFactory; -import io.servicecomb.core.provider.consumer.ReferenceConfig; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.registry.ServiceRegistryFactory; -import mockit.Injectable; - -public class TestInvocationFactory { - @BeforeClass - public static void setUp() { - ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal(); - serviceRegistry.init(); - RegistryUtils.setServiceRegistry(serviceRegistry); - } - - @Test - public void testInvocationFactoryforConsumer(@Injectable ReferenceConfig referenceConfig, - @Injectable OperationMeta operationMeta) { - Invocation invocation = - InvocationFactory.forConsumer(referenceConfig, operationMeta, new String[] {"a", "b"}); - Assert.assertEquals("perfClient", invocation.getContext(Const.SRC_MICROSERVICE)); - } - - @Test - public void testInvocationFactoryforConsumer(@Injectable ReferenceConfig referenceConfig, - @Injectable SchemaMeta schemaMeta) { - Invocation invocation = - InvocationFactory.forConsumer(referenceConfig, schemaMeta, "test", new String[] {"a", "b"}); - Assert.assertEquals("perfClient", invocation.getContext(Const.SRC_MICROSERVICE)); - } - - @Test - public void testInvocationFactoryforConsumer(@Injectable ReferenceConfig referenceConfig) { - Invocation invocation = - InvocationFactory.forConsumer(referenceConfig, "test", new String[] {"a", "b"}); - Assert.assertEquals("perfClient", invocation.getContext(Const.SRC_MICROSERVICE)); - } - - @Test - public void testInvocationFactoryforProvider(@Injectable Endpoint endpoint, - @Injectable OperationMeta operationMeta) { - Invocation invocation = - InvocationFactory.forProvider(endpoint, operationMeta, new String[] {"a", "b"}); - Assert.assertEquals(invocation.getEndpoint(), endpoint); - } -} diff --git a/core/src/test/java/io/servicecomb/core/TestTransport.java b/core/src/test/java/io/servicecomb/core/TestTransport.java deleted file mode 100644 index 4ef2fa4949b..00000000000 --- a/core/src/test/java/io/servicecomb/core/TestTransport.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.endpoint.EndpointsCache; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.swagger.invocation.AsyncResponse; -import mockit.Mocked; - -public class TestTransport { - @Test - public void testEndpoint() throws Exception { - Endpoint oEndpoint = new Endpoint(new Transport() { - - @Override - public void send(Invocation invocation, AsyncResponse asyncResp) throws Exception { - } - - @Override - public Object parseAddress(String address) { - return "127.0.0.1"; - } - - @Override - public boolean init() throws Exception { - return true; - } - - @Override - public String getName() { - return "test"; - } - - @Override - public Endpoint getEndpoint() throws Exception { - return (new Endpoint(this, "testEndpoint")); - } - - @Override - public Endpoint getPublishEndpoint() throws Exception { - return (new Endpoint(this, "testEndpoint")); - } - }, "rest://127.0.0.1:8080"); - oEndpoint.getTransport().init(); - Assert.assertEquals("rest://127.0.0.1:8080", oEndpoint.getEndpoint()); - Assert.assertEquals("127.0.0.1", oEndpoint.getAddress()); - Assert.assertEquals("test", oEndpoint.getTransport().getName()); - Assert.assertEquals("rest://127.0.0.1:8080", oEndpoint.getEndpoint().toString()); - } - - @Test - public void testAbstractTransport(@Mocked Microservice microservice) throws Exception { - - EndpointsCache oEndpointsCache = new EndpointsCache("app", "testname", "test", "test"); - - try { - List endpoionts = oEndpointsCache.getLatestEndpoints(); - Assert.assertEquals(endpoionts.size(), 0); - } catch (Exception e) { - Assert.assertEquals(null, e.getMessage()); - } - } -} diff --git a/core/src/test/java/io/servicecomb/core/Utils.java b/core/src/test/java/io/servicecomb/core/Utils.java deleted file mode 100644 index 09cc966e63a..00000000000 --- a/core/src/test/java/io/servicecomb/core/Utils.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core; - -import java.lang.reflect.Method; - -import org.springframework.util.ReflectionUtils; - -import com.netflix.config.DynamicProperty; - -public class Utils { - private static Method updatePropertyMethod = - ReflectionUtils.findMethod(DynamicProperty.class, "updateProperty", String.class, Object.class); - - static { - updatePropertyMethod.setAccessible(true); - } - - public static void updateProperty(String key, Object value) { - ReflectionUtils.invokeMethod(updatePropertyMethod, null, key, value); - } -} diff --git a/core/src/test/java/io/servicecomb/core/consumer/TestReactiveResponseExecutor.java b/core/src/test/java/io/servicecomb/core/consumer/TestReactiveResponseExecutor.java deleted file mode 100644 index 90eb5c1dbf7..00000000000 --- a/core/src/test/java/io/servicecomb/core/consumer/TestReactiveResponseExecutor.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.consumer; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.provider.consumer.ReactiveResponseExecutor; - -public class TestReactiveResponseExecutor { - @Test - public void testReactiveResponseExecutor() { - ReactiveResponseExecutor executor = new ReactiveResponseExecutor(); - Runnable cmd = Mockito.mock(Runnable.class); - boolean validAssert = true; - try { - executor.execute(cmd); - } catch (Exception e) { - validAssert = false; - } - Assert.assertTrue(validAssert); - } -} diff --git a/core/src/test/java/io/servicecomb/core/consumer/TestSyncResponseExecutor.java b/core/src/test/java/io/servicecomb/core/consumer/TestSyncResponseExecutor.java deleted file mode 100644 index 2b6493a2b80..00000000000 --- a/core/src/test/java/io/servicecomb/core/consumer/TestSyncResponseExecutor.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.consumer; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.provider.consumer.SyncResponseExecutor; -import io.servicecomb.swagger.invocation.Response; - -public class TestSyncResponseExecutor { - @Test - public void testSyncResponseExecutor() { - SyncResponseExecutor executor = new SyncResponseExecutor(); - Runnable cmd = Mockito.mock(Runnable.class); - Response response = Mockito.mock(Response.class); - executor.execute(cmd); - executor.setResponse(response); - - try { - Response responseValue = executor.waitResponse(); - Assert.assertNotNull(responseValue); - } catch (Exception e) { - Assert.assertNotNull(e); - } - } -} diff --git a/core/src/test/java/io/servicecomb/core/definition/MicroServicePropertyExtendedStub.java b/core/src/test/java/io/servicecomb/core/definition/MicroServicePropertyExtendedStub.java deleted file mode 100644 index 0ae8d25a028..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/MicroServicePropertyExtendedStub.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition; - -import java.util.Map; - -import io.servicecomb.serviceregistry.api.PropertyExtended; - -public class MicroServicePropertyExtendedStub implements PropertyExtended { - @Override - public Map getExtendedProperties() { - return null; - } -} diff --git a/core/src/test/java/io/servicecomb/core/definition/TestMicroserviceMeta.java b/core/src/test/java/io/servicecomb/core/definition/TestMicroserviceMeta.java deleted file mode 100644 index 7518c297821..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/TestMicroserviceMeta.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition; - -import java.util.Collection; - -import org.junit.Assert; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Mocked; - -public class TestMicroserviceMeta { - MicroserviceMeta microservicemeta = new MicroserviceMeta("app:microservice"); - - @Test - public void testGetSchemaMetas() { - Collection schemaMetas = microservicemeta.getSchemaMetas(); - Assert.assertNotNull(schemaMetas); - } - - @Test - public void testGetExtData() { - Object data = new Object(); - microservicemeta.putExtData("pruthi", data); - Object response = microservicemeta.getExtData("pruthi"); - Assert.assertNotNull(response); - } - - @Test - public void testIntf(@Mocked SchemaMeta sm1, @Mocked SchemaMeta sm2) { - Class intf = Object.class; - new Expectations() { - { - sm1.getSchemaId(); - result = "a"; - sm2.getSchemaId(); - result = "b"; - sm1.getSwaggerIntf(); - result = intf; - sm2.getSwaggerIntf(); - result = intf; - } - }; - - try { - microservicemeta.ensureFindSchemaMeta(intf); - Assert.assertEquals(1, 2); - } catch (Throwable e) { - Assert.assertEquals( - "No schema interface is java.lang.Object.", - e.getMessage()); - } - microservicemeta.regSchemaMeta(sm1); - Assert.assertEquals(sm1, microservicemeta.findSchemaMeta(intf)); - Assert.assertEquals(sm1, microservicemeta.ensureFindSchemaMeta(intf)); - - microservicemeta.regSchemaMeta(sm2); - Assert.assertEquals(sm1, microservicemeta.ensureFindSchemaMeta("a")); - Assert.assertEquals(sm2, microservicemeta.ensureFindSchemaMeta("b")); - try { - microservicemeta.findSchemaMeta(intf); - Assert.assertEquals(1, 2); - } catch (Throwable e) { - Assert.assertEquals( - "More than one schema interface is java.lang.Object, please use schemaId to choose a schema.", - e.getMessage()); - } - } -} diff --git a/core/src/test/java/io/servicecomb/core/definition/TestMicroserviceMetaManager.java b/core/src/test/java/io/servicecomb/core/definition/TestMicroserviceMetaManager.java deleted file mode 100644 index d24903753a7..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/TestMicroserviceMetaManager.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -public class TestMicroserviceMetaManager { - - @Test - public void testEnsureFindSchemaMeta() { - SchemaMeta meta = Mockito.mock(SchemaMeta.class); - MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); - Mockito.when(microserviceMeta.ensureFindSchemaMeta("yhfghj")).thenReturn(meta); - Assert.assertEquals(meta, microserviceMeta.ensureFindSchemaMeta("yhfghj")); - } -} diff --git a/core/src/test/java/io/servicecomb/core/definition/TestOperationMeta.java b/core/src/test/java/io/servicecomb/core/definition/TestOperationMeta.java deleted file mode 100644 index 05af123a449..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/TestOperationMeta.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.unittest.UnitTestMeta; -import io.servicecomb.swagger.extend.annotations.ResponseHeaders; -import io.servicecomb.swagger.invocation.response.ResponseMeta; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ResponseHeader; - -public class TestOperationMeta { - class Impl { - @ApiResponse( - code = 300, - response = String.class, - message = "", - responseHeaders = {@ResponseHeader(name = "h3", response = int.class)}) - @ResponseHeaders({@ResponseHeader(name = "h1", response = int.class), - @ResponseHeader(name = "h2", response = String.class, responseContainer = "List")}) - public int test(int x) { - return 100; - } - } - - @Test - public void testOperationMeta() { - UnitTestMeta meta = new UnitTestMeta(); - SchemaMeta schemaMeta = meta.getOrCreateSchemaMeta(Impl.class); - OperationMeta operationMeta = schemaMeta.findOperation("test"); - - Assert.assertEquals("POST", operationMeta.getHttpMethod()); - Assert.assertEquals("/test", operationMeta.getOperationPath()); - Assert.assertEquals(schemaMeta, operationMeta.getSchemaMeta()); - Assert.assertEquals("io.servicecomb.core.definition.TestOperationMeta$Impl.test", - operationMeta.getSchemaQualifiedName()); - Assert.assertEquals("app:test.io.servicecomb.core.definition.TestOperationMeta$Impl.test", - operationMeta.getMicroserviceQualifiedName()); - Assert.assertEquals("app:test", operationMeta.getMicroserviceName()); - Assert.assertEquals("test", operationMeta.getOperationId()); - Assert.assertEquals("x", operationMeta.getParamName(0)); - Assert.assertEquals(true, operationMeta.isSync()); - - operationMeta.putExtData("ext", 1); - Assert.assertEquals(1, (int) operationMeta.getExtData("ext")); - - ResponseMeta responseMeta = operationMeta.findResponseMeta(200); - Assert.assertEquals("Ljava/lang/Integer;", responseMeta.getJavaType().getGenericSignature()); - Assert.assertEquals("Ljava/lang/Integer;", responseMeta.getHeaders().get("h1").getGenericSignature()); - Assert.assertEquals("Ljava/util/List;", - responseMeta.getHeaders().get("h2").getGenericSignature()); - Assert.assertEquals(null, responseMeta.getHeaders().get("h3")); - - responseMeta = operationMeta.findResponseMeta(300); - Assert.assertEquals("Ljava/lang/String;", responseMeta.getJavaType().getGenericSignature()); - Assert.assertEquals("Ljava/lang/Integer;", responseMeta.getHeaders().get("h1").getGenericSignature()); - Assert.assertEquals("Ljava/util/List;", - responseMeta.getHeaders().get("h2").getGenericSignature()); - Assert.assertEquals("Ljava/lang/Integer;", responseMeta.getHeaders().get("h3").getGenericSignature()); - } -} diff --git a/core/src/test/java/io/servicecomb/core/definition/TestSchemaMeta.java b/core/src/test/java/io/servicecomb/core/definition/TestSchemaMeta.java deleted file mode 100644 index 3993d849027..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/TestSchemaMeta.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.swagger.generator.core.SwaggerConst; -import io.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; -import io.swagger.models.Swagger; -import mockit.Mocked; - -public class TestSchemaMeta { - static interface V1 { - void a(); - } - - static interface V2 extends V1 { - void b(); - } - - @Test - public void testMethodNotExist(@Mocked OperationMeta operationMeta) { - Swagger swagger = UnitTestSwaggerUtils.generateSwagger(V2.class).getSwagger(); - // make swagger have more operations than interface - swagger.getInfo().setVendorExtension(SwaggerConst.EXT_JAVA_INTF, V1.class.getName()); - - MicroserviceMeta microserviceMeta = new MicroserviceMeta("app:ms"); - SchemaMeta schemaMeta = new SchemaMeta(swagger, microserviceMeta, "schemaId"); - Assert.assertEquals(1, schemaMeta.getOperations().size()); - Assert.assertNotNull(schemaMeta.findOperation("a")); - } -} diff --git a/core/src/test/java/io/servicecomb/core/definition/TestSchemaUtils.java b/core/src/test/java/io/servicecomb/core/definition/TestSchemaUtils.java deleted file mode 100644 index d365bedfac8..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/TestSchemaUtils.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition; - -import org.junit.Assert; -import org.junit.Test; - -public class TestSchemaUtils { - @Test - public void testSchemaUtils() { - MicroserviceMeta microserviceMeta = new MicroserviceMeta("app:ms"); - Assert.assertEquals("cse.gen.app.ms.schemaId", SchemaUtils.generatePackageName(microserviceMeta, "schemaId")); - } -} diff --git a/core/src/test/java/io/servicecomb/core/definition/loader/TestDynamicSchemaLoader.java b/core/src/test/java/io/servicecomb/core/definition/loader/TestDynamicSchemaLoader.java deleted file mode 100644 index c4a952ad186..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/loader/TestDynamicSchemaLoader.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.core.definition.loader; - -import java.util.ArrayList; -import java.util.Collections; - -import org.hamcrest.Matchers; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -import io.servicecomb.core.CseContext; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.unittest.UnitTestMeta; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.registry.ServiceRegistryFactory; - -public class TestDynamicSchemaLoader { - private static MicroserviceMetaManager microserviceMetaManager = new MicroserviceMetaManager(); - - private static SchemaLoader loader = new SchemaLoader(); - - private static Microservice microservice; - - @BeforeClass - public static void init() { - UnitTestMeta.init(); - - loader.setMicroserviceMetaManager(microserviceMetaManager); - - SchemaListenerManager schemaListenerManager = new SchemaListenerManager(); - schemaListenerManager.setSchemaListenerList(Collections.emptyList()); - - CseContext context = CseContext.getInstance(); - context.setSchemaLoader(loader); - context.setSchemaListenerManager(schemaListenerManager); - - ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal(); - serviceRegistry.init(); - - microservice = serviceRegistry.getMicroservice(); - RegistryUtils.setServiceRegistry(serviceRegistry); - } - - @AfterClass - public static void teardown() { - RegistryUtils.setServiceRegistry(null); - } - - @Test - public void testRegisterSchemas() { - DynamicSchemaLoader.INSTANCE.registerSchemas("classpath*:test/test/schema.yaml"); - SchemaMeta schemaMeta = microserviceMetaManager.ensureFindSchemaMeta("perfClient", "schema"); - Assert.assertEquals("cse.gen.pojotest.perfClient.schema", schemaMeta.getPackageName()); - } - - @Test - public void testRegisterShemasAcrossApp() { - DynamicSchemaLoader.INSTANCE.registerSchemas("CSE:as", "classpath*:test/test/schema.yaml"); - SchemaMeta schemaMeta = microserviceMetaManager.ensureFindSchemaMeta("CSE:as", "schema"); - Assert.assertEquals("cse.gen.CSE.as.schema", schemaMeta.getPackageName()); - } - - @Test - public void testPutSelfBasePathIfAbsent_noUrlPrefix() { - System.clearProperty(Const.URL_PREFIX); - microservice.setPaths(new ArrayList<>()); - - loader.putSelfBasePathIfAbsent("perfClient", "/test"); - - Assert.assertThat(microservice.getPaths().size(), Matchers.is(1)); - Assert.assertThat(microservice.getPaths().get(0).getPath(), Matchers.is("/test")); - } - - @Test - public void testPutSelfBasePathIfAbsent_WithUrlPrefix() { - System.setProperty(Const.URL_PREFIX, "/root/rest"); - microservice.setPaths(new ArrayList<>()); - - loader.putSelfBasePathIfAbsent("perfClient", "/test"); - - Assert.assertThat(microservice.getPaths().size(), Matchers.is(1)); - Assert.assertThat(microservice.getPaths().get(0).getPath(), Matchers.is("/root/rest/test")); - - System.clearProperty(Const.URL_PREFIX); - } - - @Test - public void testPutSelfBasePathIfAbsent_WithUrlPrefix_StartWithUrlPrefix() { - System.setProperty(Const.URL_PREFIX, "/root/rest"); - microservice.setPaths(new ArrayList<>()); - - loader.putSelfBasePathIfAbsent("perfClient", "/root/rest/test"); - - Assert.assertThat(microservice.getPaths().size(), Matchers.is(1)); - Assert.assertThat(microservice.getPaths().get(0).getPath(), Matchers.is("/root/rest/test")); - - System.clearProperty(Const.URL_PREFIX); - } -} diff --git a/core/src/test/java/io/servicecomb/core/definition/loader/TestSchemaListenerManager.java b/core/src/test/java/io/servicecomb/core/definition/loader/TestSchemaListenerManager.java deleted file mode 100644 index 66935f1aa32..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/loader/TestSchemaListenerManager.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.definition.loader; - -import java.util.Arrays; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.SchemaMeta; - -public class TestSchemaListenerManager { - - SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); - - @Before - public void setUp() { - Mockito.when(schemaMeta.getSchemaId()).thenReturn("test"); - } - - @Test - public void testInitializationListener() { - SchemaListener listener = new SchemaListener() { - @Override - public void onSchemaLoaded(SchemaMeta... schemaMetas) { - Assert.assertEquals(1, schemaMetas.length); - Assert.assertEquals("test", schemaMetas[0].getSchemaId()); - } - }; - - SchemaListenerManager mgr = new SchemaListenerManager(); - mgr.setSchemaListenerList(Arrays.asList(listener)); - mgr.setMicroserviceMetaManager(new MicroserviceMetaManager()); - - mgr.notifySchemaListener(schemaMeta); - } -} diff --git a/core/src/test/java/io/servicecomb/core/definition/schema/TestConsumerSchemaFactory.java b/core/src/test/java/io/servicecomb/core/definition/schema/TestConsumerSchemaFactory.java deleted file mode 100644 index 38e26fb5828..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/schema/TestConsumerSchemaFactory.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.core.definition.schema; - -import java.util.Arrays; - -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.definition.loader.SchemaListener; -import io.servicecomb.core.definition.loader.SchemaListenerManager; -import io.servicecomb.core.definition.loader.SchemaLoader; -import io.servicecomb.core.unittest.UnitTestMeta; -import io.servicecomb.foundation.common.utils.ReflectUtils; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.servicecomb.swagger.generator.core.CompositeSwaggerGeneratorContext; -import io.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; -import mockit.Deencapsulation; - -public class TestConsumerSchemaFactory { - private static ConsumerSchemaFactory consumerSchemaFactory = new ConsumerSchemaFactory(); - - private static ServiceRegistryClient registryClient = Mockito.mock(ServiceRegistryClient.class); - - private static ServiceRegistry serviceRegistry = Mockito.mock(ServiceRegistry.class); - - private static SchemaListener schemaListener = new SchemaListener() { - - @Override - public void onSchemaLoaded(SchemaMeta... schemaMetas) { - - } - }; - - static interface Intf { - int add(int x, int y); - } - - class TestConsumerSchemaFactoryImpl { - public int add(int x, int y) { - return x + y; - } - } - - @BeforeClass - public static void init() { - Deencapsulation.setField(RegistryUtils.class, "serviceRegistry", serviceRegistry); - Mockito.when(serviceRegistry.getServiceRegistryClient()).thenReturn(registryClient); - - SchemaListenerManager schemaListenerManager = new SchemaListenerManager(); - schemaListenerManager.setSchemaListenerList(Arrays.asList(schemaListener)); - - MicroserviceMetaManager microserviceMetaManager = new MicroserviceMetaManager(); - SchemaLoader schemaLoader = new SchemaLoader() { - @Override - public void putSelfBasePathIfAbsent(String microserviceName, String basePath) { - } - }; - CompositeSwaggerGeneratorContext compositeSwaggerGeneratorContext = new CompositeSwaggerGeneratorContext(); - - ReflectUtils.setField(consumerSchemaFactory, "schemaListenerManager", schemaListenerManager); - ReflectUtils.setField(consumerSchemaFactory, "microserviceMetaManager", microserviceMetaManager); - ReflectUtils.setField(consumerSchemaFactory, "schemaLoader", schemaLoader); - ReflectUtils.setField(consumerSchemaFactory, - "compositeSwaggerGeneratorContext", - compositeSwaggerGeneratorContext); - - SchemaMeta schemaMeta = new UnitTestMeta().getOrCreateSchemaMeta(TestConsumerSchemaFactoryImpl.class); - String content = UnitTestSwaggerUtils.pretty(schemaMeta.getSwagger()); - - Mockito.when(registryClient.getMicroserviceId("app", "ms", "latest")).thenReturn("0"); - Mockito.when(registryClient.getSchema("0", "schema")).thenReturn(content); - - Microservice microservice = new Microservice(); - microservice.setAppId("app"); - microservice.setServiceId("0"); - microservice.setServiceName("ms"); - microservice.addSchema("schema", content); - Mockito.when(registryClient.getMicroservice("0")).thenReturn(microservice); - } - - @AfterClass - public static void teardown() { - Deencapsulation.setField(RegistryUtils.class, "serviceRegistry", null); - } - - @Test - public void testGetOrCreateConsumer() { - MicroserviceMeta microserviceMeta = - consumerSchemaFactory.getOrCreateMicroserviceMeta("app:ms", "latest"); - OperationMeta operationMeta = microserviceMeta.ensureFindOperation("schema.add"); - Assert.assertEquals("add", operationMeta.getOperationId()); - } -} diff --git a/core/src/test/java/io/servicecomb/core/definition/schema/TestProducerSchemaFactory.java b/core/src/test/java/io/servicecomb/core/definition/schema/TestProducerSchemaFactory.java deleted file mode 100644 index 3f8a09334d0..00000000000 --- a/core/src/test/java/io/servicecomb/core/definition/schema/TestProducerSchemaFactory.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.core.definition.schema; - -import javax.xml.ws.Holder; - -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.Mockito; -import org.springframework.context.ApplicationContext; - -import io.servicecomb.core.Const; -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.definition.loader.SchemaLoader; -import io.servicecomb.core.unittest.UnitTestMeta; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.ReflectUtils; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.registry.ServiceRegistryFactory; -import io.servicecomb.swagger.engine.SwaggerEnvironment; -import io.servicecomb.swagger.engine.SwaggerProducerOperation; -import io.servicecomb.swagger.engine.bootstrap.BootstrapNormal; -import io.servicecomb.swagger.generator.core.CompositeSwaggerGeneratorContext; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapperFactory; -import io.servicecomb.swagger.invocation.converter.ConverterMgr; -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.servicecomb.swagger.invocation.exception.InvocationException; -import io.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; - -public class TestProducerSchemaFactory { - private static SwaggerEnvironment swaggerEnv = new BootstrapNormal().boot(); - - private static ProducerSchemaFactory producerSchemaFactory = new ProducerSchemaFactory(); - - private static SchemaMeta schemaMeta; - - public static class TestProducerSchemaFactoryImpl { - public int add(int x, int y) { - return x + y; - } - } - - @BeforeClass - public static void init() { - ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal(); - RegistryUtils.setServiceRegistry(serviceRegistry); - - ConverterMgr converterMgr = new ConverterMgr(); - ProducerResponseMapperFactory responseMapperFactory = new ProducerResponseMapperFactory(); - responseMapperFactory.setConverterMgr(converterMgr); - - ProducerArgumentsMapperFactory producerArgsMapperFactory = new ProducerArgumentsMapperFactory(); - producerArgsMapperFactory.setConverterMgr(converterMgr); - - MicroserviceMetaManager microserviceMetaManager = new MicroserviceMetaManager(); - SchemaLoader schemaLoader = new SchemaLoader() { - @Override - public void putSelfBasePathIfAbsent(String microserviceName, String basePath) { - } - }; - CompositeSwaggerGeneratorContext compositeSwaggerGeneratorContext = new CompositeSwaggerGeneratorContext(); - - producerSchemaFactory.setSwaggerEnv(swaggerEnv); - ReflectUtils.setField(producerSchemaFactory, "microserviceMetaManager", microserviceMetaManager); - ReflectUtils.setField(producerSchemaFactory, "schemaLoader", schemaLoader); - ReflectUtils.setField(producerSchemaFactory, - "compositeSwaggerGeneratorContext", - compositeSwaggerGeneratorContext); - - BeanUtils.setContext(Mockito.mock(ApplicationContext.class)); - - UnitTestMeta.init(); - - schemaMeta = producerSchemaFactory.getOrCreateProducerSchema("app:ms", - "schema", - TestProducerSchemaFactoryImpl.class, - new TestProducerSchemaFactoryImpl()); - } - - @AfterClass - public static void teardown() { - RegistryUtils.setServiceRegistry(null); - } - - @Test - public void testGetOrCreateProducer() throws Exception { - OperationMeta operationMeta = schemaMeta.ensureFindOperation("add"); - Assert.assertEquals("add", operationMeta.getOperationId()); - - SwaggerProducerOperation producerOperation = operationMeta.getExtData(Const.PRODUCER_OPERATION); - - Object addBody = Class.forName("cse.gen.app.ms.schema.addBody").newInstance(); - ReflectUtils.setField(addBody, "x", 1); - ReflectUtils.setField(addBody, "y", 2); - Invocation invocation = new Invocation((Endpoint) null, operationMeta, new Object[] {addBody}); - Holder holder = new Holder<>(); - producerOperation.invoke(invocation, resp -> { - holder.value = resp; - }); - Assert.assertEquals(3, (int) holder.value.getResult()); - - invocation = new Invocation((Endpoint) null, operationMeta, new Object[] {1, 2}); - producerOperation.invoke(invocation, resp -> { - holder.value = resp; - }); - Assert.assertEquals(true, holder.value.isFailed()); - InvocationException exception = (InvocationException) holder.value.getResult(); - CommonExceptionData data = (CommonExceptionData) exception.getErrorData(); - Assert.assertEquals("Cse Internal Server Error", data.getMessage()); - } -} diff --git a/core/src/test/java/io/servicecomb/core/handler/TestShutdownHookHandler.java b/core/src/test/java/io/servicecomb/core/handler/TestShutdownHookHandler.java deleted file mode 100644 index d2ede6f96a1..00000000000 --- a/core/src/test/java/io/servicecomb/core/handler/TestShutdownHookHandler.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler; - -import java.lang.reflect.Field; -import java.util.concurrent.atomic.AtomicLong; - -import javax.xml.ws.Holder; - -import org.junit.Assert; -import org.junit.Test; -import org.springframework.util.ReflectionUtils; - -import io.servicecomb.core.Invocation; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.InvocationType; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.servicecomb.swagger.invocation.exception.InvocationException; -import mockit.Deencapsulation; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestShutdownHookHandler { - static Field requestCountField = ReflectionUtils.findField(ShutdownHookHandler.class, "requestCounter"); - - static { - requestCountField.setAccessible(true); - } - - static AtomicLong requestCounter = - (AtomicLong) ReflectionUtils.getField(requestCountField, ShutdownHookHandler.INSTANCE); - - @Test - public void testShutdownHookHandlerCount(@Mocked Response response) throws Exception { - Deencapsulation.setField(ShutdownHookHandler.INSTANCE, "shuttingDown", false); - - ShutdownHookHandler handler = ShutdownHookHandler.INSTANCE; - Assert.assertEquals(0, handler.getActiveCount()); - - // no reply - Invocation invocation = new MockUp() { - @Mock - public void next(AsyncResponse asyncResp) throws Exception { - } - }.getMockInstance(); - handler.handle(invocation, asyncResp -> { - }); - Assert.assertEquals(1, requestCounter.get()); - Assert.assertEquals(1, handler.getActiveCount()); - - // normal - invocation = new MockUp() { - @Mock - public void next(AsyncResponse asyncResp) throws Exception { - asyncResp.handle(response); - } - }.getMockInstance(); - handler.handle(invocation, asyncResp -> { - }); - Assert.assertEquals(2, requestCounter.get()); - Assert.assertEquals(1, handler.getActiveCount()); - - // next exception - invocation = new MockUp() { - @Mock - public void next(AsyncResponse asyncResp) throws Exception { - throw new Error(); - } - }.getMockInstance(); - try { - handler.handle(invocation, asyncResp -> { - }); - Assert.assertFalse(true); - } catch (Throwable e) { - Assert.assertEquals(3, requestCounter.get()); - Assert.assertEquals(1, handler.getActiveCount()); - } - - AtomicLong responseCounter = Deencapsulation.getField(ShutdownHookHandler.INSTANCE, "responseCounter"); - responseCounter.incrementAndGet(); - Assert.assertEquals(0, handler.getActiveCount()); - - // reply exception - // TODO: should be fixed - // try { - // handler.handle(invocation, asyncResp -> { - // throw new Error(); - // }); - // - // Assert.assertFalse(true); - // } catch (Throwable e) { - // Assert.assertEquals(3, requestCounter.get()); - // Assert.assertEquals(1, handler.getActiveCount()); - // } - } - - @Test - public void testShutdownHookHandlerReject() throws Exception { - Deencapsulation.setField(ShutdownHookHandler.INSTANCE, "shuttingDown", true); - Holder typeHolder = new Holder<>(InvocationType.PRODUCER); - Invocation invocation = new MockUp() { - @Mock - public InvocationType getInvocationType() { - return typeHolder.value; - } - }.getMockInstance(); - - ShutdownHookHandler handler = ShutdownHookHandler.INSTANCE; - handler.handle(invocation, asyncResp -> { - InvocationException e = asyncResp.getResult(); - Assert.assertEquals(((CommonExceptionData) e.getErrorData()).getMessage(), - "shutting down in progress"); - Assert.assertEquals(e.getStatusCode(), 590); - }); - - typeHolder.value = InvocationType.CONSUMER; - handler.handle(invocation, asyncResp -> { - InvocationException e = asyncResp.getResult(); - Assert.assertEquals(((CommonExceptionData) e.getErrorData()).getMessage(), - "shutting down in progress"); - Assert.assertEquals(e.getStatusCode(), 490); - }); - } -} diff --git a/core/src/test/java/io/servicecomb/core/handler/impl/TestServiceProviderHandler.java b/core/src/test/java/io/servicecomb/core/handler/impl/TestServiceProviderHandler.java deleted file mode 100644 index 5f51541342d..00000000000 --- a/core/src/test/java/io/servicecomb/core/handler/impl/TestServiceProviderHandler.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler.impl; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.swagger.invocation.AsyncResponse; - -public class TestServiceProviderHandler { - - ProducerOperationHandler serviceProviderHandler = null; - - Invocation invocation = null; - - AsyncResponse asyncResp = null; - - OperationMeta OperationMeta = null; - - @Before - public void setUp() throws Exception { - serviceProviderHandler = new ProducerOperationHandler(); - invocation = Mockito.mock(Invocation.class); - asyncResp = Mockito.mock(AsyncResponse.class); - OperationMeta = Mockito.mock(OperationMeta.class); - } - - @After - public void tearDown() throws Exception { - serviceProviderHandler = null; - invocation = null; - asyncResp = null; - OperationMeta = null; - } - - @Test - public void testHandle() { - boolean status = false; - try { - Assert.assertNotNull(serviceProviderHandler); - Mockito.when(invocation.getOperationMeta()).thenReturn(OperationMeta); - serviceProviderHandler.handle(invocation, asyncResp); - } catch (Exception e) { - status = true; - } - Assert.assertFalse(status); - } -} diff --git a/core/src/test/java/io/servicecomb/core/handler/impl/TestSimpleLoadBalanceHandler.java b/core/src/test/java/io/servicecomb/core/handler/impl/TestSimpleLoadBalanceHandler.java deleted file mode 100644 index 5b9e2ee5afd..00000000000 --- a/core/src/test/java/io/servicecomb/core/handler/impl/TestSimpleLoadBalanceHandler.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler.impl; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.endpoint.EndpointsCache; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.swagger.invocation.AsyncResponse; -import mockit.Deencapsulation; -import mockit.Mock; -import mockit.MockUp; - -public class TestSimpleLoadBalanceHandler { - MicroserviceMetaManager microserviceMetaManager = new MicroserviceMetaManager(); - - SimpleLoadBalanceHandler simpleLoadBalanceHandler = null; - - Invocation invocation = null; - - AsyncResponse asyncResp = null; - - public void mock() { - - Microservice microService = new Microservice(); - microService.setAppId("100"); - new MockUp() { - @Mock - public Microservice getMicroservice() { - return microService; - } - }; - } - - @Before - public void setUp() throws Exception { - simpleLoadBalanceHandler = new SimpleLoadBalanceHandler(); - invocation = Mockito.mock(Invocation.class); - asyncResp = Mockito.mock(AsyncResponse.class); - } - - @After - public void tearDown() throws Exception { - simpleLoadBalanceHandler = null; - invocation = null; - asyncResp = null; - } - - @Test - public void testHandler() { - boolean status = false; - mock(); - Assert.assertNotNull(simpleLoadBalanceHandler); - try { - Mockito.when(invocation.getMicroserviceName()).thenReturn(microserviceMetaManager.getName()); - Mockito.when(invocation.getMicroserviceVersionRule()).thenReturn("MicroserviceVersionRule"); - Mockito.when(invocation.getConfigTransportName()).thenReturn("TransportName"); - simpleLoadBalanceHandler.handle(invocation, asyncResp); - } catch (Exception e) { - status = true; - } - Assert.assertTrue(status); - } - - @Test - public void testHandlerWithMap() { - boolean status = false; - mock(); - Assert.assertNotNull(simpleLoadBalanceHandler); - try { - Mockito.when(invocation.getMicroserviceName()).thenReturn("MicroserviceName"); - Mockito.when(invocation.getMicroserviceVersionRule()).thenReturn("MicroserviceVersionRule"); - Mockito.when(invocation.getConfigTransportName()).thenReturn("TransportName"); - Map endpointsCacheMap = new ConcurrentHashMap<>(); - endpointsCacheMap.put("TransportName", Mockito.mock(EndpointsCache.class)); - Deencapsulation.setField(simpleLoadBalanceHandler, "endpointsCacheMap", endpointsCacheMap); - simpleLoadBalanceHandler.handle(invocation, asyncResp); - } catch (Exception e) { - status = true; - } - Assert.assertFalse(status); - } -} diff --git a/core/src/test/java/io/servicecomb/core/handler/impl/TestTransportClientHandler.java b/core/src/test/java/io/servicecomb/core/handler/impl/TestTransportClientHandler.java deleted file mode 100644 index 6792362ebd0..00000000000 --- a/core/src/test/java/io/servicecomb/core/handler/impl/TestTransportClientHandler.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.handler.impl; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import org.junit.Before; -import org.junit.Test; - -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.Transport; -import io.servicecomb.swagger.invocation.AsyncResponse; - -public class TestTransportClientHandler { - - private final Endpoint endpoint = mock(Endpoint.class); - - private final TransportClientHandler transportClientHandler = TransportClientHandler.INSTANCE; - - private final Invocation invocation = mock(Invocation.class); - - private final AsyncResponse asyncResp = mock(AsyncResponse.class); - - private final Transport transport = mock(Transport.class); - - @Before - public void setUp() throws Exception { - when(transport.getEndpoint()).thenReturn(endpoint); - when(endpoint.getEndpoint()).thenReturn("rest://localhost:8080"); - } - - @Test - public void test() throws Exception { - when(invocation.getTransport()).thenReturn(transport); - transportClientHandler.handle(invocation, asyncResp); - - verify(transport).send(invocation, asyncResp); - } -} diff --git a/core/src/test/java/io/servicecomb/core/provider/Person.java b/core/src/test/java/io/servicecomb/core/provider/Person.java deleted file mode 100644 index bef222b613a..00000000000 --- a/core/src/test/java/io/servicecomb/core/provider/Person.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.provider; - -public class Person { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/core/src/test/java/io/servicecomb/core/provider/TestCseBeanPostProcessor.java b/core/src/test/java/io/servicecomb/core/provider/TestCseBeanPostProcessor.java deleted file mode 100644 index a4eba5aea10..00000000000 --- a/core/src/test/java/io/servicecomb/core/provider/TestCseBeanPostProcessor.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.provider; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; -import org.springframework.context.ApplicationContext; - -import io.servicecomb.core.provider.CseBeanPostProcessor.ConsumerFieldProcessor; -import io.servicecomb.core.provider.CseBeanPostProcessor.ProviderProcessor; -import mockit.Deencapsulation; -import mockit.Injectable; - -public class TestCseBeanPostProcessor { - class MyProviderProcessor implements CseBeanPostProcessor.ProviderProcessor { - @Override - public void processProvider(ApplicationContext applicationContext, String beanName, Object bean) { - this.postProcessBeforeInitialization(bean, beanName); - this.postProcessAfterInitialization(bean, beanName); - Assert.assertEquals(beanName, "test"); - } - } - - class MyConsumerFieldProcessor implements CseBeanPostProcessor.ConsumerFieldProcessor { - @Override - public void processConsumerField(ApplicationContext applicationContext, Object bean, - Field field) { - } - } - - @Test - public void testCseBeanPostProcessor(@Injectable ApplicationContext context) { - CseBeanPostProcessor processor = new CseBeanPostProcessor(); - processor.setApplicationContext(context); - List providerProcessor = new ArrayList<>(); - providerProcessor.add(new MyProviderProcessor()); - - List consumerProcessor = new ArrayList<>(); - consumerProcessor.add(new MyConsumerFieldProcessor()); - - Deencapsulation.setField(processor, "providerProcessorList", providerProcessor); - Deencapsulation.setField(processor, "consumerProcessorList", consumerProcessor); - - processor.postProcessBeforeInitialization(new Person(), "test"); - processor.postProcessAfterInitialization(new Person(), "test"); - } - - @Test - public void testCseBeanPostProcessorListNull(@Injectable ApplicationContext context) { - CseBeanPostProcessor processor = new CseBeanPostProcessor(); - processor.setApplicationContext(context); - List providerProcessor = new ArrayList<>(); - - List consumerProcessor = new ArrayList<>(); - - Deencapsulation.setField(processor, "providerProcessorList", providerProcessor); - Deencapsulation.setField(processor, "consumerProcessorList", consumerProcessor); - - processor.postProcessBeforeInitialization(new Person(), "test"); - processor.postProcessAfterInitialization(new Person(), "test"); - } -} diff --git a/core/src/test/java/io/servicecomb/core/provider/consumer/TestInvokerUtils.java b/core/src/test/java/io/servicecomb/core/provider/consumer/TestInvokerUtils.java deleted file mode 100644 index 05f7994e141..00000000000 --- a/core/src/test/java/io/servicecomb/core/provider/consumer/TestInvokerUtils.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.provider.consumer; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.CseContext; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.invocation.InvocationFactory; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.InvocationException; -import mockit.Expectations; -import mockit.Injectable; -import mockit.Mock; -import mockit.MockUp; - -public class TestInvokerUtils { - - @Test - public void testSyncInvokeInvocationWithException() throws InterruptedException { - Invocation invocation = Mockito.mock(Invocation.class); - - Response response = Mockito.mock(Response.class); - new MockUp() { - @Mock - public Response waitResponse() throws InterruptedException { - return Mockito.mock(Response.class); - } - }; - Mockito.when(response.isSuccessed()).thenReturn(true); - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - Mockito.when(operationMeta.getMicroserviceQualifiedName()).thenReturn("test"); - - try { - InvokerUtils.syncInvoke(invocation); - } catch (InvocationException e) { - Assert.assertEquals(490, e.getStatusCode()); - } - } - - @Test - public void testReactiveInvoke() { - Invocation invocation = Mockito.mock(Invocation.class); - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - boolean validAssert; - try { - InvokerUtils.reactiveInvoke(invocation, asyncResp); - validAssert = true; - } catch (Exception e) { - validAssert = false; - } - Assert.assertTrue(validAssert); - } - - @Test - public void testInvokeWithException() { - new MockUp() { - @Mock - public Response waitResponse() throws InterruptedException { - return Mockito.mock(Response.class); - } - }; - Invocation invocation = Mockito.mock(Invocation.class); - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - Mockito.when(operationMeta.isSync()).thenReturn(true); - try { - InvokerUtils.invoke(invocation); - } catch (InvocationException e) { - Assert.assertEquals(490, e.getStatusCode()); - } - } - - @Test - public void testInvoke() { - Object[] objectArray = new Object[2]; - Invocation invocation = Mockito.mock(Invocation.class); - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - Mockito.when(operationMeta.isSync()).thenReturn(false); - Mockito.when(invocation.getArgs()).thenReturn(objectArray); - Object obj = InvokerUtils.invoke(invocation); - Assert.assertNull(obj); - } - - @Test - public void tetSyncInvokeNotReady() { - ReferenceConfigUtils.setReady(false); - - try { - InvokerUtils.syncInvoke("ms", "schemaId", "opName", null); - Assert.fail("must throw exception"); - } catch (IllegalStateException e) { - Assert.assertEquals("System is not ready for remote calls. " - + "When beans are making remote calls in initialization, it's better to " - + "implement io.servicecomb.core.BootListener and do it after EventType.AFTER_REGISTRY.", - e.getMessage()); - } - - try { - InvokerUtils.syncInvoke("ms", "latest", "rest", "schemaId", "opName", null); - Assert.fail("must throw exception"); - } catch (IllegalStateException e) { - Assert.assertEquals("System is not ready for remote calls. " - + "When beans are making remote calls in initialization, it's better to " - + "implement io.servicecomb.core.BootListener and do it after EventType.AFTER_REGISTRY.", - e.getMessage()); - } - } - - @Test - public void tetSyncInvokeReady(@Injectable ConsumerProviderManager consumerProviderManager, - @Injectable Invocation invocation) { - ReferenceConfigUtils.setReady(true); - CseContext.getInstance().setConsumerProviderManager(consumerProviderManager); - - new Expectations(InvocationFactory.class) { - { - InvocationFactory.forConsumer((ReferenceConfig) any, (SchemaMeta) any, (String) any, (Object[]) any); - result = invocation; - } - }; - new Expectations(InvokerUtils.class) { - { - InvokerUtils.syncInvoke(invocation); - result = "ok"; - } - }; - Object result1 = InvokerUtils.syncInvoke("ms", "schemaId", "opName", null); - Assert.assertEquals("ok", result1); - - Object result2 = InvokerUtils.syncInvoke("ms", "latest", "rest", "schemaId", "opName", null); - Assert.assertEquals("ok", result2); - - CseContext.getInstance().setConsumerProviderManager(null); - } -} diff --git a/core/src/test/java/io/servicecomb/core/provider/consumer/TestReferenceConfigUtils.java b/core/src/test/java/io/servicecomb/core/provider/consumer/TestReferenceConfigUtils.java deleted file mode 100644 index 11fedc4ffcf..00000000000 --- a/core/src/test/java/io/servicecomb/core/provider/consumer/TestReferenceConfigUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.core.provider.consumer; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.CseContext; -import mockit.Injectable; - -public class TestReferenceConfigUtils { - @Test - public void testNotReady() { - String exceptionMessage = "System is not ready for remote calls. " - + "When beans are making remote calls in initialization, it's better to " - + "implement io.servicecomb.core.BootListener and do it after EventType.AFTER_REGISTRY."; - - ReferenceConfigUtils.setReady(false); - try { - ReferenceConfigUtils.getForInvoke("abc"); - Assert.fail("must throw exception"); - } catch (IllegalStateException e) { - Assert.assertEquals(exceptionMessage, e.getMessage()); - } - - try { - ReferenceConfigUtils.getForInvoke("abc", "v1", ""); - Assert.fail("must throw exception"); - } catch (IllegalStateException e) { - Assert.assertEquals(exceptionMessage, e.getMessage()); - } - } - - @Test - public void testReady(@Injectable ConsumerProviderManager consumerProviderManager) { - ReferenceConfigUtils.setReady(true); - CseContext.getInstance().setConsumerProviderManager(consumerProviderManager); - - Assert.assertNotNull(ReferenceConfigUtils.getForInvoke("abc")); - Assert.assertNotNull(ReferenceConfigUtils.getForInvoke("abc", "v1", "")); - } -} diff --git a/core/src/test/java/io/servicecomb/core/provider/producer/TestProducerMeta.java b/core/src/test/java/io/servicecomb/core/provider/producer/TestProducerMeta.java deleted file mode 100644 index 6eb345e0ce1..00000000000 --- a/core/src/test/java/io/servicecomb/core/provider/producer/TestProducerMeta.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.core.provider.producer; - -import org.junit.Assert; -import org.junit.Test; - -public class TestProducerMeta { - @Test - public void test1() { - Object instance = new Object(); - ProducerMeta meta = new ProducerMeta("id", instance, instance.getClass()); - Assert.assertEquals("id", meta.getSchemaId()); - Assert.assertEquals(instance, meta.getInstance()); - Assert.assertEquals(Object.class, meta.getInstanceClass()); - } - - @Test - public void test2() { - ProducerMeta meta = new ProducerMeta(); - meta.setSchemaId("id1"); - Assert.assertEquals("id1", meta.getSchemaId()); - - meta.setInstance(1); - Assert.assertEquals(1, meta.getInstance()); - - meta.setInstanceClass(Integer.class); - Assert.assertEquals(Integer.class, meta.getInstanceClass()); - } -} diff --git a/core/src/test/java/io/servicecomb/core/transport/TestAbstractTransport.java b/core/src/test/java/io/servicecomb/core/transport/TestAbstractTransport.java deleted file mode 100644 index b39545b5b2f..00000000000 --- a/core/src/test/java/io/servicecomb/core/transport/TestAbstractTransport.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.transport; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.Collections; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.servicecomb.core.Invocation; -import io.servicecomb.foundation.common.exceptions.ServiceCombException; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.registry.AbstractServiceRegistry; -import io.servicecomb.swagger.invocation.AsyncResponse; -import mockit.Expectations; -import mockit.Injectable; -import mockit.Mocked; - -public class TestAbstractTransport { - class MyAbstractTransport extends AbstractTransport { - @Override - public String getName() { - return "my"; - } - - @Override - public boolean init() throws Exception { - return true; - } - - @Override - public void send(Invocation invocation, AsyncResponse asyncResp) throws Exception { - } - } - - @Injectable - AbstractServiceRegistry serviceRegistry; - - @Before - public void setup() { - RegistryUtils.setServiceRegistry(serviceRegistry); - } - - @After - public void teardown() { - RegistryUtils.setServiceRegistry(null); - } - - @Test - public void testSetListenAddressWithoutSchemaChineseSpaceNewSC() throws UnsupportedEncodingException { - new Expectations() { - { - serviceRegistry.getFeatures().isCanEncodeEndpoint(); - result = true; - } - }; - - MyAbstractTransport transport = new MyAbstractTransport(); - transport.setListenAddressWithoutSchema("127.0.0.1:9090", Collections.singletonMap("country", "中 国")); - Assert.assertEquals("my://127.0.0.1:9090?country=" + URLEncoder.encode("中 国", StandardCharsets.UTF_8.name()), - transport.getEndpoint().getEndpoint()); - } - - @Test - public void testSetListenAddressWithoutSchemaChineseSpaceOldSC() throws UnsupportedEncodingException { - MyAbstractTransport transport = new MyAbstractTransport(); - try { - transport.setListenAddressWithoutSchema("127.0.0.1:9090", Collections.singletonMap("country", "中 国")); - Assert.fail("must throw exception"); - } catch (ServiceCombException e) { - Assert.assertEquals( - "current service center not support encoded endpoint, please do not use chinese or space or anything need to be encoded.", - e.getMessage()); - Assert.assertEquals( - "Illegal character in query at index 31: rest://127.0.0.1:9090?country=中 国", - e.getCause().getMessage()); - } - } - - @Test - public void testSetListenAddressWithoutSchemaNormalNotEncode() throws UnsupportedEncodingException { - MyAbstractTransport transport = new MyAbstractTransport(); - transport.setListenAddressWithoutSchema("127.0.0.1:9090", Collections.singletonMap("country", "chinese")); - Assert.assertEquals("my://127.0.0.1:9090?country=chinese", transport.getEndpoint().getEndpoint()); - } - - @Test - public void testSetListenAddressWithoutSchemaAlreadyHaveQuery() throws UnsupportedEncodingException { - MyAbstractTransport transport = new MyAbstractTransport(); - transport.setListenAddressWithoutSchema("127.0.0.1:9090?a=aValue", - Collections.singletonMap("country", "chinese")); - Assert.assertEquals("my://127.0.0.1:9090?a=aValue&country=chinese", transport.getEndpoint().getEndpoint()); - } - - @Test - public void testMyAbstractTransport() throws Exception { - MyAbstractTransport transport = new MyAbstractTransport(); - transport.setListenAddressWithoutSchema("127.0.0.1:9090"); - Assert.assertEquals("my", transport.getName()); - Assert.assertEquals("my://127.0.0.1:9090", transport.getEndpoint().getEndpoint()); - Assert.assertEquals("127.0.0.1", ((IpPort) transport.parseAddress("my://127.0.0.1:9090")).getHostOrIp()); - transport.setListenAddressWithoutSchema("0.0.0.0:9090"); - Assert.assertNotEquals("my://127.0.0.1:9090", transport.getEndpoint().getEndpoint()); - transport.setListenAddressWithoutSchema(null); - Assert.assertNull(transport.getEndpoint().getEndpoint()); - Assert.assertNull(transport.parseAddress(null)); - Assert.assertEquals(30000, AbstractTransport.getRequestTimeout()); - } - - @Test(expected = NumberFormatException.class) - public void testMyAbstractTransportException(@Mocked TransportManager manager) throws Exception { - MyAbstractTransport transport = new MyAbstractTransport(); - - transport.setListenAddressWithoutSchema(":127.0.0.1:9090"); - } -} diff --git a/core/src/test/java/io/servicecomb/core/transport/TestTransportManager.java b/core/src/test/java/io/servicecomb/core/transport/TestTransportManager.java deleted file mode 100644 index ba2d79c96bd..00000000000 --- a/core/src/test/java/io/servicecomb/core/transport/TestTransportManager.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.core.transport; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Transport; -import io.servicecomb.foundation.common.exceptions.ServiceCombException; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import mockit.Expectations; -import mockit.Injectable; -import mockit.Mocked; - -public class TestTransportManager { - @Test - public void testTransportManagerInitFail(@Injectable Transport transport) throws Exception { - new Expectations() { - { - transport.getName(); - result = "test"; - transport.init(); - result = false; - transport.canInit(); - result = true; - } - }; - List transports = Arrays.asList(transport); - - TransportManager manager = new TransportManager(); - manager.setTransports(transports); - - manager.init(); - Assert.assertEquals(manager.findTransport("test"), transport); - } - - @Test - public void testTransportManagerInitSucc(@Injectable Transport transport, @Injectable Endpoint endpoint, - @Mocked RegistryUtils util, @Injectable MicroserviceInstance instance) throws Exception { - new Expectations() { - { - transport.getName(); - result = "test"; - transport.canInit(); - result = true; - transport.init(); - result = true; - transport.getPublishEndpoint(); - result = endpoint; - } - }; - List transports = Arrays.asList(transport); - - TransportManager manager = new TransportManager(); - manager.setTransports(transports); - - manager.init(); - Assert.assertEquals(manager.findTransport("test"), transport); - } - - @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"; - } - }; - - TransportManager manager = new TransportManager(); - manager.setTransports(Arrays.asList(t1, t2_1, t2_2)); - - Map> groups = manager.groupByName(); - Assert.assertEquals(2, groups.size()); - Assert.assertEquals(1, groups.get("t1").size()); - Assert.assertEquals(t1, groups.get("t1").get(0)); - Assert.assertEquals(2, groups.get("t2").size()); - Assert.assertEquals(t2_1, groups.get("t2").get(0)); - Assert.assertEquals(t2_2, 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; - } - }; - - TransportManager manager = new TransportManager(); - List group = Arrays.asList(t1, t2); - - try { - manager.checkTransportGroup(group); - Assert.fail("must throw exception"); - } catch (ServiceCombException e) { - Assert.assertEquals( - "io.servicecomb.core.$Impl_Transport and io.servicecomb.core.$Impl_Transport have the same order 1", - e.getMessage()); - } - } - - @Test - public void testCheckTransportGroupValid(@Mocked Transport t1, @Mocked Transport t2) { - new Expectations() { - { - t1.getOrder(); - result = 1; - - t2.getOrder(); - result = 2; - } - }; - - TransportManager manager = new TransportManager(); - List group = Arrays.asList(t1, t2); - - try { - manager.checkTransportGroup(group); - } catch (ServiceCombException e) { - Assert.fail("must not throw exception: " + e.getMessage()); - } - } - - @Test - public void testChooseOneTransportFirst(@Mocked Transport t1, @Mocked Transport t2) { - new Expectations() { - { - t1.getOrder(); - result = 1; - t1.canInit(); - result = true; - - t2.getOrder(); - result = 2; - } - }; - - TransportManager manager = new TransportManager(); - List group = Arrays.asList(t1, t2); - - Assert.assertEquals(t1, manager.chooseOneTransport(group)); - } - - @Test - public void testChooseOneTransportSecond(@Mocked Transport t1, @Mocked Transport t2) { - new Expectations() { - { - t1.getOrder(); - result = 1; - t1.canInit(); - result = false; - - t2.getOrder(); - result = 2; - t2.canInit(); - result = true; - } - }; - - TransportManager manager = new TransportManager(); - List group = Arrays.asList(t1, t2); - - Assert.assertEquals(t2, manager.chooseOneTransport(group)); - } - - @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; - } - }; - - TransportManager manager = new TransportManager(); - List group = Arrays.asList(t1, t2); - - try { - manager.chooseOneTransport(group); - Assert.fail("must throw exception"); - } catch (ServiceCombException e) { - Assert.assertEquals("all transport named t refused to init.", e.getMessage()); - } - } -} diff --git a/core/src/test/java/org/apache/servicecomb/core/Config.java b/core/src/test/java/org/apache/servicecomb/core/Config.java new file mode 100644 index 00000000000..6d78ddfc7bd --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/Config.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.core; + +public class Config { + private static int clientThread; + + private static String transport; + + private static String mode; + + public static String getTransport() { + return transport; + } + + public static void setTransport(String transport) { + Config.transport = transport; + } + + public static int getClientThread() { + return clientThread; + } + + public static void setClientThread(int clientThread) { + Config.clientThread = clientThread; + } + + public static String getMode() { + return mode; + } + + public static void setMode(String mode) { + Config.mode = mode; + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/TestConfig.java b/core/src/test/java/org/apache/servicecomb/core/TestConfig.java new file mode 100644 index 00000000000..a4584b1e64f --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/TestConfig.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.core; + +import java.util.HashMap; +import java.util.Map; + +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; +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.apache.servicecomb.swagger.invocation.context.HttpStatus; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestConfig { + @Test + public void testConstants() { + 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 + public void testHttpResponse() { + String objectString = new String("Unit Testing"); + Response oResponse = Response.success(objectString, Status.OK); + + Assertions.assertTrue(oResponse.isSucceed()); + + oResponse = Response.succResp(objectString); + Assertions.assertTrue(oResponse.isSucceed()); + Assertions.assertEquals(200, oResponse.getStatusCode()); + + Throwable oThrowable = new Throwable("Error"); + + oResponse = Response.consumerFailResp(oThrowable); + Assertions.assertTrue(oResponse.isFailed()); + + oResponse = Response.providerFailResp(oThrowable); + Assertions.assertTrue(oResponse.isFailed()); + } + + @Test + public void testHttpStatus() { + StatusType oStatus = new HttpStatus(204, "InternalServerError"); + Assertions.assertEquals("InternalServerError", oStatus.getReasonPhrase()); + } + + @Test + public void testContextUtils() { + ThreadLocal contextMgr = new ThreadLocal<>(); + Assertions.assertEquals(contextMgr.get(), ContextUtils.getInvocationContext()); + + SwaggerInvocation invocation = new SwaggerInvocation(); + invocation.addContext("test1", "testObject"); + + Assertions.assertEquals("testObject", invocation.getContext("test1")); + + Map context = new HashMap<>(); + context.put("test2", new String("testObject")); + invocation.setContext(context); + Assertions.assertEquals(context, invocation.getContext()); + + invocation.setStatus(Status.OK); + Assertions.assertEquals(200, invocation.getStatus().getStatusCode()); + + invocation.setStatus(204); + Assertions.assertEquals(204, invocation.getStatus().getStatusCode()); + + invocation.setStatus(Status.OK); + Assertions.assertEquals((Status.OK).getStatusCode(), invocation.getStatus().getStatusCode()); + + invocation.setStatus(203, "Done"); + Assertions.assertEquals(203, invocation.getStatus().getStatusCode()); + + ContextUtils.setInvocationContext(invocation); + Assertions.assertEquals(invocation, ContextUtils.getInvocationContext()); + + ContextUtils.removeInvocationContext(); + Assertions.assertNull(ContextUtils.getInvocationContext()); + } + + @Test + public void testResponse() { + Response response = Response.create(400, "test", null); + InvocationException exception = response.getResult(); + Assertions.assertNull(exception.getErrorData()); + + response = Response.create(400, "test", "errorData"); + exception = response.getResult(); + Assertions.assertEquals("errorData", exception.getErrorData()); + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/TestConfigurationSpringInitializer.java b/core/src/test/java/org/apache/servicecomb/core/TestConfigurationSpringInitializer.java new file mode 100644 index 00000000000..27b38f60110 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/TestConfigurationSpringInitializer.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.HashMap; +import java.util.Map; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.config.Configurator; +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.ArgumentMatchers; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; +import org.springframework.core.env.CompositePropertySource; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.jndi.JndiPropertySource; + +public class TestConfigurationSpringInitializer { + @BeforeEach + public void beforeTest() { + Configurator.setRootLevel(Level.OFF); + + Configurator.setRootLevel(Level.INFO); + } + + @AfterEach + public void afterTest() { + + } + + @Test + public void testSetEnvironment() { + ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); + MutablePropertySources propertySources = new MutablePropertySources(); + Map propertyMap = new HashMap<>(); + final String map0Key0 = "map0-Key0"; + final String map1Key0 = "map1-Key0"; + final String map2Key0 = "map2-Key0"; + final String map3Key0 = "map3-Key0"; + propertyMap.put(map0Key0, "map0-Value0"); + propertyMap.put(map1Key0, "map1-Value0"); + propertyMap.put(map2Key0, "map2-Value0"); + propertyMap.put(map3Key0, "map3-Value0"); + + /* + propertySources + |- compositePropertySource0 + | |- mapPropertySource0 + | | |- map0-Key0 = map0-Value0 + | |- compositePropertySource1 + | |- mapPropertySource1 + | | |- map1-Key0 = map1-Value0 + | |- mapPropertySource2 + | |- map2-Key0 = map2-Value0 + | |- JndiPropertySource(mocked) + |- mapPropertySource3 + |- map3-Key0 = map3-Value0 + */ + CompositePropertySource compositePropertySource0 = new CompositePropertySource("compositePropertySource0"); + propertySources.addFirst(compositePropertySource0); + + HashMap map0 = new HashMap<>(); + map0.put(map0Key0, propertyMap.get(map0Key0)); + MapPropertySource mapPropertySource0 = new MapPropertySource("mapPropertySource0", map0); + compositePropertySource0.addFirstPropertySource(mapPropertySource0); + + CompositePropertySource compositePropertySource1 = new CompositePropertySource("compositePropertySource1"); + compositePropertySource0.addPropertySource(compositePropertySource1); + HashMap map1 = new HashMap<>(); + map1.put(map1Key0, propertyMap.get(map1Key0)); + MapPropertySource mapPropertySource1 = new MapPropertySource("mapPropertySource1", map1); + compositePropertySource1.addPropertySource(mapPropertySource1); + HashMap map2 = new HashMap<>(); + map2.put(map2Key0, propertyMap.get(map2Key0)); + MapPropertySource mapPropertySource2 = new MapPropertySource("mapPropertySource2", map2); + compositePropertySource1.addPropertySource(mapPropertySource2); + compositePropertySource1.addPropertySource(Mockito.mock(JndiPropertySource.class)); + + HashMap map3 = new HashMap<>(); + map3.put(map3Key0, propertyMap.get(map3Key0)); + MapPropertySource mapPropertySource3 = new MapPropertySource("mapPropertySource3", map3); + compositePropertySource0.addPropertySource(mapPropertySource3); + + Mockito.when(environment.getPropertySources()).thenReturn(propertySources); + Mockito.doAnswer((Answer) invocation -> { + Object[] args = invocation.getArguments(); + String propertyName = (String) args[0]; + + if ("spring.config.name".equals(propertyName) || "spring.application.name".equals(propertyName)) { + return null; + } + + String value = propertyMap.get(propertyName); + if (null == value) { + Assertions.fail("get unexpected property name: " + propertyName); + } + return value; + }).when(environment).getProperty(ArgumentMatchers.anyString(), ArgumentMatchers.eq(Object.class)); + } + + @Test + public void testSetEnvironmentOnEnvironmentName() { + // get environment name from spring.config.name + ConfigurableEnvironment environment0 = Mockito.mock(ConfigurableEnvironment.class); + MutablePropertySources propertySources0 = new MutablePropertySources(); + Mockito.when(environment0.getPropertySources()).thenReturn(propertySources0); + Map map0 = new HashMap<>(1); + map0.put("spring.config.name", "application"); + propertySources0.addFirst(new MapPropertySource("mapPropertySource0", map0)); + Mockito.when(environment0.getProperty("spring.config.name", Object.class)).thenReturn("application"); + Mockito.when(environment0.getProperty("spring.config.name")).thenReturn("application"); + + // get environment name from spring.application.name + ConfigurableEnvironment environment1 = Mockito.mock(ConfigurableEnvironment.class); + MutablePropertySources propertySources1 = new MutablePropertySources(); + Mockito.when(environment1.getPropertySources()).thenReturn(propertySources1); + Map map1 = new HashMap<>(1); + map1.put("spring.application.name", "bootstrap"); + propertySources1.addFirst(new MapPropertySource("mapPropertySource1", map1)); + Mockito.when(environment1.getProperty("spring.application.name", Object.class)).thenReturn("bootstrap"); + Mockito.when(environment1.getProperty("spring.application.name")).thenReturn("bootstrap"); + + // get environment name from className+hashcode + ConfigurableEnvironment environment2 = Mockito.mock(ConfigurableEnvironment.class); + MutablePropertySources propertySources2 = new MutablePropertySources(); + Mockito.when(environment2.getPropertySources()).thenReturn(propertySources2); + Map map2 = new HashMap<>(1); + map2.put("key2", "value2"); + propertySources2.addFirst(new MapPropertySource("mapPropertySource2", map2)); + Mockito.when(environment2.getProperty("key2", Object.class)).thenReturn("value2"); + Mockito.when(environment2.getProperty("key2")).thenReturn("value2"); + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/TestEndpoint.java b/core/src/test/java/org/apache/servicecomb/core/TestEndpoint.java new file mode 100644 index 00000000000..17e1ef0c5e1 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/TestEndpoint.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.core; + +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 TestEndpoint { + @Test + public void testEndpoint() { + Transport transport = Mockito.mock(Transport.class); + Mockito.when(transport.parseAddress("rest://123.6.6.6:8080")).thenReturn("rest://123.6.6.6:8080"); + + Endpoint endpoint = new Endpoint(transport, "rest://123.6.6.6:8080"); + Assertions.assertEquals(endpoint.getAddress(), "rest://123.6.6.6:8080"); + Assertions.assertEquals(endpoint.getEndpoint(), "rest://123.6.6.6:8080"); + Assertions.assertEquals(endpoint.getTransport(), transport); + Assertions.assertEquals(endpoint.toString(), "rest://123.6.6.6:8080"); + } + + @Test + public void testEndpointAddressConstructor() { + Transport transport = Mockito.mock(Transport.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"); + Assertions.assertEquals(endpoint.getTransport(), transport); + Assertions.assertEquals(endpoint.toString(), "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 new file mode 100644 index 00000000000..58bce6bce43 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/TestException.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.core; + +import org.apache.servicecomb.core.exception.CseException; +import org.apache.servicecomb.core.exception.ExceptionUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestException { + @Test + public void testCseException() { + CseException oException = new CseException("500", "InternalServerError"); + Assertions.assertEquals("500", oException.getCode()); + Assertions.assertEquals("ServiceDefinitionException Code:500, Message:InternalServerError", + oException.toString()); + + oException = new CseException("503", "OwnException", new Throwable()); + Assertions.assertEquals("503", oException.getCode()); + } + + @Test + public void testExceptionUtils() { + CseException oException = ExceptionUtils + .createCseException("servicecomb.handler.ref.not.exist", new String("test")); + Assertions.assertEquals("servicecomb.handler.ref.not.exist", oException.getCode()); + + oException = + ExceptionUtils.createCseException("servicecomb.handler.ref.not.exist", new Throwable(), new String("test")); + Assertions.assertEquals("servicecomb.handler.ref.not.exist", oException.getCode()); + + oException = ExceptionUtils.producerOperationNotExist("servicecomb.error", "unit-testing"); + Assertions.assertEquals("servicecomb.producer.operation.not.exist", oException.getCode()); + + oException = ExceptionUtils.handlerRefNotExist("servicecomb.double.error"); + Assertions.assertEquals("servicecomb.handler.ref.not.exist", oException.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 new file mode 100644 index 00000000000..80b6bbda461 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/TestInvocation.java @@ -0,0 +1,274 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.Arrays; + +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.InvocationBusinessMethodStartEvent; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.InvocationStartEvent; +import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; +import org.apache.servicecomb.core.tracing.BraveTraceIdGenerator; +import org.apache.servicecomb.core.tracing.TraceIdGenerator; +import org.apache.servicecomb.foundation.common.Holder; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; +import org.apache.servicecomb.swagger.invocation.Response; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +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; + +import mockit.Expectations; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; + +public class TestInvocation { + + @Mocked + Endpoint endpoint; + + @Mocked + OperationMeta operationMeta; + + @Mocked + InvocationRuntimeType invocationRuntimeType; + + static long nanoTime = 123; + + @BeforeClass + public static void classSetup() { + EventManager.eventBus = new EventBus(); + } + + protected static void mockNonaTime() { + new MockUp() { + @Mock + long nanoTime() { + return nanoTime; + } + }; + } + + @AfterClass + public static void classTeardown() { + EventManager.eventBus = new EventBus(); + } + + @Test + public void onStart() { + Holder result = new Holder<>(); + Object subscriber = new Object() { + @Subscribe + public void onStart(InvocationStartEvent event) { + result.value = event.getInvocation(); + } + }; + EventManager.register(subscriber); + + Invocation invocation = new Invocation(endpoint, operationMeta, null); + invocation.onStart(); + + Assertions.assertSame(invocation, result.value); + EventManager.unregister(subscriber); + } + + @Test + public void onFinish() { + mockNonaTime(); + + Holder result = new Holder<>(); + Object subscriber = new Object() { + @Subscribe + public void onStart(InvocationFinishEvent event) { + result.value = event; + } + }; + EventManager.register(subscriber); + + Invocation invocation = new Invocation(endpoint, operationMeta, null); + Assertions.assertFalse(invocation.isFinished()); + Response response = Response.succResp(null); + invocation.onFinish(response); + + Assertions.assertSame(invocation, result.value.getInvocation()); + Assertions.assertSame(response, result.value.getResponse()); + Assertions.assertTrue(invocation.isFinished()); + + // should not post event again + InvocationFinishEvent oldEvent = result.value; + invocation.onFinish(null); + Assertions.assertSame(oldEvent, result.value); + + EventManager.unregister(subscriber); + } + + @Test + public void isConsumer_yes() { + 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, null); + Assertions.assertTrue(invocation.isConsumer()); + } + + @Test + public void localContext(@Mocked ReferenceConfig referenceConfig) { + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); + + invocation.addLocalContext("k", 1); + Assertions.assertSame(invocation.getHandlerContext(), invocation.getLocalContext()); + Assertions.assertEquals(1, (int) invocation.getLocalContext("k")); + } + + @Test + public void traceId_fromContext(@Mocked ReferenceConfig referenceConfig) { + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); + invocation.addContext(CoreConst.TRACE_ID_NAME, "abc"); + + invocation.onStart(); + + Assertions.assertEquals("abc", invocation.getTraceId()); + Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); + } + + @Test + public void traceId_consumerCreateTraceId(@Mocked ReferenceConfig referenceConfig) { + TraceIdGenerator generator = SPIServiceUtils.getTargetService(TraceIdGenerator.class, BraveTraceIdGenerator.class); + new Expectations(generator) { + { + generator.generate(); + result = "abc"; + } + }; + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); + + invocation.onStart(); + + Assertions.assertEquals("abc", invocation.getTraceId()); + Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); + } + + @Test + public void traceId_fromRequest(@Mocked Endpoint endpoint, @Mocked HttpServletRequestEx requestEx) { + new Expectations() { + { + requestEx.getHeader(CoreConst.TRACE_ID_NAME); + result = "abc"; + } + }; + Invocation invocation = new Invocation(endpoint, operationMeta, null); + + invocation.onStart(requestEx); + + Assertions.assertEquals("abc", invocation.getTraceId()); + 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) { + { + generator.generate(); + result = "abc"; + } + }; + Invocation invocation = new Invocation(endpoint, operationMeta, null); + + invocation.onStart(requestEx); + + Assertions.assertEquals("abc", invocation.getTraceId()); + Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); + } + + @Test + public void traceIdGeneratorInit(@Mocked TraceIdGenerator gen1, @Mocked TraceIdGenerator gen2, + @Mocked TraceIdGenerator gen3, @Mocked TraceIdGenerator gen4) { + new Expectations(SPIServiceUtils.class) { + { + gen1.getName(); + result = "zipkin"; + + gen3.getName(); + result = "apm"; + + gen2.getName(); + result = "zipkin"; + + gen4.getName(); + result = "apm"; + + SPIServiceUtils.getOrLoadSortedService(TraceIdGenerator.class); + result = Arrays.asList(gen1, gen3, gen2, gen4); + } + }; + + MatcherAssert.assertThat(Invocation.loadTraceIdGenerators(), Matchers.containsInAnyOrder(gen1, gen3)); + } + + InvocationBaseEvent invocationBaseEvent; + + @Test + public void test_business_execute_time_correct() { + Object listener = new Object() { + @Subscribe + public void onBusinessMethodStart(InvocationBusinessMethodStartEvent event) { + invocationBaseEvent = event; + } + }; + EventManager.getEventBus().register(listener); + Invocation invocation = new Invocation(endpoint, operationMeta, null); + mockNonaTime(); + invocation.onBusinessMethodStart(); + EventManager.getEventBus().unregister(listener); + + Assertions.assertSame(invocation, invocationBaseEvent.getInvocation()); + nanoTime++; + invocation.onBusinessFinish(); + 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, null); + invocation.addContext(CoreConst.TRACE_ID_NAME, "abc"); + invocation.onStart(); + Assertions.assertEquals("abc", invocation.getTraceIdLogger().getName()); + + 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 new file mode 100644 index 00000000000..99690c3c310 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/TestSCBApplicationListener.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.core; + +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.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() { + } + + @AfterAll + public static void classTeardown() { + } + + @Test + public void onApplicationEvent_close() { + ContextClosedEvent contextClosedEvent = Mockito.mock(ContextClosedEvent.class); + 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(scbEngine); + listener.onApplicationEvent(contextClosedEvent); + + Assertions.assertEquals(SCBStatus.DOWN, scbEngine.getStatus()); + + scbEngine.destroy(); + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/TestTransport.java b/core/src/test/java/org/apache/servicecomb/core/TestTransport.java new file mode 100644 index 00000000000..4ab2f99ff80 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/TestTransport.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.core; + +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 + public static void classSetup() { + + } + + @AfterAll + public static void classTeardown() { + } + + @Test + public void testEndpoint() throws Exception { + Endpoint oEndpoint = new Endpoint(new Transport() { + @Override + public Object parseAddress(String address) { + return "127.0.0.1"; + } + + @Override + public void setEnvironment(Environment environment) { + + } + @Override + public boolean init() { + return true; + } + + @Override + public String getName() { + return "test"; + } + + @Override + public Endpoint getEndpoint() { + return (new Endpoint(this, "testEndpoint")); + } + + @Override + public Endpoint getPublishEndpoint() { + return (new Endpoint(this, "testEndpoint")); + } + }, "rest://127.0.0.1:8080"); + oEndpoint.getTransport().init(); + Assertions.assertEquals("rest://127.0.0.1:8080", oEndpoint.getEndpoint()); + Assertions.assertEquals("127.0.0.1", oEndpoint.getAddress()); + Assertions.assertEquals("test", oEndpoint.getTransport().getName()); + Assertions.assertEquals("rest://127.0.0.1:8080", oEndpoint.getEndpoint()); + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/consumer/TestReactiveResponseExecutor.java b/core/src/test/java/org/apache/servicecomb/core/consumer/TestReactiveResponseExecutor.java new file mode 100644 index 00000000000..0e2b33b9012 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/consumer/TestReactiveResponseExecutor.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.core.consumer; + +import org.apache.servicecomb.core.provider.consumer.ReactiveResponseExecutor; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestReactiveResponseExecutor { + @Test + public void testReactiveResponseExecutor() { + ReactiveResponseExecutor executor = new ReactiveResponseExecutor(); + Runnable cmd = Mockito.mock(Runnable.class); + boolean validAssert = true; + try { + executor.execute(cmd); + } catch (Exception e) { + validAssert = false; + } + Assertions.assertTrue(validAssert); + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/consumer/TestSyncResponseExecutor.java b/core/src/test/java/org/apache/servicecomb/core/consumer/TestSyncResponseExecutor.java new file mode 100644 index 00000000000..750203072b7 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/consumer/TestSyncResponseExecutor.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.consumer; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.provider.consumer.SyncResponseExecutor; +import org.apache.servicecomb.swagger.invocation.Response; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestSyncResponseExecutor { + @Test + public void testSyncResponseExecutor() { + SyncResponseExecutor executor = new SyncResponseExecutor(); + Runnable cmd = Mockito.mock(Runnable.class); + Response response = Mockito.mock(Response.class); + Invocation invocation = Mockito.mock(Invocation.class); + executor.execute(cmd); + executor.setResponse(response); + + try { + Response responseValue = executor.waitResponse(invocation); + Assertions.assertNotNull(responseValue); + } catch (Exception e) { + Assertions.assertNotNull(e); + } + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/definition/MicroServicePropertyExtendedStub.java b/core/src/test/java/org/apache/servicecomb/core/definition/MicroServicePropertyExtendedStub.java new file mode 100644 index 00000000000..96550b1214f --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/definition/MicroServicePropertyExtendedStub.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.core.definition; + +import java.util.Map; + +import org.apache.servicecomb.registry.api.PropertyExtended; + +public class MicroServicePropertyExtendedStub implements PropertyExtended { + @Override + public Map getExtendedProperties() { + return null; + } +} 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 new file mode 100644 index 00000000000..ad664261980 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/definition/OperationConfigTest.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.core.definition; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.core.CoreConst; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class OperationConfigTest { + OperationConfig config = new OperationConfig(); + + @Nested + class InPoolTimeout { + @Test + void should_get_rest_value() { + config.setMsDefaultRequestWaitInPoolTimeout(1); + config.setMsRestRequestWaitInPoolTimeout(2); + + long nano = TimeUnit.MILLISECONDS.toNanos(2); + assertThat(config.getNanoRestRequestWaitInPoolTimeout()).isEqualTo(nano); + assertThat(config.getNanoRequestWaitInPoolTimeout(CoreConst.RESTFUL)).isEqualTo(nano); + } + + @Test + void should_get_highway_value() { + config.setMsDefaultRequestWaitInPoolTimeout(1); + config.setMsHighwayRequestWaitInPoolTimeout(2); + + long nano = TimeUnit.MILLISECONDS.toNanos(2); + assertThat(config.getNanoHighwayRequestWaitInPoolTimeout()).isEqualTo(nano); + assertThat(config.getNanoRequestWaitInPoolTimeout(CoreConst.HIGHWAY)).isEqualTo(nano); + } + + @Test + void should_support_register_customize_transport() { + config.setMsDefaultRequestWaitInPoolTimeout(1); + config.registerRequestWaitInPoolTimeout("abc", 2); + + long nano = TimeUnit.MILLISECONDS.toNanos(2); + assertThat(config.getNanoRequestWaitInPoolTimeout("abc")).isEqualTo(nano); + } + + @Test + void should_get_invocation_timeout_value() { + config.setMsInvocationTimeout(1); + + long nano = TimeUnit.MILLISECONDS.toNanos(1); + assertThat(config.getNanoInvocationTimeout()).isEqualTo(nano); + assertThat(config.getMsInvocationTimeout()).isEqualTo(1); + } + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/definition/TestMicroserviceMetaManager.java b/core/src/test/java/org/apache/servicecomb/core/definition/TestMicroserviceMetaManager.java new file mode 100644 index 00000000000..8d89a10a285 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/definition/TestMicroserviceMetaManager.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.core.definition; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestMicroserviceMetaManager { + + @Test + public void testEnsureFindSchemaMeta() { + SchemaMeta meta = Mockito.mock(SchemaMeta.class); + MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); + Mockito.when(microserviceMeta.ensureFindSchemaMeta("yhfghj")).thenReturn(meta); + Assertions.assertEquals(meta, microserviceMeta.ensureFindSchemaMeta("yhfghj")); + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/event/TestInvocationStartEvent.java b/core/src/test/java/org/apache/servicecomb/core/event/TestInvocationStartEvent.java new file mode 100644 index 00000000000..6e0649d4aa1 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/event/TestInvocationStartEvent.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.core.event; + +import org.apache.servicecomb.core.Invocation; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestInvocationStartEvent { + InvocationStartEvent event; + + @Test + public void construct() { + Invocation invocation = Mockito.mock(Invocation.class); + event = new InvocationStartEvent(invocation); + + Assertions.assertSame(invocation, event.getInvocation()); + } +} 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 new file mode 100644 index 00000000000..0594084e30c --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/exception/ExceptionsTest.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.core.exception; + +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 org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; +import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +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"); + + assertThat(Exceptions.convert(null, exception, BAD_REQUEST)) + .isSameAs(exception); + } + + @Test + void should_convert_unknown_client_exception_to_invocation_exception() { + IllegalStateException exception = new IllegalStateException("msg"); + + InvocationException invocationException = Exceptions.convert(null, exception, BAD_REQUEST); + + assertThat(invocationException).hasCause(exception); + assertThat(invocationException.getStatus()).isEqualTo(BAD_REQUEST); + assertThat(invocationException.getErrorData()).isInstanceOf(CommonExceptionData.class); + + 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 + void should_convert_unknown_server_exception_to_invocation_exception() { + IllegalStateException exception = new IllegalStateException("msg"); + + InvocationException invocationException = Exceptions.convert(null, exception, INTERNAL_SERVER_ERROR); + + assertThat(invocationException).hasCause(exception); + assertThat(invocationException.getStatus()).isEqualTo(INTERNAL_SERVER_ERROR); + assertThat(invocationException.getErrorData()).isInstanceOf(CommonExceptionData.class); + + 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 { + @Override + public int getOrder() { + return Integer.MIN_VALUE; + } + + @Override + public boolean canConvert(Throwable throwable) { + return true; + } + + @Override + public InvocationException convert(Invocation invocation, Throwable throwable, StatusType genericStatus) { + throw new RuntimeExceptionWithoutStackTrace("mock exception when convert"); + } + } + + @Test + void should_protect_when_converter_throw_exception() { + DefaultExceptionProcessor processor = new DefaultExceptionProcessor() + .setConverters(Collections.singletonList(new ThrowExceptionWhenConvert())); + + try (LogCollector logCollector = new LogCollector()) { + InvocationException exception = processor + .convert(null, new RuntimeExceptionWithoutStackTrace("exception need convert"), BAD_REQUEST); + + assertThat(exception.getStatus()) + .isSameAs(INTERNAL_SERVER_ERROR); + assertThat(exception.getErrorData().toString()) + .isEqualTo("CommonExceptionData{code='SCB.50000000', message='Internal Server Error', dynamic={}}"); + assertThat(logCollector.getLastEvents().getMessage().getFormattedMessage().replace("\r\n", "\n")) + .isEqualTo("BUG: ExceptionConverter.convert MUST not throw exception, please fix it.\n" + + "original exception :org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace: exception need convert\n" + + "converter exception:org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace: mock exception when convert\n"); + } + } +} 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 new file mode 100644 index 00000000000..3a1b69908bb --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/exception/converter/TimeoutExceptionConverterTest.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.core.exception.converter; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.TimeoutException; + +import jakarta.ws.rs.core.Response.Status; + +import org.apache.servicecomb.core.exception.Exceptions; +import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.junit.jupiter.api.Test; + +class TimeoutExceptionConverterTest { + private InvocationException convert() { + TimeoutException timeoutException = new TimeoutException( + "The timeout period of 30000ms has been exceeded while executing GET /xxx for server 1.1.1.1:8080"); + + return Exceptions.convert(null, timeoutException, Status.BAD_REQUEST); + } + + @Test + void should_not_leak_server_ip() { + InvocationException exception = convert(); + + assertThat(exception) + .hasMessage( + "InvocationException: code=408;msg=CommonExceptionData{code='SCB.00000000', message='Request Timeout.', dynamic={}}"); + } + + @Test + void should_log_detail() { + try (LogCollector logCollector = new LogCollector()) { + convert(); + + assertThat(logCollector.getLastEvents().getMessage().getFormattedMessage()) + .isEqualTo( + "Request timeout, Details: The timeout period of 30000ms has been exceeded while executing GET /xxx for server 1.1.1.1:8080."); + } + } +} 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 new file mode 100644 index 00000000000..2fc689efb05 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/executor/TestExecutorManager.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.executor; + +import java.util.concurrent.Executor; + +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.definition.SchemaMeta; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +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; + +public class TestExecutorManager { + @Mocked + Executor defaultExecutor; + + ExecutorManager executorManager = new ExecutorManager(); + + Environment environment = Mockito.mock(Environment.class); + + @Before + public void setup() { + executorManager.setEnvironment(environment); + } + + @After + public void teardown() { + } + + @Test + public void findExecutor_oneParam(@Mocked Executor executor, @Mocked OperationMeta operationMeta) { + new Expectations(BeanUtils.class) { + { + BeanUtils.getBean(ExecutorManager.EXECUTOR_DEFAULT); + result = executor; + } + }; + + Assertions.assertSame(executor, executorManager.findExecutor(operationMeta)); + } + + @Test + public void findExecutor_twoParam_opCfg_withoutOpDef(@Mocked Executor executor, + @Mocked OperationMeta operationMeta) { + // String schemaQualifiedName = "schemaId.opId"; + String microserviceQualifiedName = "microserviceName.schemaId.opId"; + String opBeanId = "opBeanId"; + Mockito.when(environment.getProperty( + ExecutorManager.KEY_EXECUTORS_PREFIX + microserviceQualifiedName)).thenReturn(opBeanId); + new Expectations(BeanUtils.class) { + { + operationMeta.getMicroserviceQualifiedName(); + result = microserviceQualifiedName; + BeanUtils.getBean(opBeanId); + result = executor; + } + }; + + Assertions.assertSame(executor, executorManager.findExecutor(operationMeta, null)); + } + + @Test + public void findExecutor_twoParam_opCfg_withOpDef(@Mocked Executor executor, + @Mocked Executor defExecutor, + @Mocked OperationMeta operationMeta) { + String microserviceQualifiedName = "microserviceName.schemaId.opId"; + String opBeanId = "opBeanId"; + Mockito.when(environment.getProperty( + ExecutorManager.KEY_EXECUTORS_PREFIX + microserviceQualifiedName)).thenReturn(opBeanId); + new Expectations(BeanUtils.class) { + { + operationMeta.getMicroserviceQualifiedName(); + result = microserviceQualifiedName; + BeanUtils.getBean(opBeanId); + result = executor; + } + }; + + Assertions.assertSame(executor, executorManager.findExecutor(operationMeta, defExecutor)); + } + + @Test + public void findExecutor_twoParam_schemaCfg_withOpDef(@Mocked OperationMeta operationMeta, + @Mocked Executor defExecutor) { + Assertions.assertSame(defExecutor, executorManager.findExecutor(operationMeta, defExecutor)); + } + + @Test + public void findExecutor_twoParam_schemaCfg_withoutOpDef(@Mocked Executor executor, + @Mocked OperationMeta operationMeta) { + String microserviceName = "serviceName"; + String schemaName = "schemaId"; + String opBeanId = "opBeanId"; + Mockito.when(environment.getProperty(ExecutorManager.KEY_EXECUTORS_PREFIX + microserviceName + "." + schemaName)) + .thenReturn(opBeanId); + new Expectations(BeanUtils.class) { + { + operationMeta.getSchemaId(); + result = schemaName; + operationMeta.getMicroserviceName(); + result = microserviceName; + BeanUtils.getBean(opBeanId); + result = executor; + } + }; + Assertions.assertSame(executor, executorManager.findExecutor(operationMeta, null)); + } + + @Test + public void findExecutor_twoParam_defaultCfg(@Mocked Executor executor, + @Mocked SchemaMeta schemaMeta, + @Mocked OperationMeta operationMeta) { + String beanId = "beanId"; + Mockito.when(environment.getProperty(ExecutorManager.KEY_EXECUTORS_DEFAULT)).thenReturn(beanId); + new Expectations(BeanUtils.class) { + { + BeanUtils.getBean(beanId); + result = executor; + } + }; + + 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 new file mode 100644 index 00000000000..dd95105992b --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/executor/TestGroupExecutor.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.log.LogCollector; +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() { + 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() { + } + + Environment environment = Mockito.mock(Environment.class); + + GroupExecutor groupExecutor = new GroupExecutor(environment); + + @Test + public void groupCount() { + groupExecutor.initConfig(); + Assertions.assertEquals(2, groupExecutor.groupCount); + + Mockito.when(environment.getProperty(KEY_GROUP, int.class, 2)).thenReturn(4); + groupExecutor.initConfig(); + Assertions.assertEquals(4, groupExecutor.groupCount); + } + + @Test + public void coreThreads() { + groupExecutor.initConfig(); + Assertions.assertEquals(25, groupExecutor.coreThreads); + + Mockito.when(environment.getProperty(KEY_CORE_THREADS, int.class, 25)).thenReturn(100); + groupExecutor.initConfig(); + Assertions.assertEquals(100, groupExecutor.coreThreads); + } + + @Test + public void maxIdleInSecond() { + groupExecutor.initConfig(); + Assertions.assertEquals(60, groupExecutor.maxIdleInSecond); + + Mockito.when(environment.getProperty(KEY_MAX_IDLE_SECOND, int.class, 60)).thenReturn(100); + groupExecutor.initConfig(); + Assertions.assertEquals(100, groupExecutor.maxIdleInSecond); + } + + @Test + public void maxQueueSize() { + groupExecutor.initConfig(); + Assertions.assertEquals(Integer.MAX_VALUE, groupExecutor.maxQueueSize); + Mockito.when(environment.getProperty(KEY_MAX_QUEUE_SIZE, int.class, Integer.MAX_VALUE)).thenReturn(100); + groupExecutor.initConfig(); + Assertions.assertEquals(100, groupExecutor.maxQueueSize); + } + + @Test + public void maxThreads() { + groupExecutor.initConfig(); + Assertions.assertEquals(100, groupExecutor.maxThreads); + + LogCollector collector = new LogCollector(); + 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(); + + Mockito.when(environment.getProperty(KEY_MAX_THREADS, int.class, -1)).thenReturn(300); + groupExecutor.initConfig(); + Assertions.assertEquals(300, groupExecutor.maxThreads); + } + + @Test + public void adjustCoreThreads() { + 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(); + } + + @Test + public void testGroupExecutor() { + groupExecutor.init(); + groupExecutor.execute(() -> { + }); + Map threadExecutorMap = Deencapsulation.getField(groupExecutor, "threadExecutorMap"); + Assertions.assertTrue((threadExecutorMap.size() > 0)); + + List executorList = Deencapsulation.getField(groupExecutor, "executorList"); + Assertions.assertTrue((executorList.size() > 1)); + + ReactiveExecutor oReactiveExecutor = new ReactiveExecutor(); + oReactiveExecutor.execute(() -> strThreadTest = "thread Ran"); + oReactiveExecutor.close(); + Assertions.assertEquals("thread Ran", strThreadTest); + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/executor/TestThreadPoolExecutorEx.java b/core/src/test/java/org/apache/servicecomb/core/executor/TestThreadPoolExecutorEx.java new file mode 100644 index 00000000000..7382d3793a4 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/executor/TestThreadPoolExecutorEx.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.executor; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.function.IntSupplier; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestThreadPoolExecutorEx { + private static final Logger LOGGER = LoggerFactory.getLogger(TestThreadPoolExecutorEx.class); + + static class TestTask implements Runnable { + CountDownLatch notify = new CountDownLatch(1); + + Future future; + + public void quit() throws ExecutionException, InterruptedException { + notify.countDown(); + future.get(); + } + + @Override + public void run() { + try { + notify.await(); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + } + } + + ThreadPoolExecutorEx executorEx = new ThreadPoolExecutorEx(2, 4, 2, TimeUnit.SECONDS, + new LinkedBlockingQueueEx(2), Executors.defaultThreadFactory()); + + public TestTask submitTask() { + TestTask task = new TestTask(); + task.future = executorEx.submit(task); + return task; + } + + @Test + public void schedule() throws ExecutionException, InterruptedException { + // init + Assertions.assertEquals(0, executorEx.getPoolSize()); + Assertions.assertEquals(0, executorEx.getRejectedCount()); + Assertions.assertEquals(0, executorEx.getNotFinished()); + Assertions.assertEquals(0, executorEx.getQueue().size()); + + // use core threads + TestTask t1 = submitTask(); + Assertions.assertEquals(1, executorEx.getPoolSize()); + Assertions.assertEquals(0, executorEx.getRejectedCount()); + Assertions.assertEquals(1, executorEx.getNotFinished()); + Assertions.assertEquals(0, executorEx.getQueue().size()); + + TestTask t2 = submitTask(); + Assertions.assertEquals(2, executorEx.getPoolSize()); + Assertions.assertEquals(0, executorEx.getRejectedCount()); + Assertions.assertEquals(2, executorEx.getNotFinished()); + Assertions.assertEquals(0, executorEx.getQueue().size()); + + // extend threads + TestTask t3 = submitTask(); + Assertions.assertEquals(3, executorEx.getPoolSize()); + Assertions.assertEquals(0, executorEx.getRejectedCount()); + Assertions.assertEquals(3, executorEx.getNotFinished()); + Assertions.assertEquals(0, executorEx.getQueue().size()); + + TestTask t4 = submitTask(); + Assertions.assertEquals(4, executorEx.getPoolSize()); + Assertions.assertEquals(0, executorEx.getRejectedCount()); + Assertions.assertEquals(4, executorEx.getNotFinished()); + Assertions.assertEquals(0, executorEx.getQueue().size()); + + // queue the tasks + TestTask t5 = submitTask(); + Assertions.assertEquals(4, executorEx.getPoolSize()); + Assertions.assertEquals(0, executorEx.getRejectedCount()); + Assertions.assertEquals(5, executorEx.getNotFinished()); + Assertions.assertEquals(1, executorEx.getQueue().size()); + + TestTask t6 = submitTask(); + Assertions.assertEquals(4, executorEx.getPoolSize()); + Assertions.assertEquals(0, executorEx.getRejectedCount()); + Assertions.assertEquals(6, executorEx.getNotFinished()); + Assertions.assertEquals(2, executorEx.getQueue().size()); + + // reject the task + try { + submitTask(); + } catch (RejectedExecutionException e) { + + } + Assertions.assertEquals(4, executorEx.getPoolSize()); + Assertions.assertEquals(1, executorEx.getRejectedCount()); + Assertions.assertEquals(6, executorEx.getNotFinished()); + Assertions.assertEquals(2, executorEx.getQueue().size()); + + // t1/t2/t3 finish + t1.quit(); + t2.quit(); + t3.quit(); + Assertions.assertEquals(4, executorEx.getPoolSize()); + Assertions.assertEquals(1, executorEx.getRejectedCount()); + waitForResult(3, executorEx::getNotFinished); + waitForResult(0, executorEx.getQueue()::size); + + // reuse thread + t3 = submitTask(); + Assertions.assertEquals(4, executorEx.getPoolSize()); + Assertions.assertEquals(1, executorEx.getRejectedCount()); + waitForResult(4, executorEx::getNotFinished); + waitForResult(0, executorEx.getQueue()::size); + + t3.quit(); + t4.quit(); + t5.quit(); + t6.quit(); + waitForResult(2, executorEx::getPoolSize); + executorEx.shutdown(); + } + + private void waitForResult(int expect, IntSupplier supplier) { + long max = 30000; + long waited = 0; + + for (; ; ) { + if (waited > max) { + throw new IllegalStateException("timed out waiting."); + } + int actual = supplier.getAsInt(); + if (expect == actual) { + return; + } + + LOGGER.info("waiting for thread result, expect:{}, actual: {}.", expect, actual); + try { + TimeUnit.MILLISECONDS.sleep(200); + waited += 200; + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + } + } +} 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 new file mode 100644 index 00000000000..5a5dc1cb05e --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/filter/FilterChainTest.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.apache.servicecomb.core.filter.FilterNode.buildChain; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import java.util.List; +import java.util.Vector; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.OperationConfig; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.filter.impl.ScheduleFilter; +import org.apache.servicecomb.swagger.invocation.Response; +import org.junit.BeforeClass; +import org.junit.Test; + +import mockit.Expectations; +import mockit.Mocked; + +public class FilterChainTest { + static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor(); + + static String THREAD_NAME; + + @Mocked + Invocation invocation; + + @Mocked + OperationMeta operationMeta; + + @Mocked + OperationConfig operationConfig; + + List msg = new Vector<>(); + + Filter recordThreadFilter = new AbstractFilter() { + @Override + public String getName() { + return "f2"; + } + + @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 = new AbstractFilter() { + @Override + public String getName() { + return "f1"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + throw new IllegalStateException("e1"); + } + }; + + @BeforeClass + public static void beforeClass() { + try { + THREAD_NAME = EXECUTOR.submit(() -> Thread.currentThread().getName()).get(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + private void mockInvocation() { + new Expectations() { + { + invocation.getOperationMeta(); + result = operationMeta; + + operationMeta.getExecutor(); + result = EXECUTOR; + + operationConfig.getNanoRequestWaitInPoolTimeout(anyString); + result = Long.MAX_VALUE; + } + }; + } + + @Test + public void should_switch_thread_after_schedule() throws ExecutionException, InterruptedException { + mockInvocation(); + + buildChain(recordThreadFilter, scheduler, recordThreadFilter) + .onFilter(invocation) + .get(); + + assertThat(msg).containsExactly("main", THREAD_NAME); + } + + @Test + public void should_stop_chain_when_first_filter_throw_exception() { + ExecutionException executionException = (ExecutionException) catchThrowable( + () -> buildChain(exceptionFilter, recordThreadFilter) + .onFilter(invocation) + .get()); + + assertThat(msg).isEmpty(); + assertThat(executionException.getCause()) + .isInstanceOf(IllegalStateException.class) + .hasMessage("e1"); + } + + @Test + public void should_stop_chain_when_middle_filter_throw_exception() { + ExecutionException executionException = (ExecutionException) catchThrowable( + () -> buildChain(recordThreadFilter, exceptionFilter, recordThreadFilter) + .onFilter(invocation) + .get()); + + assertThat(msg).containsExactly("main"); + assertThat(executionException.getCause()) + .isInstanceOf(IllegalStateException.class) + .hasMessage("e1"); + } +} 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 new file mode 100644 index 00000000000..4afed2d7dde --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilterTest.java @@ -0,0 +1,181 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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 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.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.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; + +public class ParameterValidatorFilterTest { + public static class BaseModel { + @NotNull(message = "can not be null") + private String name; + + public String getName() { + return name; + } + + public BaseModel setName(String name) { + this.name = name; + return this; + } + } + + public static class Model extends BaseModel { + @NotBlank(message = "can not be blank") + @JsonProperty(value = "nick-name") + private String nickName; + + public String getNickName() { + return nickName; + } + + public Model setNickName(String nickName) { + this.nickName = nickName; + return this; + } + } + + public static class Controller { + public void op(@NotNull(message = "not null") String query, @Valid Model model) { + + } + } + + ParameterValidatorFilter filter = new ParameterValidatorFilter(); + + @Mocked + Invocation invocation; + + @Mocked + SwaggerProducerOperation operation; + + 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(); + result = new Controller(); + + operation.getProducerMethod(); + result = MethodUtils.getAccessibleMethod(Controller.class, "op", String.class, Model.class); + + invocation.toProducerArguments(); + result = new Object[] {null, new Model()}; + } + }; + } + + private InvocationException getException() { + Throwable throwable = catchThrowable(() -> filter.onFilter(invocation, FilterNode.EMPTY).get()); + return Exceptions.convert(invocation, throwable, BAD_REQUEST); + } + + private CommonExceptionData getExceptionData() { + InvocationException invocationException = getException(); + return (CommonExceptionData) invocationException.getErrorData(); + } + + @Test + public void status_code_should_be_bad_request() { + InvocationException invocationException = getException(); + + assertThat(invocationException.getStatusCode()).isEqualTo(BAD_REQUEST.getStatusCode()); + } + + @Test + public void error_code_and_message_should_be_build_in_value() { + Mockito.when(environment.getProperty(KEY_CODE, String.class, DEFAULT_VALIDATE)).thenReturn(DEFAULT_VALIDATE); + CommonExceptionData errorData = getExceptionData(); + + assertThat(errorData.getCode()).isEqualTo(DEFAULT_VALIDATE); + assertThat(errorData.getMessage()).isEqualTo("invalid parameters."); + } + + @Test + public void should_allow_customize_error_code_by_configuration() { + 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"); + } + + @Test + public void should_use_json_property_value_as_property_name() { + CommonExceptionData errorData = getExceptionData(); + List details = errorData.getDynamic("validateDetail"); + + assertThat(details.stream().map(ValidateDetail::getPropertyPath)) + .contains("op.model.nick-name"); + } + + @Test + public void should_include_all_validate_detail() { + CommonExceptionData errorData = getExceptionData(); + List details = errorData.getDynamic("validateDetail"); + + assertThat(details.stream().map(ValidateDetail::getPropertyPath)) + .contains("op.query", "op.model.name", "op.model.nick-name"); + assertThat(details.stream().map(ValidateDetail::getMessage)) + .contains("not null", "can not be null", "can not be blank"); + } +} 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 new file mode 100644 index 00000000000..dd46b17d28b --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/filter/impl/ProducerOperationFilterTest.java @@ -0,0 +1,200 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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; + +import java.lang.reflect.Method; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.exception.Exceptions; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; +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.context.InvocationContext; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.junit.Test; + +import mockit.Expectations; +import mockit.Injectable; +import mockit.Mocked; +import mockit.Verifications; + +public class ProducerOperationFilterTest { + ProviderOperationFilter filter = new ProviderOperationFilter(); + + @Injectable + Invocation invocation; + + @Mocked + SwaggerProducerOperation producerOperation; + + static InvocationContext threadInvocationContext; + + public static class Controller { + public void sync() { + threadInvocationContext = ContextUtils.getInvocationContext(); + } + + public void syncException() { + throw new RuntimeExceptionWithoutStackTrace("syncException"); + } + + public CompletableFuture async() { + threadInvocationContext = ContextUtils.getInvocationContext(); + return CompletableFuture.completedFuture(null); + } + + public CompletableFuture asyncException() { + throw new RuntimeExceptionWithoutStackTrace("asyncException"); + } + } + + @Test + public void should_record_invocation_trace_time() throws NoSuchMethodException { + setInvokeSyncMethod(); + filter.onFilter(invocation, FilterNode.EMPTY); + + new Verifications() { + { + invocation.onBusinessMethodStart(); + times = 1; + + invocation.onBusinessFinish(); + times = 1; + } + }; + } + + private void setInvokeMethod(String name) throws NoSuchMethodException { + Controller instance = new Controller(); + Method method = instance.getClass().getMethod(name); + new Expectations() { + { + producerOperation.getProducerInstance(); + result = instance; + + producerOperation.getProducerMethod(); + result = method; + } + }; + } + + private void setInvokeSyncMethod() throws NoSuchMethodException { + setInvokeMethod("sync"); + } + + private void setInvokeAsyncMethod() throws NoSuchMethodException { + setInvokeMethod("async"); + } + + @Test + public void should_provide_thread_local_invocation_context_for_sync_method() throws NoSuchMethodException { + setInvokeSyncMethod(); + + filter.onFilter(invocation, FilterNode.EMPTY); + + assertThat(threadInvocationContext).isSameAs(invocation); + } + + @Test + public void should_clear_thread_local_invocation_context_after_sync_method() throws NoSuchMethodException { + setInvokeSyncMethod(); + + filter.onFilter(invocation, FilterNode.EMPTY); + + assertThat(ContextUtils.getInvocationContext()).isNull(); + } + + @Test + public void should_catch_sync_business_exception() throws NoSuchMethodException { + setInvokeMethod("syncException"); + + 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"); + } + + @Test + public void should_provide_thread_local_invocation_context_for_async_method() throws NoSuchMethodException { + setInvokeAsyncMethod(); + + filter.onFilter(invocation, FilterNode.EMPTY); + + assertThat(threadInvocationContext).isSameAs(invocation); + } + + @Test + public void should_clear_thread_local_invocation_context_after_async_method() throws NoSuchMethodException { + setInvokeAsyncMethod(); + + filter.onFilter(invocation, FilterNode.EMPTY); + + assertThat(ContextUtils.getInvocationContext()).isNull(); + } + + @Test + public void should_catch_async_business_exception() throws NoSuchMethodException { + setInvokeMethod("asyncException"); + + 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"); + } + + @Test + public void should_unify_IllegalArgumentException_message_when_convert_exception() throws NoSuchMethodException { + setInvokeSyncMethod(); + new Expectations() { + { + invocation.toProducerArguments(); + result = new Object[] {1}; + } + }; + CompletableFuture future = filter.onFilter(invocation, FilterNode.EMPTY); + + assertThat(future) + .failsWithin(Duration.ofSeconds(1)) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(IllegalArgumentException.class); + + InvocationException throwable = Exceptions + .convert(invocation, catchThrowable(future::get), INTERNAL_SERVER_ERROR); + assertThat(throwable).hasCauseInstanceOf(IllegalArgumentException.class); + CommonExceptionData data = (CommonExceptionData) throwable.getErrorData(); + assertThat(data.getMessage()).isEqualTo("Parameters not valid or types not match."); + } +} 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 new file mode 100644 index 00000000000..e2e3b2c421d --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.invocation; + +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; +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 ProducerInvocationFlowTest { + class TestFlow extends ProducerInvocationFlow { + public TestFlow(InvocationCreator invocationCreator) { + super(invocationCreator); + } + + @Override + protected Invocation sendCreateInvocationException(Throwable throwable) { + sendException = Exceptions.unwrap(throwable); + return null; + } + + @Override + protected void endResponse(Invocation invocation, Response response) { + sendInvocation = invocation; + } + } + + FilterNode filterNode = FilterNode.EMPTY; + + Throwable sendException; + + Invocation sendInvocation; + + @Injectable + Invocation invocation; + + @Mocked + MicroserviceMeta microserviceMeta; + + @Test + public void should_send_exception_response_when_failed_to_create_invocation() { + RuntimeException exception = new RuntimeExceptionWithoutStackTrace(); + TestFlow flow = new TestFlow(() -> { + throw exception; + }); + + flow.run(); + + assertThat(sendException).isSameAs(exception); + } + + private void mockFilterChain() { + new Expectations() { + { + microserviceMeta.getProviderFilterChain(); + result = filterNode; + } + }; + } + + @Test + public void should_start_invocation_when_succeed_to_create_invocation() { + mockFilterChain(); + TestFlow flow = new TestFlow(() -> completedFuture(invocation)); + + flow.run(); + + new Verifications() { + { + invocation.onStart((HttpServletRequestEx) any); + times = 1; + } + }; + } + + @Test + public void should_send_response_when_invocation_success() { + mockFilterChain(); + TestFlow flow = new TestFlow(() -> completedFuture(invocation)); + + flow.run(); + + assertThat(sendInvocation).isSameAs(invocation); + } + + @Test + public void should_finish_invocation_when_invocation_success() { + mockFilterChain(); + TestFlow flow = new TestFlow(() -> completedFuture(invocation)); + + flow.run(); + + new Verifications() { + { + invocation.onFinish((Response) any); + times = 1; + } + }; + } + + private void mockInvocationFailed() { + filterNode = new FilterNode(new AbstractFilter() { + @Override + public String getName() { + return "test"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + throw new RuntimeExceptionWithoutStackTrace(); + } + }); + mockFilterChain(); + } + + @Test + public void should_send_response_when_invocation_fail() { + mockInvocationFailed(); + TestFlow flow = new TestFlow(() -> completedFuture(invocation)); + + flow.run(); + + assertThat(sendInvocation).isSameAs(invocation); + } + + @Test + public void should_finish_invocation_when_invocation_fail() { + mockInvocationFailed(); + TestFlow flow = new TestFlow(() -> completedFuture(invocation)); + + flow.run(); + + new Verifications() { + { + invocation.onFinish((Response) any); + times = 1; + } + }; + } +} 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 new file mode 100644 index 00000000000..5b9c7040caa --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/TestInvocationStageTrace.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.core.invocation; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.InvocationRuntimeType; +import org.apache.servicecomb.core.definition.OperationMeta; +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; + +public class TestInvocationStageTrace { + Invocation invocation; + + InvocationStageTrace stageTrace; + + @Mocked + Endpoint endpoint; + + @Mocked + ReferenceConfig referenceConfig; + + @Mocked + OperationMeta operationMeta; + + @Mocked + InvocationRuntimeType invocationRuntimeType; + + Map args = new HashMap<>(); + + static long nanoTime = 0; + + @BeforeClass + public static void classSetup() { + new MockUp() { + @Mock + long nanoTime() { + return nanoTime; + } + }; + } + + @Test + public void consumer() { + invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, args); + stageTrace = new InvocationStageTrace(invocation); + + stageTrace.startCreateInvocation(1); + nanoTime = 2; + stageTrace.finishCreateInvocation(); + stageTrace.startConsumerConnection(); + nanoTime = 3; + stageTrace.finishConsumerConnection(); + stageTrace.startConsumerEncodeRequest(); + nanoTime = 4; + stageTrace.finishConsumerEncodeRequest(); + stageTrace.startConsumerSendRequest(); + nanoTime = 5; + stageTrace.finishConsumerSendRequest(); + stageTrace.startWaitResponse(); + nanoTime = 6; + stageTrace.finishWaitResponse(); + stageTrace.startConsumerDecodeResponse(); + nanoTime = 7; + stageTrace.finishConsumerDecodeResponse(); + + stageTrace.finish(); + + 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 + public void producer() { + invocation = new Invocation(endpoint, operationMeta, args); + stageTrace = new InvocationStageTrace(invocation); + + stageTrace.startCreateInvocation(1); + nanoTime = 2; + stageTrace.finishCreateInvocation(); + stageTrace.startProviderDecodeRequest(); + nanoTime = 3; + stageTrace.finishProviderDecodeRequest(); + stageTrace.startProviderQueue(); + nanoTime = 4; + stageTrace.finishProviderQueue(); + stageTrace.startBusinessExecute(); + nanoTime = 5; + stageTrace.finishBusinessExecute(); + stageTrace.startProviderEncodeResponse(); + nanoTime = 6; + stageTrace.finishProviderEncodeResponse(); + stageTrace.startProviderSendResponse(); + nanoTime = 7; + stageTrace.finishProviderSendResponse(); + + stageTrace.finish(); + + 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(); + + stageTrace.startCreateInvocation(1); + nanoTime = 2; + stageTrace.finishCreateInvocation(); + stageTrace.startProviderDecodeRequest(); + nanoTime = 3; + stageTrace.finishProviderDecodeRequest(); + stageTrace.startConsumerConnection(); + nanoTime = 4; + stageTrace.finishConsumerConnection(); + stageTrace.startConsumerEncodeRequest(); + nanoTime = 5; + stageTrace.finishConsumerEncodeRequest(); + stageTrace.startConsumerSendRequest(); + nanoTime = 6; + stageTrace.finishConsumerSendRequest(); + stageTrace.startWaitResponse(); + nanoTime = 7; + stageTrace.finishWaitResponse(); + stageTrace.startConsumerDecodeResponse(); + nanoTime = 8; + stageTrace.finishConsumerDecodeResponse(); + stageTrace.startProviderEncodeResponse(); + nanoTime = 10; + stageTrace.finishProviderEncodeResponse(); + stageTrace.startProviderSendResponse(); + nanoTime = 11; + stageTrace.finishProviderSendResponse(); + stageTrace.finish(); + + 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 new file mode 100644 index 00000000000..b3c0733bb48 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.invocation.endpoint; + +import static com.google.common.collect.ImmutableMap.of; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Map; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.foundation.common.Holder; +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import io.swagger.v3.oas.models.OpenAPI; + +public class EndpointTest { + public interface TestSchema { + void say(Endpoint endpoint); + } + + @Test + void should_ignore_endpoint_when_generate_swagger() { + SwaggerGenerator generator = SwaggerGenerator.create(TestSchema.class); + OpenAPI swagger = generator.generate(); + + 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 + void should_set_endpoint_to_invocation_when_map_arguments() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + SwaggerConsumer consumer = environment + .createConsumer(TestSchema.class, SwaggerGenerator.generate(TestSchema.class)); + SwaggerConsumerOperation operation = consumer.findOperation("say"); + + Endpoint endpoint = new Endpoint(Mockito.mock(Transport.class), null); + Invocation invocation = Mockito.mock(Invocation.class); + Holder holder = new Holder<>(); + Mockito + .doAnswer(invocationOnMock -> { + holder.value = invocationOnMock.getArguments()[0]; + return null; + }) + .when(invocation) + .setEndpoint(Mockito.any()); + + Map argsMap = of("endpoint", endpoint); + operation.getArgumentsMapper().invocationArgumentToSwaggerArguments(invocation, argsMap); + + assertThat(holder.value).isSameAs(endpoint); + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtilsTest.java b/core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtilsTest.java new file mode 100644 index 00000000000..43cc21ba478 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtilsTest.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.invocation.endpoint; + +import static org.apache.servicecomb.core.invocation.endpoint.EndpointUtils.formatFromUri; +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +class EndpointUtilsTest { + @Nested + class Http { + @Test + void should_convert_without_port() { + assertThat(formatFromUri("http://host")).isEqualTo("rest://host:80"); + } + + @Test + void should_convert_with_port() { + assertThat(formatFromUri("http://host:8080")).isEqualTo("rest://host:8080"); + } + + @Test + void should_not_lost_query_parameters() { + assertThat(formatFromUri("http://host?q1=v1&q2=v2")).isEqualTo("rest://host:80?q1=v1&q2=v2"); + } + } + + @Nested + class Https { + @Test + void should_convert_without_port() { + assertThat(formatFromUri("https://host")).isEqualTo("rest://host:443?sslEnabled=true"); + } + + @Test + void should_convert_with_port() { + assertThat(formatFromUri("https://host:8443")).isEqualTo("rest://host:8443?sslEnabled=true"); + } + + @Test + void should_not_lost_query_parameters() { + assertThat(formatFromUri("https://host?q1=v1&q2=v2")).isEqualTo("rest://host:443?q1=v1&q2=v2&sslEnabled=true"); + } + } + + @Nested + class H2C { + @Test + void should_convert_without_port() { + assertThat(formatFromUri("h2c://host")).isEqualTo("rest://host:80?protocol=http2"); + } + + @Test + void should_convert_with_port() { + assertThat(formatFromUri("h2c://host:8080")).isEqualTo("rest://host:8080?protocol=http2"); + } + + @Test + void should_not_lost_query_parameters() { + assertThat(formatFromUri("h2c://host?q1=v1&q2=v2")).isEqualTo("rest://host:80?q1=v1&q2=v2&protocol=http2"); + } + } + + @Nested + class H2 { + @Test + void should_convert_without_port() { + assertThat(formatFromUri("h2://host")).isEqualTo("rest://host:443?sslEnabled=true&protocol=http2"); + } + + @Test + void should_convert_with_port() { + assertThat(formatFromUri("h2://host:8443")).isEqualTo("rest://host:8443?sslEnabled=true&protocol=http2"); + } + + @Test + void should_not_lost_query_parameters() { + assertThat(formatFromUri("h2://host?q1=v1&q2=v2")) + .isEqualTo("rest://host:443?q1=v1&q2=v2&sslEnabled=true&protocol=http2"); + } + } + + @Nested + class NotProvideScheme { + @Test + void should_set_scheme_to_h2c() { + assertThat(formatFromUri("host")).isEqualTo("rest://host:80?protocol=http2"); + } + + @Test + void should_not_lost_query_parameters() { + assertThat(formatFromUri("host?q1=v1&q2=v2")).isEqualTo("rest://host:80?q1=v1&q2=v2&protocol=http2"); + } + } + + @Nested + class UnknownScheme { + @Test + void should_not_change() { + assertThat(formatFromUri("abc://host:123?q1=v1")).isEqualTo("abc://host:123?q1=v1"); + } + } +} 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 new file mode 100644 index 00000000000..58de7e9d2a0 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategyTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.core.invocation.timeout; + +import static org.apache.servicecomb.core.invocation.timeout.PassingTimeStrategy.CHAIN_START_TIME; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.test.scaffolding.time.MockClock; +import org.junit.jupiter.api.Test; + +import com.google.common.collect.ImmutableMap; + +class PassingTimeStrategyTest { + PassingTimeStrategy strategy = new PassingTimeStrategy(); + + @Test + void should_init_when_start_as_first_node_of_a_process_but_not_first_of_a_chain() { + Invocation invocation = new Invocation(); + invocation.setContext(ImmutableMap.of(CHAIN_START_TIME, "10")); + + strategy.start(invocation); + + assertThat(invocation.getContext(CHAIN_START_TIME)).isEqualTo("10"); + assertThat(invocation.getLocalContext(CHAIN_START_TIME)).isEqualTo(10L); + } + + @Test + void should_do_nothing_when_start_not_as_first_node_of_a_process() { + Invocation invocation = new Invocation(); + invocation.setContext(ImmutableMap.of()); + invocation.setLocalContext(ImmutableMap.of(CHAIN_START_TIME, 10L)); + + Throwable throwable = catchThrowable(() -> strategy.start(invocation)); + + assertThat(throwable).isNull(); + } + + @Test + void should_calc_elapsed_time_as_passing_time() { + Invocation invocation = new Invocation(); + invocation.addLocalContext(CHAIN_START_TIME, 10L); + strategy.setClock(new MockClock(100L)); + + long elapsedNanoTime = strategy.calculateElapsedNanoTime(invocation); + + assertThat(elapsedNanoTime).isEqualTo(TimeUnit.MILLISECONDS.toNanos(90)); + } +} 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 new file mode 100644 index 00000000000..05d744e15db --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategyTest.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.core.invocation.timeout; + +import static org.apache.servicecomb.core.invocation.timeout.ProcessingTimeStrategy.CHAIN_PROCESSING; +import static org.apache.servicecomb.core.invocation.timeout.ProcessingTimeStrategy.CHAIN_START_TIME; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.test.scaffolding.time.MockTicker; +import org.junit.jupiter.api.Test; + +import com.google.common.collect.ImmutableMap; + +class ProcessingTimeStrategyTest { + ProcessingTimeStrategy strategy = new ProcessingTimeStrategy(); + + @Test + void should_do_nothing_when_not_first_node_of_a_process() { + Invocation invocation = new Invocation(); + invocation.setLocalContext(ImmutableMap.of( + CHAIN_START_TIME, 10L, + CHAIN_PROCESSING, 0L + )); + + Throwable throwable = catchThrowable(() -> strategy.start(invocation)); + + assertThat(throwable).isNull(); + } + + @Test + void should_calc_elapsed_time_as_processing_time() { + strategy.setTicker(new MockTicker(50L)); + + Invocation invocation = new Invocation(); + invocation.addLocalContext(CHAIN_START_TIME, 10L); + invocation.addLocalContext(CHAIN_PROCESSING, 20L); + + long elapsedNanoTime = strategy.calculateElapsedNanoTime(invocation); + + assertThat(elapsedNanoTime).isEqualTo(60L); + } + + @Test + void should_update_processing_time_before_send() { + strategy = new ProcessingTimeStrategy() { + @Override + public void checkTimeout(Invocation invocation) { + + } + }; + strategy.setTicker(new MockTicker(50L)); + + Invocation invocation = new Invocation(); + invocation.addLocalContext(CHAIN_START_TIME, 10L); + invocation.addLocalContext(CHAIN_PROCESSING, 20L); + + strategy.beforeSendRequest(invocation); + + assertThat(invocation.getContext(CHAIN_PROCESSING)).isEqualTo("60"); + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/provider/Person.java b/core/src/test/java/org/apache/servicecomb/core/provider/Person.java new file mode 100644 index 00000000000..99994b1e20b --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/provider/Person.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.core.provider; + +public class Person { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} 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 new file mode 100644 index 00000000000..3b3e56092c3 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.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.core.provider.consumer; + +import org.junit.jupiter.api.Test; + +public class TestInvokerUtils { + @Test + public void testRetryInvocation503() { + + } +} 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 new file mode 100644 index 00000000000..e327759eff2 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerBootListener.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.producer; + +import java.util.Arrays; +import java.util.concurrent.Executor; + +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.foundation.test.scaffolding.log.LogCollector; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestProducerBootListener { + ProducerBootListener producerBootListener = new ProducerBootListener(); + + @Test + public void onBootEvent_notClose() { + BootEvent event = new BootEvent(); + event.setEventType(EventType.BEFORE_CLOSE); + + // should not throw exception + producerBootListener.onBootEvent(event); + } + + @Test + public void onBootEvent_close_unknown() { + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); + OperationMeta op = Mockito.mock(OperationMeta.class); + + Executor executor = new UnCloseableExecutor(); + 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(); + event.setScbEngine(scbEngine); + event.setEventType(EventType.AFTER_CLOSE); + + producerBootListener.onBootEvent(event); + + Assertions.assertEquals( + "Executor org.apache.servicecomb.core.provider.producer.TestProducerBootListener$UnCloseableExecutor " + + "do not support close or shutdown, it may block service shutdown.", + logCollector.getLastEvents().getMessage().getFormattedMessage()); + } + } + + public static class UnCloseableExecutor implements Executor { + @Override + public void execute(Runnable command) { + command.run(); + } + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerMeta.java b/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerMeta.java new file mode 100644 index 00000000000..1c481c7189b --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerMeta.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.core.provider.producer; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestProducerMeta { + @Test + public void test1() { + Object instance = new Object(); + ProducerMeta meta = new ProducerMeta("id", instance); + Assertions.assertEquals("id", meta.getSchemaId()); + Assertions.assertEquals(instance, meta.getInstance()); + } + + @Test + public void test2() { + ProducerMeta meta = new ProducerMeta(); + meta.setSchemaId("id1"); + Assertions.assertEquals("id1", meta.getSchemaId()); + + meta.setInstance(1); + Assertions.assertEquals(1, meta.getInstance()); + } +} 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 new file mode 100644 index 00000000000..f36229b0eea --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/provider/producer/TestProducerProviderManager.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.core.provider.producer; + +import org.junit.jupiter.api.Test; + +public class TestProducerProviderManager { + @Test + public void allowedNoProvider() { + // must not throw exception + new ProducerProviderManager(null).init(); + } +} 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 new file mode 100644 index 00000000000..cc79ba2f684 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/registry/discovery/TestEndpointDiscoveryFilter.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.core.registry.discovery; + +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.transport.TransportManager; +import org.apache.servicecomb.registry.discovery.DiscoveryContext; +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(); + + Invocation invocation; + + @BeforeEach + public void setup() { + + } + + @AfterEach + public void teardown() { + } + + @Test + public void getOrder() { + Assertions.assertEquals(Short.MAX_VALUE, filter.getOrder()); + } + + @Test + public void getTransportName() { + 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() { + 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() { + 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(); + + 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, CoreConst.RESTFUL, endpoint, instance); + Assertions.assertSame(transport, ep.getTransport()); + Assertions.assertSame(address, ep.getAddress()); + Assertions.assertSame(instance, ep.getMicroserviceInstance()); + Assertions.assertEquals(endpoint, ep.getEndpoint()); + } +} 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 new file mode 100644 index 00000000000..e858dd954c5 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/tracing/BraveTraceIdGeneratorTest.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.core.tracing; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class BraveTraceIdGeneratorTest { + + @Test + public void generateStringId() { + TraceIdGenerator traceIdGenerator = new BraveTraceIdGenerator(); + Assertions.assertNotEquals(traceIdGenerator.generate(), traceIdGenerator.generate()); + + String traceId = traceIdGenerator.generate(); + try { + Long.parseLong(traceId, 16); + } catch (NumberFormatException e) { + Assertions.fail("wrong traceId format: " + traceId); + } + } +} 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 new file mode 100644 index 00000000000..65c44b12560 --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.apache.servicecomb.core.transport.AbstractTransport.PUBLISH_ADDRESS; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Collections; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.common.net.IpPort; +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 TestAbstractTransport { + Environment environment = Mockito.mock(Environment.class); + + static class MyAbstractTransport extends AbstractTransport { + + @Override + public String getName() { + return "my"; + } + + @Override + public boolean init() { + return true; + } + } + + @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 { + 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()); + } + + @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()); + } + + @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()); + } + + @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()); + Assertions.assertEquals("127.0.0.1", ((IpPort) transport.parseAddress("my://127.0.0.1:9090")).getHostOrIp()); + transport.setListenAddressWithoutSchema("0.0.0.0:9090"); + Assertions.assertNotEquals("my://127.0.0.1:9090", transport.getEndpoint().getEndpoint()); + transport.setListenAddressWithoutSchema(null); + Assertions.assertNull(transport.getEndpoint().getEndpoint()); + Assertions.assertNull(transport.parseAddress(null)); + } + + @Test + public void testMyAbstractTransportException() { + MyAbstractTransport transport = new MyAbstractTransport(); + 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 new file mode 100644 index 00000000000..89b591beecb --- /dev/null +++ b/core/src/test/java/org/apache/servicecomb/core/transport/TestTransportManager.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestTransportManager { + @Test + public void testTransportManagerInitFail() + throws Exception { + 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); + + manager.init(scbEngine); + Assertions.assertEquals(manager.findTransport("test"), transport); + } + + @Test + 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(); + manager.addTransportsBeforeInit(transports); + + manager.init(scbEngine); + Assertions.assertEquals(manager.findTransport("test"), transport); + } + + @Test + 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, 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(t21, groups.get("t2").get(0)); + Assertions.assertEquals(t22, groups.get("t2").get(1)); + } + + @Test + 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); + + try { + manager.checkTransportGroup(group); + Assertions.fail("must throw exception"); + } catch (ServiceCombException e) { + Assertions.assertTrue(e.getMessage().contains("have the same order")); + } + } + + @Test + 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); + + try { + manager.checkTransportGroup(group); + } catch (ServiceCombException e) { + Assertions.fail("must not throw exception: " + e.getMessage()); + } + } + + @Test + 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); + + Assertions.assertEquals(t1, manager.chooseOneTransport(group)); + } + + @Test + 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); + + Assertions.assertEquals(t1, manager.chooseOneTransport(group)); + } + + @Test + 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); + + try { + manager.chooseOneTransport(group); + Assertions.fail("must throw exception"); + } catch (ServiceCombException e) { + Assertions.assertEquals("all transport named t refused to init.", e.getMessage()); + } + } +} diff --git a/core/src/test/resources/META-INF/spring/cse.bean.xml b/core/src/test/resources/META-INF/spring/cse.bean.xml index e531331f649..a9774182816 100644 --- a/core/src/test/resources/META-INF/spring/cse.bean.xml +++ b/core/src/test/resources/META-INF/spring/cse.bean.xml @@ -1,12 +1,13 @@ - - - - - - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> - - - \ No newline at end of file + + + diff --git a/core/src/test/resources/config/config.cse.inc.xml b/core/src/test/resources/config/config.cse.inc.xml deleted file mode 100644 index 4dedd85ae2b..00000000000 --- a/core/src/test/resources/config/config.cse.inc.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - classpath*:config/cse.properties - - \ No newline at end of file diff --git a/core/src/test/resources/config/cse.handler.xml b/core/src/test/resources/config/cse.handler.xml deleted file mode 100644 index 5a281603762..00000000000 --- a/core/src/test/resources/config/cse.handler.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - \ No newline at end of file diff --git a/core/src/test/resources/config/cse.properties b/core/src/test/resources/config/cse.properties deleted file mode 100644 index 404a917b764..00000000000 --- a/core/src/test/resources/config/cse.properties +++ /dev/null @@ -1,23 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -cse.demo.client.thread=100 -cse.demo.client.transport=highway -cse.demo.client.mode=reactive -cse.handler.chain.Provider.default=test -cse.handler.chain.Consumer.default=test -cse.handler.chain.Provider.service.test=test -cse.handler.chain.Consumer.service.test=test \ No newline at end of file diff --git a/core/src/test/resources/config/log4j.demo.properties b/core/src/test/resources/config/log4j.demo.properties deleted file mode 100644 index 14aec88f691..00000000000 --- a/core/src/test/resources/config/log4j.demo.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=../logs/ -paas.logs.file=cse.log - -log4j.rootLogger=INFO,paas,stdout \ No newline at end of file diff --git a/core/src/test/resources/log4j.properties b/core/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/core/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/core/src/test/resources/log4j2.xml b/core/src/test/resources/log4j2.xml new file mode 100644 index 00000000000..8cee07220ec --- /dev/null +++ b/core/src/test/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/core/src/test/resources/microservice.yaml b/core/src/test/resources/microservice.yaml index dbe573d417e..b17313cd44c 100644 --- a/core/src/test/resources/microservice.yaml +++ b/core/src/test/resources/microservice.yaml @@ -1,24 +1,33 @@ -APPLICATION_ID: pojotest +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +APPLICATION_ID: app service_description: name: perfClient version: 0.0.1 -cse: +servicecomb: service: registry: - address: http://127.0.0.1:9980 - rest: - client: - thread-count: 10 - connection-pool-per-thread: 1 - server: - thread-count: 10 - highway: - thread-count: 1 - connection-pool-per-thread: 10 + address: http://127.0.0.1:30100 handler: chain: Consumer: default: simpleLB - metrics: - cycle: - ms: 1000 +zq: + - tlist: a + - tlist: b + - tlist: 1 diff --git a/core/src/test/resources/test/test/microservice.yaml b/core/src/test/resources/test/test/microservice.yaml index 83630c255d5..7ec47b07b63 100644 --- a/core/src/test/resources/test/test/microservice.yaml +++ b/core/src/test/resources/test/test/microservice.yaml @@ -1,11 +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. +## --------------------------------------------------------------------------- + service_description: name: default version: 0.0.1 - propertyExtentedClass: io.servicecomb.serviceregistry.MicroServicePropertyExtendedStub -cse: + propertyExtendedClass: org.apache.servicecomb.serviceregistry.MicroServicePropertyExtendedStub +servicecomb: service: registry: - address: http://127.0.0.1:9980 + address: http://127.0.0.1:30100 client: httpVersion: HTTP_1_1 workerPoolSize: 1 diff --git a/core/src/test/resources/test/test/schema.yaml b/core/src/test/resources/test/test/schema.yaml index 9ee0810b0c4..1fc4b15f949 100644 --- a/core/src/test/resources/test/test/schema.yaml +++ b/core/src/test/resources/test/test/schema.yaml @@ -1,8 +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. +## --------------------------------------------------------------------------- + --- swagger: "2.0" info: version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" + title: "swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema" x-java-interface: "gen.cse.ms.ut.SchemaIntf" basePath: "/Schema" consumes: @@ -20,7 +37,7 @@ paths: schema: $ref: "#/definitions/testintBody" responses: - 200: + "200": description: "response of 200" definitions: testintBody: diff --git a/coverage-reports/pom.xml b/coverage-reports/pom.xml index 910abdf6871..97f795fcbf5 100644 --- a/coverage-reports/pom.xml +++ b/coverage-reports/pom.xml @@ -1,12 +1,13 @@ - - io.servicecomb + org.apache.servicecomb java-chassis-parent - 0.3.0-SNAPSHOT - ../parent + 3.4.0-SNAPSHOT + ../parents/default 4.0.0 @@ -30,223 +30,477 @@ jar + + + + org.apache.servicecomb + config-center-client + + + org.apache.servicecomb + config-clients-common + + + org.apache.servicecomb + config-kie-client + + + org.apache.servicecomb + dashboard-client + + + org.apache.servicecomb + http-client-common + + + org.apache.servicecomb + service-center-client + + + + + org.apache.servicecomb + common-access-log + + + org.apache.servicecomb + common-protobuf + - io.servicecomb + org.apache.servicecomb + common-rest + + + + + org.apache.servicecomb + java-chassis-core + + + + + org.apache.servicecomb + config-apollo + + + org.apache.servicecomb + config-cc + + + org.apache.servicecomb + config-kie + + + org.apache.servicecomb + config-nacos + + + + + org.apache.servicecomb + edge-core + + + + + org.apache.servicecomb foundation-common - io.servicecomb + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-metrics + + + org.apache.servicecomb + foundation-protobuf + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + foundation-spi + + + org.apache.servicecomb foundation-ssl - io.servicecomb - foundation-config + org.apache.servicecomb + foundation-test-scaffolding - io.servicecomb + org.apache.servicecomb foundation-vertx + + - io.servicecomb - foundation-metrics + org.apache.servicecomb + servicecomb-governance + + - io.servicecomb - java-chassis-core + org.apache.servicecomb + handler-fault-injection - io.servicecomb - handler-bizkeeper + org.apache.servicecomb + handler-flowcontrol-qps - io.servicecomb + org.apache.servicecomb handler-loadbalance - io.servicecomb - handler-flowcontrol-qps + org.apache.servicecomb + handler-publickey-auth - io.servicecomb - common-rest + org.apache.servicecomb + handler-router - io.servicecomb - transport-rest-client + org.apache.servicecomb + handler-tracing-zipkin + + - io.servicecomb - transport-rest-vertx + org.apache.servicecomb + metrics-core - io.servicecomb - transport-rest-servlet + org.apache.servicecomb + metrics-prometheus + + - io.servicecomb - common-protobuf + org.apache.servicecomb + provider-jaxrs - io.servicecomb - common-javassist + org.apache.servicecomb + provider-pojo - io.servicecomb + org.apache.servicecomb + provider-rest-common + + + org.apache.servicecomb + provider-springmvc + + + + + org.apache.servicecomb + registry-lightweight + + + org.apache.servicecomb + registry-local + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + registry-zero-config + + + + + org.apache.servicecomb + solution-basic + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-servlet + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + + + org.apache.servicecomb + servicestage + + + + org.apache.servicecomb + dashboard + + + org.apache.servicecomb + darklaunch + + + + + org.apache.servicecomb swagger-generator-core - io.servicecomb + org.apache.servicecomb swagger-generator-jaxrs - io.servicecomb + org.apache.servicecomb + swagger-generator-spring-data + + + org.apache.servicecomb swagger-generator-springmvc + - io.servicecomb + org.apache.servicecomb swagger-invocation-core - io.servicecomb + org.apache.servicecomb swagger-invocation-jaxrs - io.servicecomb + org.apache.servicecomb swagger-invocation-springmvc - io.servicecomb - service-registry + org.apache.servicecomb + swagger-invocation-validator + + - io.servicecomb - provider-pojo + org.apache.servicecomb + tracing-common - io.servicecomb - provider-rest-common + org.apache.servicecomb + tracing-zipkin + + - io.servicecomb - provider-jaxrs + org.apache.servicecomb + transport-common - io.servicecomb - provider-springmvc + org.apache.servicecomb + transport-highway - io.servicecomb - transport-highway + org.apache.servicecomb + transport-rest-client - io.servicecomb - spring-boot-starter-configuration + org.apache.servicecomb + transport-rest-servlet - io.servicecomb - spring-boot-starter-provider + org.apache.servicecomb + transport-rest-vertx + + + - io.servicecomb - spring-boot-starter-transport + org.apache.servicecomb.demo + crossapp-client + ${project.version} + test - io.servicecomb - spring-boot-starter-discovery + org.apache.servicecomb.demo + crossapp-server + ${project.version} + test + - io.servicecomb - handler-tracing-zipkin + org.apache.servicecomb.demo + authentication + ${project.version} + test - io.servicecomb.demo - pojo-client - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + business-1-0-0 + ${project.version} + test - io.servicecomb.demo - pojo-server - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + business-1-1-0 + ${project.version} + test + + + org.apache.servicecomb.demo + business-2-0-0 + ${project.version} + test + + + org.apache.servicecomb.demo + consumer + ${project.version} + test + + + org.apache.servicecomb.demo + edge-service + ${project.version} + test + + + org.apache.servicecomb.demo + model + ${project.version} + test + - io.servicecomb.demo + org.apache.servicecomb.demo jaxrs-client - 0.3.0-SNAPSHOT + ${project.version} + test - io.servicecomb.demo + org.apache.servicecomb.demo jaxrs-server - 0.3.0-SNAPSHOT + ${project.version} + test + - io.servicecomb.demo - springmvc-client - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + demo-local-registry-client + ${project.version} + test - io.servicecomb.demo - springmvc-server - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + demo-local-registry-server + ${project.version} + test + - io.servicecomb.demo - crossapp-client - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + demo-multi-registries-client + ${project.version} + test - io.servicecomb.demo - crossapp-server - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + demo-multi-registries-server + ${project.version} + test + + + + org.apache.servicecomb.demo + pojo-client + ${project.version} + test + + + org.apache.servicecomb.demo + pojo-server + ${project.version} + test + - io.servicecomb.demo - demo-spring-boot-jaxrs-server - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + demo-register-url-prefix-client + ${project.version} + test - io.servicecomb.demo - demo-spring-boot-jaxrs-client - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + demo-register-url-prefix-server + ${project.version} + test + + + org.apache.servicecomb.demo + demo-schema + ${project.version} + test + + + - io.servicecomb.demo + org.apache.servicecomb.demo demo-spring-boot-pojo-client - 0.3.0-SNAPSHOT + ${project.version} + test - io.servicecomb.demo + org.apache.servicecomb.demo demo-spring-boot-pojo-server - 0.3.0-SNAPSHOT + ${project.version} + test - io.servicecomb.demo - demo-spring-boot-discovery-server - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + demo-spring-boot-springmvc-client + ${project.version} + test - io.servicecomb.demo - demo-spring-boot-discovery-client - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + demo-spring-boot-springmvc-server + ${project.version} + test + - io.servicecomb.demo - demo-spring-boot-zuul-proxy - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + springmvc-client + ${project.version} + test + + + org.apache.servicecomb.demo + springmvc-server + ${project.version} + test + - io.servicecomb.tests - jaxrs-tests - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + demo-zeroconfig-registry-client + ${project.version} + test - io.servicecomb.tests - springmvc-tests - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + demo-zeroconfig-registry-server + ${project.version} + test - io.servicecomb.tests - tracing-tests - 0.3.0-SNAPSHOT + org.apache.servicecomb.demo + demo-zeroconfig-registry-tests + ${project.version} + test diff --git a/demo/README.md b/demo/README.md new file mode 100644 index 00000000000..18153ce7a57 --- /dev/null +++ b/demo/README.md @@ -0,0 +1,9 @@ +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/assembly/assembly.xml b/demo/assembly/assembly.xml index b5af3432fe6..2683395b554 100644 --- a/demo/assembly/assembly.xml +++ b/demo/assembly/assembly.xml @@ -1,27 +1,46 @@ + + - bin - - dir - - false - - - / - false - - ${artifact} - - - - /lib - false - - ${artifact} - - - - \ No newline at end of file + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> + bin + + dir + + false + + + + /maven + ${project.build.directory} + + **/*.jar + + + + /maven + ${project.build.directory}/classes + + certificates/* + + + + + 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 b2a78a4cdc1..fa2e051cd0f 100644 --- a/demo/demo-crossapp/crossapp-client/pom.xml +++ b/demo/demo-crossapp/crossapp-client/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-crossapp - 0.3.0-SNAPSHOT - - crossapp-client + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + + org.apache.servicecomb.demo + demo-crossapp + 3.4.0-SNAPSHOT + + crossapp-client + Java Chassis::Demo::CrossApp::Client - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-pojo - - - io.servicecomb - provider-springmvc - - + + - - io.servicecomb.demo.crossapp.CrossappClient - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-run-config - 0.3.0-SNAPSHOT - - - - - - + + org.apache.servicecomb.demo.crossapp.CrossappClient + - - - docker - - crossapp-server - -Dcse.rest.address=0.0.0.0:8080 - - - - - io.fabric8 - docker-maven-plugin - - - - - + + + docker + + 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 + + + + + diff --git a/demo/demo-crossapp/crossapp-client/src/main/java/io/servicecomb/demo/crossapp/CrossappBootListener.java b/demo/demo-crossapp/crossapp-client/src/main/java/io/servicecomb/demo/crossapp/CrossappBootListener.java deleted file mode 100644 index e4f7e82c570..00000000000 --- a/demo/demo-crossapp/crossapp-client/src/main/java/io/servicecomb/demo/crossapp/CrossappBootListener.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.crossapp; - -import org.springframework.stereotype.Component; - -import io.servicecomb.core.BootListener; -import io.servicecomb.core.definition.loader.DynamicSchemaLoader; - -@Component -public class CrossappBootListener implements BootListener { - @Override - public void onBootEvent(BootEvent event) { - if (EventType.BEFORE_CONSUMER_PROVIDER.equals(event.getEventType())) { - // 动态注册schemas目录下面的契约到当前服务 - DynamicSchemaLoader.INSTANCE.registerSchemas("appServer:appService", "classpath*:schemas/*.yaml"); - } - } -} diff --git a/demo/demo-crossapp/crossapp-client/src/main/java/io/servicecomb/demo/crossapp/CrossappClient.java b/demo/demo-crossapp/crossapp-client/src/main/java/io/servicecomb/demo/crossapp/CrossappClient.java deleted file mode 100644 index a2e016fbbf7..00000000000 --- a/demo/demo-crossapp/crossapp-client/src/main/java/io/servicecomb/demo/crossapp/CrossappClient.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.crossapp; - -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; - -import io.servicecomb.core.provider.consumer.InvokerUtils; -import io.servicecomb.demo.TestMgr; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.provider.pojo.RpcReference; -import io.servicecomb.provider.springmvc.reference.RestTemplateBuilder; - -@Component -public class CrossappClient { - @RpcReference(microserviceName = "appServer:appService", schemaId = "helloworld") - private static HelloWorld helloWorld; - - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - - run(); - - TestMgr.summary(); - } - - public static void run() { - Object result = InvokerUtils.syncInvoke("appServer:appService", "helloworld", "sayHello", null); - TestMgr.check("hello world", result); - - RestTemplate restTemplate = RestTemplateBuilder.create(); - result = restTemplate.getForObject("cse://appServer:appService/helloworld/hello", String.class); - TestMgr.check("hello world", result); - - result = helloWorld.sayHello(); - TestMgr.check("hello world", result); - } -} diff --git a/demo/demo-crossapp/crossapp-client/src/main/java/io/servicecomb/demo/crossapp/HelloWorld.java b/demo/demo-crossapp/crossapp-client/src/main/java/io/servicecomb/demo/crossapp/HelloWorld.java deleted file mode 100644 index 405269dbaaf..00000000000 --- a/demo/demo-crossapp/crossapp-client/src/main/java/io/servicecomb/demo/crossapp/HelloWorld.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.crossapp; - -public interface HelloWorld { - String sayHello(); -} 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 new file mode 100644 index 00000000000..7d78648963a --- /dev/null +++ b/demo/demo-crossapp/crossapp-client/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappClient.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.demo.crossapp; + +import java.util.Collections; +import java.util.TreeSet; + +import org.apache.servicecomb.core.provider.consumer.InvokerUtils; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +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 +public class CrossappClient { + @RpcReference(microserviceName = "appServer:appService", schemaId = "helloworld") + private static HelloWorld helloWorld; + + public static void main(String[] args) throws Exception { + new SpringApplicationBuilder(CrossappClient.class).web(WebApplicationType.NONE).run(args); + + run(); + } + + public static void run() { + System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); + + String result = InvokerUtils.syncInvoke("appServer:appService", "helloworld", + "sayHello", null, String.class); + TestMgr.check("hello world", result); + + 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); + TestMgr.check("hello world", result); + + result = helloWorld.sayHello(); + TestMgr.check("hello world", result); + + testCorsHandlerOptions(); + testCorsHandlerGet(); + + TestMgr.summary(); + System.setProperty("sun.net.http.allowRestrictedHeaders", "false"); + } + + private static void testCorsHandlerOptions() { + // first domain + RestOperations springRestTemplate = new RestTemplate(); + MultiValueMap requestHeaders = new LinkedMultiValueMap<>(); + 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.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("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/java/org/apache/servicecomb/demo/crossapp/HelloWorld.java b/demo/demo-crossapp/crossapp-client/src/main/java/org/apache/servicecomb/demo/crossapp/HelloWorld.java new file mode 100644 index 00000000000..14347cb3846 --- /dev/null +++ b/demo/demo-crossapp/crossapp-client/src/main/java/org/apache/servicecomb/demo/crossapp/HelloWorld.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.crossapp; + +public interface HelloWorld { + String sayHello(); +} diff --git a/demo/demo-crossapp/crossapp-client/src/main/resources/log4j2.xml b/demo/demo-crossapp/crossapp-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-crossapp/crossapp-client/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 6412bc6aaca..1728b196574 100644 --- a/demo/demo-crossapp/crossapp-client/src/main/resources/microservice.yaml +++ b/demo/demo-crossapp/crossapp-client/src/main/resources/microservice.yaml @@ -1,11 +1,27 @@ -APPLICATION_ID: appClient -service_description: - name: appClientService - version: 0.0.1 -cse: +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: - registry: - address: http://127.0.0.1:9980 + application: appClient + name: appClientService + version: 0.0.1 + registry: + sc: + address: http://127.0.0.1:30100 handler: chain: Consumer: @@ -13,6 +29,6 @@ cse: loadbalance: appServer:appService: transactionControl: - policy: io.servicecomb.loadbalance.filter.SimpleTransactionControlFilter + policy: org.apache.servicecomb.loadbalance.filter.SimpleTransactionControlFilter options: - tag0: value0 \ No newline at end of file + tag0: value0 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 3cd174d3815..00000000000 --- a/demo/demo-crossapp/crossapp-client/src/main/resources/schemas/helloworld.yaml +++ /dev/null @@ -1,19 +0,0 @@ -swagger: '2.0' -info: - title: hello service - version: 1.0.0 - x-java-interface: io.servicecomb.demo.crossapp.HelloWorld - -basePath: /helloworld -produces: - - application/json - -paths: - /hello: - get: - operationId: sayHello - responses: - 200: - description: say hello - schema: - type: string \ No newline at end of file diff --git a/demo/demo-crossapp/crossapp-client/src/test/java/io/servicecomb/demo/crossapp/CrossAppIT.java b/demo/demo-crossapp/crossapp-client/src/test/java/io/servicecomb/demo/crossapp/CrossAppIT.java deleted file mode 100644 index 70b3feea3c8..00000000000 --- a/demo/demo-crossapp/crossapp-client/src/test/java/io/servicecomb/demo/crossapp/CrossAppIT.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.servicecomb.demo.crossapp; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -import org.junit.Before; -import org.junit.Test; - -import io.servicecomb.demo.TestMgr; - -public class CrossAppIT { - - @Before - public void setUp() throws Exception { - TestMgr.errors().clear(); - } - - @Test - public void clientGetsNoError() throws Exception { - CrossappClient.main(new String[0]); - - assertThat(TestMgr.errors().isEmpty(), is(true)); - } -} diff --git a/demo/demo-crossapp/crossapp-client/src/test/java/org/apache/servicecomb/demo/crossapp/CrossAppIT.java b/demo/demo-crossapp/crossapp-client/src/test/java/org/apache/servicecomb/demo/crossapp/CrossAppIT.java new file mode 100644 index 00000000000..54b02c90ae9 --- /dev/null +++ b/demo/demo-crossapp/crossapp-client/src/test/java/org/apache/servicecomb/demo/crossapp/CrossAppIT.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.crossapp; + +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 = CrossappClient.class) +public class CrossAppIT { + + @BeforeEach + public void setUp() throws Exception { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + CrossappClient.run(); + + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} diff --git a/demo/demo-crossapp/crossapp-server/pom.xml b/demo/demo-crossapp/crossapp-server/pom.xml index a47610fc9c0..0207bdb7b20 100644 --- a/demo/demo-crossapp/crossapp-server/pom.xml +++ b/demo/demo-crossapp/crossapp-server/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-crossapp - 0.3.0-SNAPSHOT - - crossapp-server + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + + org.apache.servicecomb.demo + demo-crossapp + 3.4.0-SNAPSHOT + + crossapp-server + Java Chassis::Demo::CrossApp::Server - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-pojo - - + + - - io.servicecomb.demo.crossapp.CrossappServer - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-build-config - 0.3.0-SNAPSHOT - - - - - - + + org.apache.servicecomb.demo.crossapp.CrossappServer + + + + + org.springframework.boot + spring-boot-maven-plugin + + + - - - docker - - - - io.fabric8 - docker-maven-plugin - - - org.commonjava.maven.plugins - directory-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-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/CrossappBootListener.java b/demo/demo-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/CrossappBootListener.java deleted file mode 100644 index 68a88a0b961..00000000000 --- a/demo/demo-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/CrossappBootListener.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.crossapp; - -import org.springframework.stereotype.Component; - -import io.servicecomb.core.BootListener; -import io.servicecomb.core.definition.loader.DynamicSchemaLoader; - -@Component -public class CrossappBootListener implements BootListener { - @Override - public void onBootEvent(BootEvent event) { - if (EventType.BEFORE_PRODUCER_PROVIDER.equals(event.getEventType())) { - // 动态注册schemas目录下面的契约到当前服务 - DynamicSchemaLoader.INSTANCE.registerSchemas("classpath*:schemas/*.yaml"); - } - } -} diff --git a/demo/demo-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/CrossappServer.java b/demo/demo-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/CrossappServer.java deleted file mode 100644 index 3db2f96b280..00000000000 --- a/demo/demo-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/CrossappServer.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.crossapp; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class CrossappServer { - - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/demo/demo-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/HelloWorld.java b/demo/demo-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/HelloWorld.java deleted file mode 100644 index 405269dbaaf..00000000000 --- a/demo/demo-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/HelloWorld.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.crossapp; - -public interface HelloWorld { - String sayHello(); -} diff --git a/demo/demo-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/HelloWorldImpl.java b/demo/demo-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/HelloWorldImpl.java deleted file mode 100644 index 57ff2fbb14b..00000000000 --- a/demo/demo-crossapp/crossapp-server/src/main/java/io/servicecomb/demo/crossapp/HelloWorldImpl.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.crossapp; - -import io.servicecomb.provider.pojo.RpcSchema; - -@RpcSchema(schemaId = "helloworld") -public class HelloWorldImpl implements HelloWorld { - - @Override - public String sayHello() { - return "hello world"; - } -} 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 new file mode 100644 index 00000000000..63678f2e9d6 --- /dev/null +++ b/demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappServer.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.demo.crossapp; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +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 new file mode 100644 index 00000000000..b8ede3eba7e --- /dev/null +++ b/demo/demo-crossapp/crossapp-server/src/main/java/org/apache/servicecomb/demo/crossapp/HelloWorldImpl.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.crossapp; + +import org.apache.servicecomb.provider.rest.common.RestSchema; + +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/log4j2.xml b/demo/demo-crossapp/crossapp-server/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-crossapp/crossapp-server/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 2ea0eccf00f..0f1bbdc8853 100644 --- a/demo/demo-crossapp/crossapp-server/src/main/resources/microservice.yaml +++ b/demo/demo-crossapp/crossapp-server/src/main/resources/microservice.yaml @@ -1,16 +1,37 @@ -APPLICATION_ID: appServer -service_description: - name: appService - version: 0.0.1 - properties: - allowCrossApp: true -instance_description: - properties: - tag0: value0 +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- -cse: +servicecomb: service: - registry: - address: http://127.0.0.1:9980 + application: appServer + name: appService + version: 0.0.1 + properties: + allowCrossApp: true + tag0: value0 + registry: + sc: + address: http://127.0.0.1:30100 rest: - address: 127.0.0.1:8080 \ No newline at end of file + address: 0.0.0.0:8080 + cors: + enabled: true + origin: "http://test.domain:8080,http://test.domain:9090" + allowedHeader: abc,def + allowedMethod: GET,PUT,POST,DELETE + exposedHeader: abc,def + maxAge: 1 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 3cd174d3815..00000000000 --- a/demo/demo-crossapp/crossapp-server/src/main/resources/schemas/helloworld.yaml +++ /dev/null @@ -1,19 +0,0 @@ -swagger: '2.0' -info: - title: hello service - version: 1.0.0 - x-java-interface: io.servicecomb.demo.crossapp.HelloWorld - -basePath: /helloworld -produces: - - application/json - -paths: - /hello: - get: - operationId: sayHello - responses: - 200: - description: say hello - schema: - type: string \ No newline at end of file diff --git a/demo/demo-crossapp/pom.xml b/demo/demo-crossapp/pom.xml index 57af71e64d2..08c2055d357 100644 --- a/demo/demo-crossapp/pom.xml +++ b/demo/demo-crossapp/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.3.0-SNAPSHOT - - demo-crossapp - pom + 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.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-crossapp + Java Chassis::Demo::CrossApp + pom - - crossapp-server - crossapp-client - + + crossapp-server + crossapp-client + + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + diff --git a/demo/demo-cse-v1/README.md b/demo/demo-cse-v1/README.md new file mode 100644 index 00000000000..965592efcf8 --- /dev/null +++ b/demo/demo-cse-v1/README.md @@ -0,0 +1,22 @@ +# 使用微服务引擎专业版的测试用例 + +* 首先登陆华为云华南区,获取 AK/SK。 测试机器设置环境变量: CREDENTIALS_AK、CREDENTIALS_SK。 +* 在配置中心增加如下配置: + * consumer.yaml + +```yaml +cse: + v1: + test: + foo: foo + dynamicString: a,b + dynamicArray: + - m + - n +``` + +* 依次启动 provider、provider-canary、consumer、gateway +* 在配置中心增加如下配置: + * cse.v1.test.bar: bar + +* 执行 tests-client 里面的集成测试用例 diff --git a/demo/demo-cse-v1/consumer/pom.xml b/demo/demo-cse-v1/consumer/pom.xml new file mode 100644 index 00000000000..78a7bf75a23 --- /dev/null +++ b/demo/demo-cse-v1/consumer/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-cse-v1 + 3.4.0-SNAPSHOT + + + cse-v1-consumer + Java Chassis::Demo::CSE-V1::CONSUMER + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + 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 new file mode 100644 index 00000000000..3e7a2a0e285 --- /dev/null +++ b/demo/demo-cse-v1/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-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 new file mode 100644 index 00000000000..f86c5a29492 --- /dev/null +++ b/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.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 java.util.List; + +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.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RestSchema(schemaId = "ConsumerConfigController") +@RequestMapping(path = "/") +public class ConsumerConfigController { + private Environment environment; + + private ConsumerConfigurationProperties consumerConfigurationProperties; + + @Autowired + public ConsumerConfigController(Environment environment, + ConsumerConfigurationProperties consumerConfigurationProperties) { + this.environment = environment; + this.consumerConfigurationProperties = consumerConfigurationProperties; + } + + @GetMapping("/config") + public String config(@RequestParam("key") String key) { + return environment.getProperty(key); + } + + @GetMapping("/foo") + public String foo() { + return consumerConfigurationProperties.getFoo(); + } + + @GetMapping("/bar") + public String bar() { + return consumerConfigurationProperties.getBar(); + } + + @GetMapping("/dynamicString") + public String dynamicString(@RequestParam("key") String key) { + return environment.getProperty(key); + } + + @GetMapping("/dynamicArray") + @SuppressWarnings("unchecked") + public List dynamicArray() { + return consumerConfigurationProperties.getDynamicArray(); + } +} diff --git a/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigurationProperties.java b/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigurationProperties.java new file mode 100644 index 00000000000..6f9592425d9 --- /dev/null +++ b/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigurationProperties.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.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@ConfigurationProperties("cse.v1.test") +@Component +public class ConsumerConfigurationProperties { + private String foo; + + private String bar; + + private List dynamicArray; + + public String getFoo() { + return foo; + } + + public void setFoo(String foo) { + this.foo = foo; + } + + public String getBar() { + return bar; + } + + public void setBar(String bar) { + this.bar = bar; + } + + public List getDynamicArray() { + return dynamicArray; + } + + public void setDynamicArray(List dynamicArray) { + this.dynamicArray = dynamicArray; + } +} diff --git a/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java b/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java new file mode 100644 index 00000000000..dc3ead65da2 --- /dev/null +++ b/demo/demo-cse-v1/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("/sayHelloCanary") + public String sayHelloCanary(@RequestParam("name") String name) { + return providerService.sayHelloCanary(name); + } +} diff --git a/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java b/demo/demo-cse-v1/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java new file mode 100644 index 00000000000..176bcee9932 --- /dev/null +++ b/demo/demo-cse-v1/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 sayHelloCanary(String name); +} diff --git a/demo/demo-cse-v1/consumer/src/main/resources/application.yml b/demo/demo-cse-v1/consumer/src/main/resources/application.yml new file mode 100644 index 00000000000..11f739de444 --- /dev/null +++ b/demo/demo-cse-v1/consumer/src/main/resources/application.yml @@ -0,0 +1,137 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +# override common configurations in common module +servicecomb-config-order: 10 + +servicecomb: + service: + application: demo-java-chassis-cse-v1 + name: consumer + version: 0.0.1 + registry: + sc: + address: https://cse.cn-south-1.myhuaweicloud.com + watch: false + config: + client: + serverUri: https://cse.cn-south-1.myhuaweicloud.com + fileSource: consumer.yaml + + rest: + address: 0.0.0.0:9092 # should be same with server.port to use web container + + routeRule: + provider: | + - precedence: 1 + match: + headers: + canary: + exact: new + route: + - weight: 20 + tags: + version: 0.0.1 + - weight: 80 + tags: + version: 0.0.2 + - precedence: 2 + match: + headers: + canary: + exact: old + route: + - 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 + + # Configure AK/SK credentials if needed. Default not enabled. + credentials: + akskEnabled: true + accessKey: ${CREDENTIALS_AK} + 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 new file mode 100644 index 00000000000..4e54783856a --- /dev/null +++ b/demo/demo-cse-v1/consumer/src/main/resources/log4j2.xml @@ -0,0 +1,45 @@ + + + + + + + + ./file/log/ + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/demo-cse-v1/gateway/pom.xml b/demo/demo-cse-v1/gateway/pom.xml new file mode 100644 index 00000000000..49241e669cf --- /dev/null +++ b/demo/demo-cse-v1/gateway/pom.xml @@ -0,0 +1,50 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-cse-v1 + 3.4.0-SNAPSHOT + + + cse-v1-gateway + Java Chassis::Demo::CSE-V1::GATEWAY + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + edge-core + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + 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 new file mode 100644 index 00000000000..7d58caafd9d --- /dev/null +++ b/demo/demo-cse-v1/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-cse-v1/gateway/src/main/resources/application.yml b/demo/demo-cse-v1/gateway/src/main/resources/application.yml new file mode 100644 index 00000000000..52f065c4f62 --- /dev/null +++ b/demo/demo-cse-v1/gateway/src/main/resources/application.yml @@ -0,0 +1,70 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +# override common configurations in common module +servicecomb-config-order: 100 + +servicecomb: + service: + application: demo-java-chassis-cse-v1 + name: gateway + version: 0.0.1 + registry: + sc: + address: https://cse.cn-south-1.myhuaweicloud.com + watch: false + config: + client: + serverUri: https://cse.cn-south-1.myhuaweicloud.com + + 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+ + + matchGroup: + canary-header: | + matches: + - headers: + canary: + prefix: "" + mapper: + canary-header: | + target: + canary: $H{canary} + + # Configure AK/SK credentials if needed. Default not enabled. + credentials: + akskEnabled: true + accessKey: ${CREDENTIALS_AK} + secretKey: ${CREDENTIALS_SK} + akskCustomCipher: default + 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 new file mode 100644 index 00000000000..165cdf7e0a3 --- /dev/null +++ b/demo/demo-cse-v1/gateway/src/main/resources/log4j2.xml @@ -0,0 +1,45 @@ + + + + + + + + ./gateway/log/ + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/demo-cse-v1/pom.xml b/demo/demo-cse-v1/pom.xml new file mode 100644 index 00000000000..72e70e58c15 --- /dev/null +++ b/demo/demo-cse-v1/pom.xml @@ -0,0 +1,78 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-cse-v1 + Java Chassis::Demo::CSE-V1 + pom + + + org.apache.servicecomb + solution-basic + + + org.apache.servicecomb + dashboard + + + org.apache.servicecomb + darklaunch + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + 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 + + + + + provider + provider-canary + consumer + gateway + test-client + + + diff --git a/demo/demo-cse-v1/provider-canary/pom.xml b/demo/demo-cse-v1/provider-canary/pom.xml new file mode 100644 index 00000000000..34c393b9e8d --- /dev/null +++ b/demo/demo-cse-v1/provider-canary/pom.xml @@ -0,0 +1,51 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-cse-v1 + 3.4.0-SNAPSHOT + + + cse-v1-provider-canary + Java Chassis::Demo::CSE-V1::PROVIDER CANARY + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + 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 new file mode 100644 index 00000000000..fde6f36b2e3 --- /dev/null +++ b/demo/demo-cse-v1/provider-canary/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-cse-v1/provider-canary/src/main/java/org/apache/servicecomb/samples/ProviderController.java b/demo/demo-cse-v1/provider-canary/src/main/java/org/apache/servicecomb/samples/ProviderController.java new file mode 100644 index 00000000000..d5b8e1384a9 --- /dev/null +++ b/demo/demo-cse-v1/provider-canary/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 in canary " + name; + } + + @GetMapping("/sayHelloCanary") + public String sayHelloCanary(@RequestParam("name") String name) { + return "Hello Canary in canary " + name; + } +} 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 new file mode 100644 index 00000000000..29e8e8f3879 --- /dev/null +++ b/demo/demo-cse-v1/provider-canary/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +# override common configurations in common module +servicecomb-config-order: 10 + +servicecomb: + service: + application: demo-java-chassis-cse-v1 + name: provider + version: 0.0.2 + registry: + sc: + address: https://cse.cn-south-1.myhuaweicloud.com + watch: false + config: + client: + serverUri: https://cse.cn-south-1.myhuaweicloud.com + + rest: + address: 0.0.0.0:9095 + +# Configure AK/SK credentials if needed. Default not enabled. + credentials: + akskEnabled: true + accessKey: ${CREDENTIALS_AK} + secretKey: ${CREDENTIALS_SK} + akskCustomCipher: default + project: cn-south-1 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 new file mode 100644 index 00000000000..9ac74305e0c --- /dev/null +++ b/demo/demo-cse-v1/provider-canary/src/main/resources/log4j2.xml @@ -0,0 +1,45 @@ + + + + + + + + ./user/log/ + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/demo-cse-v1/provider/pom.xml b/demo/demo-cse-v1/provider/pom.xml new file mode 100644 index 00000000000..12015bd6b7a --- /dev/null +++ b/demo/demo-cse-v1/provider/pom.xml @@ -0,0 +1,51 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-cse-v1 + 3.4.0-SNAPSHOT + + + cse-v1-provider + Java Chassis::Demo::CSE-V1::PROVIDER + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + 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 new file mode 100644 index 00000000000..fde6f36b2e3 --- /dev/null +++ b/demo/demo-cse-v1/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-cse-v1/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java b/demo/demo-cse-v1/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java new file mode 100644 index 00000000000..9e1c8f96541 --- /dev/null +++ b/demo/demo-cse-v1/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("/sayHelloCanary") + public String sayHelloCanary(@RequestParam("name") String name) { + return "Hello Canary " + name; + } +} diff --git a/demo/demo-cse-v1/provider/src/main/resources/application.yml b/demo/demo-cse-v1/provider/src/main/resources/application.yml new file mode 100644 index 00000000000..097e2e9cc85 --- /dev/null +++ b/demo/demo-cse-v1/provider/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +# override common configurations in common module +servicecomb-config-order: 10 + +servicecomb: + service: + application: demo-java-chassis-cse-v1 + name: provider + version: 0.0.1 + registry: + sc: + address: https://cse.cn-south-1.myhuaweicloud.com + watch: false + config: + client: + serverUri: https://cse.cn-south-1.myhuaweicloud.com + + rest: + address: 0.0.0.0:9093 # should be same with server.port to use web container + +# Configure AK/SK credentials if needed. Default not enabled. + credentials: + akskEnabled: true + accessKey: ${CREDENTIALS_AK} + secretKey: ${CREDENTIALS_SK} + akskCustomCipher: default + project: cn-south-1 diff --git a/demo/demo-cse-v1/provider/src/main/resources/log4j2.xml b/demo/demo-cse-v1/provider/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..9ac74305e0c --- /dev/null +++ b/demo/demo-cse-v1/provider/src/main/resources/log4j2.xml @@ -0,0 +1,45 @@ + + + + + + + + ./user/log/ + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/demo-cse-v1/test-client/pom.xml b/demo/demo-cse-v1/test-client/pom.xml new file mode 100644 index 00000000000..697c28fb64e --- /dev/null +++ b/demo/demo-cse-v1/test-client/pom.xml @@ -0,0 +1,55 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-cse-v1 + 3.4.0-SNAPSHOT + + + cse-v1-test-client + Java Chassis::Demo::CSE-V1::TEST-CLIENT + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb.demo + demo-schema + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/Config.java b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/Config.java new file mode 100644 index 00000000000..2e9105cdd4a --- /dev/null +++ b/demo/demo-cse-v1/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-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 new file mode 100644 index 00000000000..2aff8c25966 --- /dev/null +++ b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.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.samples; + +import java.util.List; + +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 { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testConfig(); + testFooBar(); + } + + @SuppressWarnings("unchecked") + private void testConfig() { + String result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v1.test.foo", String.class); + TestMgr.check("foo", result); + result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v1.test.bar", String.class); + TestMgr.check("bar", result); + result = template.getForObject(Config.GATEWAY_URL + "/dynamicString?key=cse.v1.test.dynamicString", String.class); + TestMgr.check("a,b", result); + + List listResult = template + .getForObject(Config.GATEWAY_URL + "/dynamicArray", List.class); + TestMgr.check(2, listResult.size()); + TestMgr.check("m", listResult.get(0)); + TestMgr.check("n", listResult.get(1)); + } + + private void testFooBar() { + String result = template.getForObject(Config.GATEWAY_URL + "/foo", String.class); + TestMgr.check("foo", result); + result = template.getForObject(Config.GATEWAY_URL + "/bar", String.class); + 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 new file mode 100644 index 00000000000..691ee47a162 --- /dev/null +++ b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 HelloWorldIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHelloWorldFallback(); + testHelloWorldNoHeader(); + testHelloWorld(); + testHelloWorldCanary(); + testHelloWorldEmptyProtectionCloseWeight100(); + testHelloWorldeEmptyProtectionCloseWeightLess100(); + testHelloWorldEmptyProtectionCloseFallback(); + testHelloWorldEmptyProtectionCloseWeight100Two(); + } + + private void testHelloWorld() { + for (int i = 0; i < 10; i++) { + MultiValueMap headers = new HttpHeaders(); + headers.add("canary", "old"); + 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); + } + } + + private void testHelloWorldCanary() { + int oldCount = 0; + int newCount = 0; + + for (int i = 0; i < 20; i++) { + 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\"")) { + 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(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 new file mode 100644 index 00000000000..10d62c6bba4 --- /dev/null +++ b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.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.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); + + CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + } +} 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 new file mode 100644 index 00000000000..13ffddc60b8 --- /dev/null +++ b/demo/demo-cse-v1/test-client/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +# override common configurations in common module +servicecomb-config-order: 10 + +servicecomb: + service: + application: demo-java-chassis-cse-v1 + name: test-client + version: 0.0.1 + registry: + sc: + address: https://cse.cn-south-1.myhuaweicloud.com + watch: false + config: + client: + serverUri: https://cse.cn-south-1.myhuaweicloud.com + + rest: + address: 0.0.0.0:9097 # should be same with server.port to use web container + + # Configure AK/SK credentials if needed. Default not enabled. + credentials: + akskEnabled: true + accessKey: ${CREDENTIALS_AK} + secretKey: ${CREDENTIALS_SK} + akskCustomCipher: default + 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 new file mode 100644 index 00000000000..9ac74305e0c --- /dev/null +++ b/demo/demo-cse-v1/test-client/src/main/resources/log4j2.xml @@ -0,0 +1,45 @@ + + + + + + + + ./user/log/ + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/demo-cse-v2/README.md b/demo/demo-cse-v2/README.md new file mode 100644 index 00000000000..dda7dee702e --- /dev/null +++ b/demo/demo-cse-v2/README.md @@ -0,0 +1,137 @@ +# 使用微服务引擎2.0的测试用例 + +* 参考:https://support.huaweicloud.com/devg-cse/cse_devg_0036.html 安装微服务引擎2.0 + +* 设置环境变量: + * PAAS_CSE_SC_ENDPOINT: 注册中心的地址 + * PAAS_CSE_CC_ENDPOINT: 配置中心的地址 + +* 依次启动 provider、consumer、gateway + +* 在配置中心增加如下配置: +* 应用级配置:consumer.yaml。类型为 yaml。 + +```yaml +cse: + v2: + test: + foo: foo +``` + + * 自定义配置:priority1.yaml。label信息: public=default 。类型为 yaml。 +```yaml +cse: + v2: + test: + priority: v1 + common: common +``` + + * 自定义配置:priority1.yaml。label信息: public=default,extra=default 。类型为 yaml。 +```yaml +cse: + v2: + test: + priority: v1 + extra: common +``` + + * 应用级配置:priority2.yaml。类型为 yaml。 +```yaml +cse: + v2: + test: + priority: v2 +``` + + * 服务级配置:priority3.yaml,微服务性选择consumer。类型为 yaml。 +```yaml +cse: + v2: + test: + priority: v3 +``` + +* 应用级配置:consumerApp.yaml,应用选择demo-java-chassis-cse-v2。类型为 yaml。 +```yaml +cse: + v2: + test: + priority1: v1 +``` + +* 服务级配置:consumerService.yaml,微服务性选择consumer。类型为 yaml。 +```yaml +cse: + v2: + test: + priority1: v2 +``` + + * 应用级配置: cse.v2.test.bar: bar 。 类型为 text。 + +* 执行 tests-client 里面的集成测试用例 (成功) + +* 修改 + * priority1.yaml。label信息: public=default 。类型为 yaml。 +```yaml +cse: + v2: + test: + priority: v4 +``` + +* 执行 tests-client 里面的集成测试用例 (成功) + +* 修改 + * 应用级priority3.yaml。 +```yaml +cse: + v2: + test: + priority: v5 +``` + +* 执行 tests-client 里面的集成测试用例 (失败) + +* 修改 + * 应用级priority3.yaml。label信息: +```yaml +cse: + v2: + test: + priority: v3 +``` + +* 执行 tests-client 里面的集成测试用例 (成功) +* 修改 + * 应用级consumerApp.yaml。 +```yaml +cse: + v2: + test: + priority1: v10 +``` + +* 执行 tests-client 里面的集成测试用例 (成功) +* 修改 + * 服务级配置:consumerService.yaml。 +```yaml +cse: + v2: + test: + priority1: v20 +``` + +* 执行 tests-client 里面的集成测试用例 (成功) + +* 修改 + * 版本级配置:consumerIns.yaml。 +```yaml +cse: + v2: + test: + priority1: v30 +``` + +* 执行 tests-client 里面的集成测试用例 (失败) diff --git a/demo/demo-cse-v2/consumer/pom.xml b/demo/demo-cse-v2/consumer/pom.xml new file mode 100644 index 00000000000..0fe6e05f4d0 --- /dev/null +++ b/demo/demo-cse-v2/consumer/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-cse-v2 + 3.4.0-SNAPSHOT + + + cse-v2-consumer + Java Chassis::Demo::CSE-V2::CONSUMER + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + 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 new file mode 100644 index 00000000000..3e7a2a0e285 --- /dev/null +++ b/demo/demo-cse-v2/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-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.java b/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.java new file mode 100644 index 00000000000..8c3c45d24a3 --- /dev/null +++ b/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigController.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.samples; + +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.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RestSchema(schemaId = "ConsumerConfigController") +@RequestMapping(path = "/") +public class ConsumerConfigController { + private Environment environment; + + private ConsumerConfigurationProperties consumerConfigurationProperties; + + @Autowired + public ConsumerConfigController(Environment environment, ConsumerConfigurationProperties consumerConfigurationProperties) { + this.environment = environment; + this.consumerConfigurationProperties = consumerConfigurationProperties; + } + + @GetMapping("/config") + public String config(@RequestParam("key") String key) { + return environment.getProperty(key); + } + + @GetMapping("/foo") + public String foo() { + return consumerConfigurationProperties.getFoo(); + } + + @GetMapping("/bar") + public String bar() { + return consumerConfigurationProperties.getBar(); + } + + @GetMapping("/priority") + public String priority() { + return consumerConfigurationProperties.getPriority(); + } + + @GetMapping("/common") + public String common() { + return consumerConfigurationProperties.getCommon(); + } + + @GetMapping("/extra") + public String extra() { + return consumerConfigurationProperties.getExtra(); + } +} diff --git a/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigurationProperties.java b/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigurationProperties.java new file mode 100644 index 00000000000..e87d9ebb915 --- /dev/null +++ b/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerConfigurationProperties.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.samples; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@ConfigurationProperties("cse.v2.test") +@Component +public class ConsumerConfigurationProperties { + private String foo; + + private String bar; + + private String priority; + + private String common; + + private String extra; + + public String getFoo() { + return foo; + } + + public void setFoo(String foo) { + this.foo = foo; + } + + public String getBar() { + return bar; + } + + public void setBar(String bar) { + this.bar = bar; + } + + public String getPriority() { + return priority; + } + + public void setPriority(String priority) { + this.priority = priority; + } + + public String getCommon() { + return common; + } + + public void setCommon(String common) { + this.common = common; + } + + public String getExtra() { + return extra; + } + + public void setExtra(String extra) { + this.extra = extra; + } +} diff --git a/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java b/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java new file mode 100644 index 00000000000..4ed0ecd85fe --- /dev/null +++ b/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.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.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); + } +} diff --git a/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java b/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java new file mode 100644 index 00000000000..f327412b759 --- /dev/null +++ b/demo/demo-cse-v2/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.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 ProviderService { + String sayHello(String name); +} diff --git a/demo/demo-cse-v2/consumer/src/main/resources/application.yml b/demo/demo-cse-v2/consumer/src/main/resources/application.yml new file mode 100644 index 00000000000..5982bb668b5 --- /dev/null +++ b/demo/demo-cse-v2/consumer/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- + +# override common configurations in common module +servicecomb-config-order: 10 + +servicecomb: + service: + application: demo-java-chassis-cse-v2 + name: consumer + version: 0.0.1 + registry: + sc: + address: ${PAAS_CSE_SC_ENDPOINT:http://127.0.0.1:30100} + watch: false + kie: + 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 new file mode 100644 index 00000000000..4e54783856a --- /dev/null +++ b/demo/demo-cse-v2/consumer/src/main/resources/log4j2.xml @@ -0,0 +1,45 @@ + + + + + + + + ./file/log/ + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/demo-cse-v2/gateway/pom.xml b/demo/demo-cse-v2/gateway/pom.xml new file mode 100644 index 00000000000..1d04a248410 --- /dev/null +++ b/demo/demo-cse-v2/gateway/pom.xml @@ -0,0 +1,50 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-cse-v2 + 3.4.0-SNAPSHOT + + + cse-v2-gateway + Java Chassis::Demo::CSE-V2::GATEWAY + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + edge-core + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + 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 new file mode 100644 index 00000000000..7d58caafd9d --- /dev/null +++ b/demo/demo-cse-v2/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-cse-v2/gateway/src/main/resources/application.yml b/demo/demo-cse-v2/gateway/src/main/resources/application.yml new file mode 100644 index 00000000000..32d2a728d76 --- /dev/null +++ b/demo/demo-cse-v2/gateway/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- + +# override common configurations in common module +servicecomb-config-order: 100 + +servicecomb: + service: + application: demo-java-chassis-cse-v2 + name: gateway + version: 0.0.1 + registry: + sc: + address: ${PAAS_CSE_SC_ENDPOINT:http://127.0.0.1:30100} + watch: false + + kie: + serverUri: ${PAAS_CSE_CC_ENDPOINT:http://127.0.0.1:30110} + customLabel: public + + 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-cse-v2/gateway/src/main/resources/log4j2.xml b/demo/demo-cse-v2/gateway/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..165cdf7e0a3 --- /dev/null +++ b/demo/demo-cse-v2/gateway/src/main/resources/log4j2.xml @@ -0,0 +1,45 @@ + + + + + + + + ./gateway/log/ + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/demo-cse-v2/pom.xml b/demo/demo-cse-v2/pom.xml new file mode 100644 index 00000000000..35a623930da --- /dev/null +++ b/demo/demo-cse-v2/pom.xml @@ -0,0 +1,65 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-cse-v2 + Java Chassis::Demo::CSE-V2 + pom + + + org.apache.servicecomb + solution-basic + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + config-kie + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + + + provider + consumer + gateway + test-client + + + diff --git a/demo/demo-cse-v2/provider/pom.xml b/demo/demo-cse-v2/provider/pom.xml new file mode 100644 index 00000000000..4308b1169d9 --- /dev/null +++ b/demo/demo-cse-v2/provider/pom.xml @@ -0,0 +1,51 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-cse-v2 + 3.4.0-SNAPSHOT + + + cse-v2-provider + Java Chassis::Demo::CSE-V2::PROVIDER + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + 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 new file mode 100644 index 00000000000..fde6f36b2e3 --- /dev/null +++ b/demo/demo-cse-v2/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-cse-v2/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java b/demo/demo-cse-v2/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java new file mode 100644 index 00000000000..6eea7776a6b --- /dev/null +++ b/demo/demo-cse-v2/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.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.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; + } +} diff --git a/demo/demo-cse-v2/provider/src/main/resources/application.yml b/demo/demo-cse-v2/provider/src/main/resources/application.yml new file mode 100644 index 00000000000..bb55de45abc --- /dev/null +++ b/demo/demo-cse-v2/provider/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +# spring boot configurations +server: + port: 9093 # should be same with servicecomb.rest.address to use web container + +# override common configurations in common module +servicecomb-config-order: 10 + +servicecomb: + service: + application: demo-java-chassis-cse-v2 + name: provider + version: 0.0.1 + registry: + sc: + address: ${PAAS_CSE_SC_ENDPOINT:http://127.0.0.1:30100} + watch: false + kie: + 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 new file mode 100644 index 00000000000..9ac74305e0c --- /dev/null +++ b/demo/demo-cse-v2/provider/src/main/resources/log4j2.xml @@ -0,0 +1,45 @@ + + + + + + + + ./user/log/ + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/demo-cse-v2/test-client/pom.xml b/demo/demo-cse-v2/test-client/pom.xml new file mode 100644 index 00000000000..78917aa7e5c --- /dev/null +++ b/demo/demo-cse-v2/test-client/pom.xml @@ -0,0 +1,55 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-cse-v2 + 3.4.0-SNAPSHOT + + + cse-v2-test-client + Java Chassis::Demo::CSE-V2::TEST-CLIENT + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb.demo + demo-schema + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/Config.java b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/Config.java new file mode 100644 index 00000000000..2e9105cdd4a --- /dev/null +++ b/demo/demo-cse-v2/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-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 new file mode 100644 index 00000000000..16b1388641a --- /dev/null +++ b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.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.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 { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testConfig(); + testFooBar(); + } + + private void testConfig() { + String result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.foo", String.class); + TestMgr.check("foo", result); + result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.bar", String.class); + TestMgr.check("bar", result); + result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.priority", String.class); + TestMgr.check("v1", result); + result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.common", String.class); + TestMgr.check("common", result); + result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.extra", String.class); + TestMgr.check("common", result); + result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.priority1", String.class); + TestMgr.check("v2", result); + } + + private void testFooBar() { + String result = template.getForObject(Config.GATEWAY_URL + "/bar", String.class); + TestMgr.check("bar", result); + result = template.getForObject(Config.GATEWAY_URL + "/foo", String.class); + TestMgr.check("foo", result); + result = template.getForObject(Config.GATEWAY_URL + "/priority", String.class); + TestMgr.check("v1", result); + result = template.getForObject(Config.GATEWAY_URL + "/common", String.class); + TestMgr.check("common", result); + result = template.getForObject(Config.GATEWAY_URL + "/extra", String.class); + 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 new file mode 100644 index 00000000000..59d06bb1e01 --- /dev/null +++ b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.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; + +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(); + } + + private void testHelloWorld() { + String result = template + .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); + 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 new file mode 100644 index 00000000000..10d62c6bba4 --- /dev/null +++ b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.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.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); + + CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + } +} 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 new file mode 100644 index 00000000000..046fa53ee6b --- /dev/null +++ b/demo/demo-cse-v2/test-client/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- +# spring boot configurations +server: + port: 9093 # should be same with servicecomb.rest.address to use web container + +# override common configurations in common module +servicecomb-config-order: 10 + +servicecomb: + service: + application: demo-java-chassis-cse-v2 + name: test-client + version: 0.0.1 + registry: + sc: + address: ${PAAS_CSE_SC_ENDPOINT:http://127.0.0.1:30100} + watch: false + kie: + serverUri: ${PAAS_CSE_CC_ENDPOINT:http://127.0.0.1:30110} + customLabel: public + + rest: + address: 0.0.0.0:9097 # should be same with server.port to use web container 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 new file mode 100644 index 00000000000..9ac74305e0c --- /dev/null +++ b/demo/demo-cse-v2/test-client/src/main/resources/log4j2.xml @@ -0,0 +1,45 @@ + + + + + + + + ./user/log/ + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 00000000000..4849faf28fe --- /dev/null +++ b/demo/demo-edge/authentication/pom.xml @@ -0,0 +1,94 @@ + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-edge + 3.4.0-SNAPSHOT + + authentication + Java Chassis::Demo::Edge::Authentication + + + + org.apache.servicecomb.demo.edge.authentication.AuthMain + + + + + 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-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/AuthImpl.java b/demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/AuthImpl.java new file mode 100644 index 00000000000..15963c96388 --- /dev/null +++ b/demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/AuthImpl.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.demo.edge.authentication; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "auth") +@RequestMapping(path = "auth/v1") +public class AuthImpl { + @PostMapping(path = "/auth") + public boolean auth(@RequestHeader(name = "info") String info) { + return true; + } +} 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 new file mode 100644 index 00000000000..4928706edcd --- /dev/null +++ b/demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/AuthMain.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.demo.edge.authentication; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +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/java/org/apache/servicecomb/demo/edge/authentication/encrypt/EncryptImpl.java b/demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/encrypt/EncryptImpl.java new file mode 100644 index 00000000000..1bd47a476d1 --- /dev/null +++ b/demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/encrypt/EncryptImpl.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.demo.edge.authentication.encrypt; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "encrypt") +@RequestMapping(path = "auth/v1") +public class EncryptImpl { + @GetMapping(path = "/queryUserId") + public String queryUserId(String serviceToken) { + return serviceToken + "-userId"; + } + + @GetMapping(path = "/queryHcr") + public Hcr queryHcr(String hcrId) { + Hcr hcr = new Hcr(); + hcr.setBodyKey("bodyKey-" + hcrId + "-"); + hcr.setSignatureKey("signatureKey-" + hcrId + "-"); + return hcr; + } +} diff --git a/demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/encrypt/Hcr.java b/demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/encrypt/Hcr.java new file mode 100644 index 00000000000..aafdbcf7ddd --- /dev/null +++ b/demo/demo-edge/authentication/src/main/java/org/apache/servicecomb/demo/edge/authentication/encrypt/Hcr.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.demo.edge.authentication.encrypt; + +public class Hcr { + private String bodyKey; + + private String signatureKey; + + public String getBodyKey() { + return bodyKey; + } + + public void setBodyKey(String bodyKey) { + this.bodyKey = bodyKey; + } + + public String getSignatureKey() { + return signatureKey; + } + + public void setSignatureKey(String signatureKey) { + this.signatureKey = signatureKey; + } +} diff --git a/demo/demo-edge/authentication/src/main/resources/log4j2.xml b/demo/demo-edge/authentication/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-edge/authentication/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/demo/demo-edge/authentication/src/main/resources/microservice.yaml b/demo/demo-edge/authentication/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..038b64eb1ec --- /dev/null +++ b/demo/demo-edge/authentication/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + 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 + server: + http2: + useAlpnEnabled: false + # for test case run in one core machine + verticle-count: 1 diff --git a/demo/demo-edge/business-1-1-0/pom.xml b/demo/demo-edge/business-1-1-0/pom.xml new file mode 100644 index 00000000000..1cef9b01cab --- /dev/null +++ b/demo/demo-edge/business-1-1-0/pom.xml @@ -0,0 +1,100 @@ + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-edge + 3.4.0-SNAPSHOT + + business-1-1-0 + Java Chassis::Demo::Edge::Business 1.1.0 + + org.apache.servicecomb.demo.edge.business.BusinessMain_V1_1_0 + + + + + org.apache.servicecomb.demo + model + ${project.version} + + + + + + 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-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 new file mode 100644 index 00000000000..f725fa24e0e --- /dev/null +++ b/demo/demo-edge/business-1-1-0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V1_1_0.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.demo.edge.business; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +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 new file mode 100644 index 00000000000..38420d285d6 --- /dev/null +++ b/demo/demo-edge/business-1-1-0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.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.edge.business; + +import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; +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; + +@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(); + String rsp = "result from 1.1.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); + } +} diff --git a/demo/demo-edge/business-1-1-0/src/main/resources/log4j2.xml b/demo/demo-edge/business-1-1-0/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-edge/business-1-1-0/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 new file mode 100644 index 00000000000..0fa3a1764e4 --- /dev/null +++ b/demo/demo-edge/business-1-1-0/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: edge + name: business + version: 1.1.0 + registry: + sc: + address: http://127.0.0.1:30100 + rest: + address: 0.0.0.0:8090 + server: + verticle-count: 1 diff --git a/demo/demo-edge/business-1.0.0/pom.xml b/demo/demo-edge/business-1.0.0/pom.xml new file mode 100644 index 00000000000..d7e35748fef --- /dev/null +++ b/demo/demo-edge/business-1.0.0/pom.xml @@ -0,0 +1,102 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-edge + 3.4.0-SNAPSHOT + + business-1-0-0 + Java Chassis::Demo::Edge::Business 1.0.0 + + + org.apache.servicecomb.demo.edge.business.BusinessMain_V1_0_0 + + + + + org.apache.servicecomb.demo + model + ${project.version} + + + + + + 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-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 new file mode 100644 index 00000000000..e1a57c7e8b1 --- /dev/null +++ b/demo/demo-edge/business-1.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V1_0_0.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.demo.edge.business; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +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 new file mode 100644 index 00000000000..1e0d1b84bb8 --- /dev/null +++ b/demo/demo-edge/business-1.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.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.demo.edge.business; + +import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; +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; + +@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(); + String rsp = "result from 1.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); + } +} diff --git a/demo/demo-edge/business-1.0.0/src/main/resources/log4j2.xml b/demo/demo-edge/business-1.0.0/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-edge/business-1.0.0/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 new file mode 100644 index 00000000000..cc3c45ed92c --- /dev/null +++ b/demo/demo-edge/business-1.0.0/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: edge + name: business + version: 1.0.0 + registry: + sc: + address: http://127.0.0.1:30100 + rest: + address: 0.0.0.0:8080 + server: + verticle-count: 1 diff --git a/demo/demo-edge/business-2.0.0/pom.xml b/demo/demo-edge/business-2.0.0/pom.xml new file mode 100644 index 00000000000..3500be47d75 --- /dev/null +++ b/demo/demo-edge/business-2.0.0/pom.xml @@ -0,0 +1,102 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-edge + 3.4.0-SNAPSHOT + + business-2-0-0 + Java Chassis::Demo::Edge::Business 2.0.0 + + + org.apache.servicecomb.demo.edge.business.BusinessMain_V2_0_0 + + + + + org.apache.servicecomb.demo + model + ${project.version} + + + + + + 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-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 new file mode 100644 index 00000000000..81559ce431f --- /dev/null +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/BusinessMain_V2_0_0.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.demo.edge.business; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +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 new file mode 100644 index 00000000000..f8d951cd226 --- /dev/null +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/EdgeServiceGovernanceService.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.edge.business; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +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; + +@RestSchema(schemaId = "EdgeServiceGovernanceService") +@RequestMapping(path = "/business/v2") +public class EdgeServiceGovernanceService { + private static final Logger LOGGER = LoggerFactory.getLogger(EdgeServiceGovernanceService.class); + + private Map retryTimes = new HashMap<>(); + + private AtomicInteger instanceIsolationIndex = new AtomicInteger(); + + @GetMapping("/testEdgeServiceRetry") + @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 testEdgeServiceRetry(@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("/testEdgeServiceInstanceIsolation") + @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 testEdgeServiceInstanceIsolation(@RequestParam(name = "name") String name) { + if (instanceIsolationIndex.getAndIncrement() % 3 != 0) { + throw new InvocationException(502, "InstanceIsolation", "InstanceIsolation"); + } + return name; + } + + @GetMapping("/testEdgeServiceInstanceBulkhead") + public String testEdgeServiceInstanceBulkhead(@RequestParam(name = "name") String name) { + return name; + } +} 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 new file mode 100644 index 00000000000..fde945e50bf --- /dev/null +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.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.edge.business; + +import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; +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; + +@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(); + 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); + } +} 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 new file mode 100644 index 00000000000..9fd5aeec777 --- /dev/null +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/CustomExceptionToProducerResponseConverter.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.edge.business.error; + +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 + public boolean canConvert(Throwable throwable) { + return throwable instanceof IllegalStateException; + } + + @Override + public int getOrder() { + return 100; + } + + @Override + public InvocationException convert(Invocation invocation, IllegalStateException e, + StatusType genericStatus) { + IllegalStateErrorData data = new IllegalStateErrorData(); + data.setId(500); + data.setMessage(e.getMessage()); + data.setState(e.getMessage()); + return new InvocationException(Status.INTERNAL_SERVER_ERROR, data); + } +} diff --git a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/ErrorData.java b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/ErrorData.java new file mode 100644 index 00000000000..078c1941422 --- /dev/null +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/ErrorData.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.edge.business.error; + +public class ErrorData { + private int id; + + private String message; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} 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 new file mode 100644 index 00000000000..f4b56e58782 --- /dev/null +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/ErrorService.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.edge.business.error; + +import jakarta.ws.rs.core.Response.Status; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@RestSchema(schemaId = "error-v2") +@RequestMapping(path = "/business/v2/error") +public class ErrorService { + @RequestMapping(path = "/add", method = RequestMethod.GET) + public int add(int x, int y) { + if (x == 99) { + throw new NullPointerException("un expected NPE test."); + } + if (x == 88) { + ErrorData data = new ErrorData(); + data.setId(12); + data.setMessage("not allowed id."); + throw new InvocationException(Status.FORBIDDEN, data); + } + if (x == 77) { + throw new IllegalStateException("77"); + } + return x + y; + } +} diff --git a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/IllegalStateErrorData.java b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/IllegalStateErrorData.java new file mode 100644 index 00000000000..d771c7a514b --- /dev/null +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/error/IllegalStateErrorData.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.edge.business.error; + +public class IllegalStateErrorData { + private int id; + + private String message; + + private String state; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } +} diff --git a/demo/demo-edge/business-2.0.0/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter b/demo/demo-edge/business-2.0.0/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter new file mode 100644 index 00000000000..2675a9b60dd --- /dev/null +++ b/demo/demo-edge/business-2.0.0/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.edge.business.error.CustomExceptionToProducerResponseConverter diff --git a/demo/demo-edge/business-2.0.0/src/main/resources/log4j2.xml b/demo/demo-edge/business-2.0.0/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-edge/business-2.0.0/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 new file mode 100644 index 00000000000..45a9f2ff743 --- /dev/null +++ b/demo/demo-edge/business-2.0.0/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: edge + name: business + version: 2.0.0 + registry: + sc: + address: http://127.0.0.1:30100 + rest: + address: 0.0.0.0:8091 + server: + verticle-count: 1 diff --git a/demo/demo-edge/consumer/pom.xml b/demo/demo-edge/consumer/pom.xml new file mode 100644 index 00000000000..c26ca048528 --- /dev/null +++ b/demo/demo-edge/consumer/pom.xml @@ -0,0 +1,260 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-edge + 3.4.0-SNAPSHOT + + consumer + Java Chassis::Demo::Edge::Consumer + + + org.apache.servicecomb.demo.edge.consumer.ConsumerMain + + + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb.demo + model + ${project.version} + + + + + + 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 + + + + + + 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 new file mode 100644 index 00000000000..ab10a7c3713 --- /dev/null +++ b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java @@ -0,0 +1,302 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.consumer; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +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; +import org.apache.servicecomb.demo.edge.model.DependTypeB; +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.api.DiscoveryInstance; +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.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestOperations; + +public class Consumer { + RestOperations template = RestTemplateBuilder.create(); + + ChannelRequestBase request = new ChannelRequestBase(); + + String edgePrefix; + + List addV1Result = new ArrayList<>(); + + List decV1Result = new ArrayList<>(); + + List addV2Result = new ArrayList<>(); + + List decV2Result = new ArrayList<>(); + + DiscoveryManager discoveryManager = BeanUtils.getBean(DiscoveryManager.class); + + Environment environment = BeanUtils.getBean(Environment.class); + + public Consumer() { + request.setDeviceId("2a5cc42ff60006ac"); + request.setServiceToken( + "c2VydmliZVRva2VuPTAwMjAwMDg2MDAwMDAwMDAwODMyYzYzamV1ZnV1cWdpYXgmRGV2aWNIVHIw" + + "ZT0wJkRLdmljZUIEPTg2MzgoMDAyMDA0NDcwMiZhcHBJRD1jb20uaHVhd2VpLndhbGxldA"); + request.setPhoneType("VTR-AL00"); + request.setUserId("20086000000000832"); + request.setCmdId("5"); + request.setNet("1"); + request.setUserGrant("000"); + request.setSysVer("EMUI5.1"); + request.setTs("1497356427334"); + request.setChannelId("3"); + request.setLocation(null); + request.setCmdVer("2.0"); + request.setLanguage("zh_CN"); + } + + public void run(String prefix) { + prepareEdge(prefix); + + testRecursiveSelf(); + testDependType(); + testDownload(); + testDownloadBigFile(); + testErrorCode(); + + invoke("/v1/add", 2, 1, addV1Result); + invoke("/v1/add", 3, 1, addV1Result); + invoke("/v1/add", 4, 1, addV1Result); + invoke("/v1/add", 5, 1, addV1Result); + + invoke("/v1/dec", 2, 1, decV1Result); + invoke("/v1/dec", 3, 1, decV1Result); + + invoke("/v2/add", 2, 1, addV2Result); + invoke("/v2/add", 3, 1, addV2Result); + + invoke("/v2/dec", 2, 1, decV2Result); + invoke("/v2/dec", 3, 1, decV2Result); + + printResults("v1/add", addV1Result); + printResults("v1/dec", decV1Result); + printResults("v2/add", addV2Result); + printResults("v2/dec", decV2Result); + + checkResult("v1/add", addV1Result, "1.0.0", "1.1.0"); + checkResult("v1/dec", decV1Result, "1.1.0"); + checkResult("v2/add", addV2Result, "2.0.0"); + checkResult("v2/dec", decV2Result, "2.0.0"); + } + + public void testEncrypt() { + prepareEdge("encryptApi"); + String url = edgePrefix + "/v2/encrypt"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + + MultiValueMap form = new LinkedMultiValueMap<>(); + form.add("name", "userName"); + form.add("age", "10"); + form.add("serviceToken", "serviceTokenTest"); + form.add("hcrId", "hcrIdTest"); + form.add("body", "bodyKey-hcrIdTest-{\"body1\":\"b1\",\"body2\":\"b2\",\"body3\":\"b3\"}"); + + HttpEntity> entity = new HttpEntity<>(form, headers); + + @SuppressWarnings("unchecked") + Map result = (Map) template.postForObject(url, entity, Map.class); + Assert.isTrue(result.containsKey("signature"), "must exist signature"); + result.remove("signature"); + + String expected = "{name=userName, age=10, userId=serviceTokenTest-userId, body1=b1, body2=b2, body3=b3}"; + Assert.isTrue(expected.equalsIgnoreCase(result.toString()), + String.format("expected: %s\nreal : %s", expected, result)); + } + + protected void testRecursiveSelf() { + String url = edgePrefix + "/v2/recursiveSelf"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + RecursiveSelfType recursiveSelfType = new RecursiveSelfType(); + recursiveSelfType.setField(new RecursiveSelfType()); + recursiveSelfType.getField().setValue(10); + + HttpEntity entity = new HttpEntity<>(recursiveSelfType, headers); + + RecursiveSelfType response = template.postForObject(url, entity, RecursiveSelfType.class); + Assert.isTrue(response.getValue() == 0, "default must be 0"); + Assert.isTrue(response.getField().getValue() == 10, "must be 10"); + Assert.isNull(response.getField().getField(), "must be null"); + } + + @SuppressWarnings({"rawtypes"}) + protected void testErrorCode() { + String url = edgePrefix + "/v2/error/add"; + + int response = template.getForObject(url + "?x=2&y=3", Integer.class); + Assert.isTrue(response == 5, "not get 5."); + + try { + Map raw = template.getForObject(url + "?x=99&y=3", Map.class); + } catch (HttpServerErrorException e) { + Assert.isTrue(e.getStatusCode().value() == 500, "x99"); + Assert.isTrue(e.getResponseBodyAsString().contains("un expected NPE test."), "x99"); + } + + try { + template.getForObject(url + "?x=88&y=3", Map.class); + Assert.isTrue(false, "x88"); + } catch (HttpClientErrorException e) { + 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.getStatusCode().value() == 500, "x77"); + Assert.isTrue(e.getResponseBodyAsString().equals("{\"id\":500,\"message\":\"77\",\"state\":\"77\"}"), "x77"); + } + } + + protected void testDependType() { + String url = edgePrefix + "/v2/dependType"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + DependTypeA dependTypeA = new DependTypeA(); + dependTypeA.setB(new DependTypeB()); + dependTypeA.getB().setValue(10); + + HttpEntity entity = new HttpEntity<>(dependTypeA, headers); + + DependTypeA response = template.postForObject(url, entity, DependTypeA.class); + Assert.isTrue(response.getB().getValue() == 10, "must be 10"); + } + + protected void testDownloadBigFile() { + String url = edgePrefix + "/v2/bigFile"; + AtomicInteger size = new AtomicInteger(); + + template.execute(url, HttpMethod.GET, req -> { + }, resp -> { + byte[] buf = new byte[1 * 1024 * 1024]; + try (InputStream is = resp.getBody()) { + for (; ; ) { + int len = is.read(buf); + if (len == -1) { + break; + } + + size.addAndGet(len); + } + } + return null; + }); + Assert.isTrue(size.get() == 10 * 1024 * 1024, "size is : " + size.get() + " not 10 * 1024 * 1024"); + System.out.println("test download bigFile finished"); + } + + protected void testDownload() { + String url = edgePrefix + "/v2/download"; + String content = template.getForObject(url, String.class); + Assert.isTrue("download".equals(content), "content is : " + content + " not download"); + System.out.println("test download finished"); + } + + private void checkResult(String name, List results, String... expectedVersions) { + Set versions = new HashSet<>(); + Set remained = new HashSet<>(Arrays.asList(expectedVersions)); + for (ResultWithInstance result : results) { + versions.add(result.getVersion()); + remained.remove(result.getVersion()); + } + + Assert.isTrue(remained.isEmpty(), + String.format("%s expectedVersions %s, real versions %s.", + name, + Arrays.deepToString(expectedVersions), + versions)); + } + + protected void printResults(String name, List results) { + System.out.println(name); + for (ResultWithInstance result : results) { + System.out.println(result); + } + System.out.println(""); + } + + protected void invoke(String appendUrl, int x, int y, List results) { + String url = edgePrefix + appendUrl + String.format("?x=%d&y=%d", x, y); + ResultWithInstance result = template.getForObject(url, ResultWithInstance.class); + results.add(result); + } + + private URIEndpointObject prepareEdge(String prefix) { + DiscoveryInstance instance = discoveryManager.findServiceInstances( + BootStrapProperties.readApplication(environment), "edge") + .stream() + .findFirst() + .get(); + 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) { + List result = new ArrayList<>(6); + for (int i = 0; i < 6; i++) { + String url = urlPrefix + "/channel/news/subscribe"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity entity = new HttpEntity<>(request, headers); + + ResponseEntity response = template.postForEntity(url, entity, AppClientDataRsp.class); + 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 new file mode 100644 index 00000000000..ae462eb6fd1 --- /dev/null +++ b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.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.demo.edge.consumer; + +import org.apache.servicecomb.demo.CategorizedTestCaseRunner; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ConsumerMain { + public static void main(String[] args) throws Exception { + new SpringApplicationBuilder(ConsumerMain.class).web(WebApplicationType.NONE).run(args); + + runTest(); + } + + public static void runTest() throws Exception { + new Consumer().testEncrypt(); + new Consumer().invokeBusiness("cse://business/business/v1", new ChannelRequestBase()); + + System.out.println("Running api dispatcher."); + new Consumer().run("api"); + System.out.println("Running rest dispatcher."); + new Consumer().run("rest"); + System.out.println("Running url dispatcher."); + new Consumer().run("url"); +// 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."); + + runCategorizedTest(); + + TestMgr.summary(); + if (!TestMgr.errors().isEmpty()) { + throw new IllegalStateException("tests failed"); + } + } + + private static void runCategorizedTest() throws Exception { + CategorizedTestCaseRunner + .runCategorizedTestCase("edge"); + } +} 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 new file mode 100644 index 00000000000..ad774433957 --- /dev/null +++ b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.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.demo.edge.consumer; + +import java.util.concurrent.CountDownLatch; +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.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.RestOperations; + +@Component +public class EdgeServiceGovernanceTest implements CategorizedTestCase { + 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(); + testEdgeServiceInstanceBulkhead(); + testEdgeServiceInstanceIsolation(); // may isolate instance for 5 seconds. + Thread.sleep(6000); // ensure isolation is open for new requests + } + + private void testEdgeServiceInstanceBulkhead() throws Exception { + String url = edgePrefix + "/business/v2/testEdgeServiceInstanceBulkhead"; + + CountDownLatch latch = new CountDownLatch(100); + AtomicBoolean expectedFailed503 = 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 { + String result = template.getForObject(url + "?name={1}", String.class, "hello"); + if (!"hello".equals(result)) { + notExpectedFailed.set(true); + } + } catch (Exception e) { + if (!(e instanceof HttpServerErrorException)) { + notExpectedFailed.set(true); + } else { + if (((HttpServerErrorException) e).getStatusCode().value() == 503) { + expectedFailed503.set(true); + } + } + } + latch.countDown(); + } + }.start(); + } + Thread.sleep(100); + } + + latch.await(20, TimeUnit.SECONDS); + TestMgr.check(true, expectedFailed503.get()); + TestMgr.check(false, notExpectedFailed.get()); + } + + 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); + + 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 { + String result = template.getForObject(url + "?name={1}", String.class, "hello"); + if (!"hello".equals(result)) { + notExpectedFailed.set(true); + } + } catch (Exception e) { + 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(); + } + }.start(); + } + Thread.sleep(100); + } + + 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 URIEndpointObject prepareEdge(String prefix) { + DiscoveryInstance microserviceInstance = discoveryManager.findServiceInstances( + BootStrapProperties.readApplication(environment), "edge") + .stream() + .findFirst() + .get(); + URIEndpointObject edgeAddress = new URIEndpointObject(microserviceInstance.getEndpoints().get(0)); + edgePrefix = String.format("http://%s:%d/%s", edgeAddress.getHostOrIp(), edgeAddress.getPort(), prefix); + return edgeAddress; + } +} diff --git a/demo/demo-edge/consumer/src/main/resources/log4j2.xml b/demo/demo-edge/consumer/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-edge/consumer/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/demo/demo-edge/consumer/src/main/resources/microservice.yaml b/demo/demo-edge/consumer/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..953b1e567e4 --- /dev/null +++ b/demo/demo-edge/consumer/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: edge + name: consumer + version: 0.0.1 + registry: + sc: + address: http://127.0.0.1:30100 + rest: + client.http2.useAlpnEnabled: false + handler: + chain: + Consumer: + default: loadbalance diff --git a/demo/demo-edge/consumer/src/test/java/org/apache/servicecomb/demo/edge/consumer/EdgeDemoIT.java b/demo/demo-edge/consumer/src/test/java/org/apache/servicecomb/demo/edge/consumer/EdgeDemoIT.java new file mode 100644 index 00000000000..0ef1f0c9ba3 --- /dev/null +++ b/demo/demo-edge/consumer/src/test/java/org/apache/servicecomb/demo/edge/consumer/EdgeDemoIT.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.edge.consumer; + +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 = ConsumerMain.class) +public class EdgeDemoIT { + @BeforeEach + public void setUp() { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + ConsumerMain.runTest(); + + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} diff --git a/demo/demo-edge/edge-service/pom.xml b/demo/demo-edge/edge-service/pom.xml new file mode 100644 index 00000000000..31fdcff3874 --- /dev/null +++ b/demo/demo-edge/edge-service/pom.xml @@ -0,0 +1,100 @@ + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-edge + 3.4.0-SNAPSHOT + + edge-service + Java Chassis::Demo::Edge::Service + + + org.apache.servicecomb.demo.edge.EdgeMain + + + + + org.apache.servicecomb + edge-core + + + + + + 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-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 new file mode 100644 index 00000000000..b1c17a93cab --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/EdgeMain.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.demo.edge; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +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/authentication/encrypt/Hcr.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/authentication/encrypt/Hcr.java new file mode 100644 index 00000000000..aafdbcf7ddd --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/authentication/encrypt/Hcr.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.demo.edge.authentication.encrypt; + +public class Hcr { + private String bodyKey; + + private String signatureKey; + + public String getBodyKey() { + return bodyKey; + } + + public void setBodyKey(String bodyKey) { + this.bodyKey = bodyKey; + } + + public String getSignatureKey() { + return signatureKey; + } + + public void setSignatureKey(String signatureKey) { + this.signatureKey = signatureKey; + } +} diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/CustomResponseMetaMapper.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/CustomResponseMetaMapper.java new file mode 100644 index 00000000000..7aa294c83ed --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/CustomResponseMetaMapper.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.edge.service; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.invocation.response.ResponseMetaMapper; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.SimpleType; + +public class CustomResponseMetaMapper implements ResponseMetaMapper { + private static final Map CODES = new HashMap<>(1); + + static { + CODES.put(500, SimpleType.constructUnsafe(IllegalStateErrorData.class)); + } + + @Override + public int getOrder() { + return 100; + } + + @Override + public Map getMapper() { + return CODES; + } +} diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/EdgeConst.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/EdgeConst.java new file mode 100644 index 00000000000..7439ec60d44 --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/EdgeConst.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.edge.service; + +public interface EdgeConst { + String ENCRYPT_CONTEXT = "encryptContext"; +} 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 new file mode 100644 index 00000000000..b900a427409 --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/EdgeDispatcher.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.service; + +import java.util.Map; + +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.EdgeInvocationCreator; +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 io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; + +public class EdgeDispatcher extends AbstractEdgeDispatcher { + @Override + public int getOrder() { + return 10000; + } + + @Override + public void init(Router router) { + String regex = "/api/([^\\\\/]+)/([^\\\\/]+)/(.*)"; + // 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); + } + + protected void onRequest(RoutingContext context) { + Map pathParams = context.pathParams(); + String microserviceName = pathParams.get("param0"); + String path = context.request().path().substring(4); + + requestByFilter(context, microserviceName, 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, path); + new RestProducerInvocationFlow(creator, requestEx, responseEx) + .run(); + } +} diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/IllegalStateErrorData.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/IllegalStateErrorData.java new file mode 100644 index 00000000000..017183f1c56 --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/IllegalStateErrorData.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.edge.service; + +public class IllegalStateErrorData { + private int id; + + private String message; + + private String state; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } +} diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/Encrypt.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/Encrypt.java new file mode 100644 index 00000000000..a85ce903e26 --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/Encrypt.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.edge.service.encrypt; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; + +public interface Encrypt { + CompletableFuture queryUserId(String serviceToken); + + CompletableFuture queryHcr(String hcrId); +} diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptContext.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptContext.java new file mode 100644 index 00000000000..88c8b2b33e2 --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptContext.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.edge.service.encrypt; + +import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; + +public class EncryptContext { + private Hcr hcr; + + private String userId; + + public EncryptContext(Hcr hcr, String userId) { + this.hcr = hcr; + this.userId = userId; + } + + public Hcr getHcr() { + return hcr; + } + + public void setHcr(Hcr hcr) { + this.hcr = hcr; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } +} 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 new file mode 100644 index 00000000000..76ea1840402 --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptEdgeDispatcher.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.service.encrypt; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; +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.EdgeInvocationCreator; +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.provider.pojo.Invoker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.http.HttpServerRequest; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; + +public class EncryptEdgeDispatcher extends AbstractEdgeDispatcher { + private static final Logger LOGGER = LoggerFactory.getLogger(EncryptEdgeDispatcher.class); + + private Encrypt encrypt = Invoker.createProxy("auth", "encrypt", Encrypt.class); + + private String prefix = "encryptApi"; + + @Override + public int getOrder() { + return 10000; + } + + @Override + public void init(Router router) { + 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); + } + + protected void onRequest(RoutingContext context) { + HttpServerRequest httpServerRequest = context.request(); + + // queryUserId always success + CompletableFuture userIdFuture = queryUserId(httpServerRequest); + queryHcr(httpServerRequest).thenCombine(userIdFuture, (hcr, userId) -> { + // hcr and userId all success + routeToBackend(context, hcr, userId); + return null; + }).whenComplete((v, e) -> { + // failed to query hcr + if (e != null) { + context.response().end("failed to query hcr: " + e.getMessage()); + return; + } + }); + } + + private CompletableFuture queryUserId(HttpServerRequest httpServerRequest) { + String serviceToken = httpServerRequest.getParam("serviceToken"); + if (serviceToken == null) { + // no need to query userId + return CompletableFuture.completedFuture(null); + } + + CompletableFuture future = new CompletableFuture<>(); + encrypt.queryUserId(serviceToken).whenComplete((userId, e) -> { + if (e != null) { + // treat as success, just userId is null + LOGGER.error("Failed to query userId, serviceToken={}.", serviceToken, e); + } + + future.complete(userId); + }); + + return future; + } + + private CompletableFuture queryHcr(HttpServerRequest httpServerRequest) { + String hcrId = httpServerRequest.getParam("hcrId"); + return encrypt.queryHcr(hcrId); + } + + private void routeToBackend(RoutingContext context, Hcr hcr, String userId) { + Map pathParams = context.pathParams(); + String microserviceName = pathParams.get("param0"); + String path = context.request().path().substring(prefix.length() + 1); + + requestByFilter(context, microserviceName, path, + new EncryptContext(hcr, userId)); + } + + 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, path) { + @Override + public CompletableFuture createAsync() { + CompletableFuture result = super.createAsync(); + return result.whenComplete((invocation, throwable) + -> { + if (throwable == null) { + invocation.getHandlerContext().put(EdgeConst.ENCRYPT_CONTEXT, encryptContext); + } + }); + } + }; + new RestProducerInvocationFlow(creator, requestEx, responseEx) + .run(); + } +} 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 new file mode 100644 index 00000000000..d9e0f332318 --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/DecodeBodyFilter.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.service.encrypt.filter; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +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.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.Response; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +@Component +public class DecodeBodyFilter extends AbstractFilter implements EdgeFilter { + private JavaType bodyType = TypeFactory.defaultInstance().constructMapType(Map.class, String.class, String[].class); + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1790; + } + + @Override + public String getName() { + return "test-edge-decode-body"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + EncryptContext encryptContext = (EncryptContext) invocation.getHandlerContext().get(EdgeConst.ENCRYPT_CONTEXT); + if (encryptContext == null) { + return nextNode.onFilter(invocation); + } + Hcr hcr = encryptContext.getHcr(); + + String encodedBody = invocation.getRequestEx().getParameter("body"); + if (encodedBody == null) { + return nextNode.onFilter(invocation); + } + + encodedBody = encodedBody.substring(hcr.getBodyKey().length()); + + try { + Map decodedBody = RestObjectMapperFactory.getRestObjectMapper() + .readValue(encodedBody, bodyType); + invocation.getRequestEx().getParameterMap().putAll(decodedBody); + } catch (Throwable e) { + // should be a meaning exception response + return CompletableFuture.failedFuture(e); + } + return nextNode.onFilter(invocation); + } +} 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 new file mode 100644 index 00000000000..42443b555b4 --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureRequestFilter.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.service.encrypt.filter; + +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +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.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.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; + +@Component +public class EdgeSignatureRequestFilter extends AbstractFilter implements EdgeFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(EdgeSignatureRequestFilter.class); + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1800; + } + + @Override + public String getName() { + return "test-edge-signature-request"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + EncryptContext encryptContext = (EncryptContext) invocation.getHandlerContext().get(EdgeConst.ENCRYPT_CONTEXT); + if (encryptContext == null) { + return nextNode.onFilter(invocation); + } + Hcr hcr = encryptContext.getHcr(); + + // signature for query and form + List names = Collections.list(invocation.getRequestEx().getParameterNames()); + names.sort(Comparator.naturalOrder()); + + Hasher hasher = Hashing.sha256().newHasher(); + hasher.putString(hcr.getSignatureKey(), StandardCharsets.UTF_8); + for (String name : names) { + hasher.putString(name, StandardCharsets.UTF_8); + hasher.putString(invocation.getRequestEx().getParameter(name), StandardCharsets.UTF_8); + } + LOGGER.info("afterReceiveRequest signature: {}", hasher.hash()); + + return nextNode.onFilter(invocation); + } +} 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 new file mode 100644 index 00000000000..a3909d75540 --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureResponseFilter.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.edge.service.encrypt.filter; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CompletableFuture; + +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.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.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; + +import io.vertx.core.buffer.Buffer; + +@Component +public class EdgeSignatureResponseFilter extends AbstractFilter implements EdgeFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(EdgeSignatureResponseFilter.class); + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1991; + } + + @Override + public String getName() { + return "test-edge-signature-response"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + return nextNode.onFilter(invocation).whenComplete((response, throwable) -> { + if (throwable != null) { + return; + } + EncryptContext encryptContext = (EncryptContext) invocation.getHandlerContext().get(EdgeConst.ENCRYPT_CONTEXT); + if (encryptContext == null) { + return; + } + Hcr hcr = encryptContext.getHcr(); + + // bad practice: it's better to set signature in response header + Buffer bodyBuffer = response.getResult(); + String body = bodyBuffer.toString(); + if (body.endsWith("}")) { + Hasher hasher = Hashing.sha256().newHasher(); + hasher.putString(hcr.getSignatureKey(), StandardCharsets.UTF_8); + hasher.putString(body, StandardCharsets.UTF_8); + String signature = hasher.hash().toString(); + LOGGER.info("beforeSendResponse signature: {}", signature); + + body = body.substring(0, body.length() - 1) + ",\"signature\":\"" + signature + "\"}"; + response.setResult(Buffer.buffer(body)); + } + }); + } +} 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 new file mode 100644 index 00000000000..fd20c0e6d7e --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/UserIdFilter.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.edge.service.encrypt.filter; + +import java.util.concurrent.CompletableFuture; + +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.demo.edge.service.EdgeConst; +import org.apache.servicecomb.demo.edge.service.encrypt.EncryptContext; +import org.apache.servicecomb.swagger.invocation.Response; +import org.springframework.stereotype.Component; + +@Component +public class UserIdFilter extends AbstractFilter implements EdgeFilter { + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1790; + } + + @Override + public String getName() { + return "test-edge-user-id"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + EncryptContext encryptContext = (EncryptContext) invocation.getHandlerContext().get(EdgeConst.ENCRYPT_CONTEXT); + if (encryptContext == null) { + return nextNode.onFilter(invocation); + } + + String userId = encryptContext.getUserId(); + if (userId != null) { + invocation.getRequestEx().setParameter("userId", userId); + } + + return nextNode.onFilter(invocation); + } +} diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/Auth.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/Auth.java new file mode 100644 index 00000000000..8cf00fb8138 --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/Auth.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.demo.edge.service.handler; + +import java.util.concurrent.CompletableFuture; + +public interface Auth { + CompletableFuture auth(String info); +} 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 new file mode 100644 index 00000000000..4aeb44be08a --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/AuthHandler.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.demo.edge.service.handler; + +import java.util.concurrent.CompletableFuture; + +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.demo.edge.service.EdgeConst; +import org.apache.servicecomb.provider.pojo.Invoker; +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 extends AbstractFilter implements EdgeFilter { + private static Logger LOGGER = LoggerFactory.getLogger(AuthHandler.class); + + private static Auth auth; + + static { + auth = Invoker.createProxy("auth", "auth", Auth.class); + } + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1980; + } + + @Override + 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); + } + + @Override + public String getName() { + return "test-auth"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + if (invocation.getHandlerContext().get(EdgeConst.ENCRYPT_CONTEXT) != null) { + return nextNode.onFilter(invocation); + } + + return auth.auth("").thenCompose(result -> doHandle(invocation, nextNode, result)); + } + + protected CompletableFuture doHandle(Invocation invocation, FilterNode nextNode, Boolean authSucc) { + if (!authSucc) { + return CompletableFuture.failedFuture(new InvocationException(Status.UNAUTHORIZED, (Object) "auth failed")); + } + + LOGGER.debug("auth success."); + return nextNode.onFilter(invocation); + } +} 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 new file mode 100644 index 00000000000..dcc25e0a838 --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.ResponseMetaMapper @@ -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.edge.service.CustomResponseMetaMapper diff --git a/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher b/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher new file mode 100644 index 00000000000..dffc432396f --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher @@ -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.demo.edge.service.EdgeDispatcher +org.apache.servicecomb.demo.edge.service.encrypt.EncryptEdgeDispatcher diff --git a/demo/demo-edge/edge-service/src/main/resources/log4j2.xml b/demo/demo-edge/edge-service/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/demo/demo-edge/edge-service/src/main/resources/microservice.yaml b/demo/demo-edge/edge-service/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..56403677e38 --- /dev/null +++ b/demo/demo-edge/edge-service/src/main/resources/microservice.yaml @@ -0,0 +1,137 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: edge + name: edge + version: 0.0.1 + registry: + sc: + address: http://127.0.0.1:30100 + edge: + filter: + addHeader: + enabled: true + allowedHeaders: xxx + rest: + address: 0.0.0.0:18090 + server: + # for test case run in one core machine + verticle-count: 1 + client: + verticle-count: 1 + http2.useAlpnEnabled: false + handler: + chain: + Consumer: + default: auth,loadbalance,instance-isolation-consumer,instance-bulkhead-consumer + service: + auth: loadbalance,instance-isolation-consumer,instance-bulkhead-consumer + http: + dispatcher: + edge: + default: + enabled: true + prefix: rest + withVersion: true + prefixSegmentCount: 1 + url: + enabled: true + mappings: + businessV1: + prefixSegmentCount: 1 + path: "/url/business/v1/.*" + microserviceName: business + businessV2: + prefixSegmentCount: 1 + path: "/url/business/v2/.*" + microserviceName: business + http: + enabled: true + mappings: + businessV2: + prefixSegmentCount: 1 + path: "/http/business/v2/.*" + microserviceName: business + businessV1: + prefixSegmentCount: 1 + path: "/http/business/v1/add.*" + microserviceName: business + businessV1_1: + prefixSegmentCount: 1 + path: "/http/business/v1/dec.*" + microserviceName: business + + # 服务治理配置 + matchGroup: + testEdgeServiceRetry: | + matches: + - apiPath: + exact: "/business/v2/testEdgeServiceRetry" + testEdgeServiceInstanceIsolation: | + matches: + - apiPath: + exact: "/business/v2/testEdgeServiceInstanceIsolation" + testEdgeServiceInstanceBulkhead: | + matches: + - apiPath: + exact: "/business/v2/testEdgeServiceInstanceBulkhead" + # retry not supported now + # retry: + # testEdgeServiceRetry: | + # maxAttempts: 2 + # retryOnSame: 0 + instanceIsolation: + testEdgeServiceInstanceIsolation: | + minimumNumberOfCalls: 10 + slidingWindowSize: 20 + slidingWindowType: COUNT_BASED + failureRateThreshold: 50 + slowCallRateThreshold: 100 + slowCallDurationThreshold: 3000 + 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 new file mode 100644 index 00000000000..3adda2c13e4 --- /dev/null +++ b/demo/demo-edge/model/pom.xml @@ -0,0 +1,35 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-edge + 3.4.0-SNAPSHOT + + model + Java Chassis::Demo::Edge::Model + + + org.apache.servicecomb + registry-service-center + + + diff --git a/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/AppClientDataRsp.java b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/AppClientDataRsp.java new file mode 100644 index 00000000000..9e54fd86064 --- /dev/null +++ b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/AppClientDataRsp.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.edge.model; + +public class AppClientDataRsp { + private String rsp; + + public String getRsp() { + return rsp; + } + + public void setRsp(String rsp) { + this.rsp = rsp; + } + + @Override + public String toString() { + return "AppClientDataRsp [rsp=" + rsp + "]"; + } +} diff --git a/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/ChannelRequestBase.java b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/ChannelRequestBase.java new file mode 100644 index 00000000000..1feabe45547 --- /dev/null +++ b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/ChannelRequestBase.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.edge.model; + +public class ChannelRequestBase { + private String deviceId; + + private String serviceToken; + + private String phoneType; + + private String userId; + + private String cmdId; + + private String net; + + private String userGrant; + + private String sysVer; + + private String ts; + + private String channelId; + + private String location; + + private String cmdVer; + + private String version; + + private String language; + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getServiceToken() { + return serviceToken; + } + + public void setServiceToken(String serviceToken) { + this.serviceToken = serviceToken; + } + + public String getPhoneType() { + return phoneType; + } + + public void setPhoneType(String phoneType) { + this.phoneType = phoneType; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getCmdId() { + return cmdId; + } + + public void setCmdId(String cmdId) { + this.cmdId = cmdId; + } + + public String getNet() { + return net; + } + + public void setNet(String net) { + this.net = net; + } + + public String getUserGrant() { + return userGrant; + } + + public void setUserGrant(String userGrant) { + this.userGrant = userGrant; + } + + public String getSysVer() { + return sysVer; + } + + public void setSysVer(String sysVer) { + this.sysVer = sysVer; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + public String getCmdVer() { + return cmdVer; + } + + public void setCmdVer(String cmdVer) { + this.cmdVer = cmdVer; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + @Override + public String toString() { + return "ChannelRequestBase [deviceId=" + deviceId + ", serviceToken=" + serviceToken + ", phoneType=" + phoneType + + ", userId=" + userId + ", cmdId=" + cmdId + ", net=" + net + ", userGrant=" + userGrant + ", sysVer=" + sysVer + + ", ts=" + ts + ", channelId=" + channelId + ", location=" + location + ", cmdVer=" + cmdVer + ", version=" + + version + ", language=" + language + "]"; + } +} diff --git a/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeA.java b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeA.java new file mode 100644 index 00000000000..605cc88c3b9 --- /dev/null +++ b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeA.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.demo.edge.model; + +public class DependTypeA { + private DependTypeB b; + + public DependTypeB getB() { + return b; + } + + public void setB(DependTypeB b) { + this.b = b; + } +} diff --git a/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeB.java b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeB.java new file mode 100644 index 00000000000..19067900c30 --- /dev/null +++ b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/DependTypeB.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.demo.edge.model; + +public class DependTypeB { + private DependTypeA a; + + private int value; + + public DependTypeA getA() { + return a; + } + + public void setA(DependTypeA a) { + this.a = a; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } +} diff --git a/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/RecursiveSelfType.java b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/RecursiveSelfType.java new file mode 100644 index 00000000000..2ae58adcfc8 --- /dev/null +++ b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/RecursiveSelfType.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.demo.edge.model; + +public class RecursiveSelfType { + private RecursiveSelfType field; + + private int value; + + public RecursiveSelfType getField() { + return field; + } + + public void setField(RecursiveSelfType field) { + this.field = field; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } +} 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 new file mode 100644 index 00000000000..c225f7fee34 --- /dev/null +++ b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/ResultWithInstance.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.model; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.springframework.core.env.Environment; + +public class ResultWithInstance { + private int result; + + private String serviceId; + + private String instanceId; + + private String version; + + public static ResultWithInstance create(int value, Environment environment) { + ResultWithInstance result = new ResultWithInstance(); + result.setResult(value); + result.setVersion(BootStrapProperties.readServiceVersion(environment)); + + return result; + } + + public int getResult() { + return result; + } + + public void setResult(int result) { + this.result = result; + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public String getInstanceId() { + return instanceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + @Override + public String toString() { + return "ResultWithInstance [result=" + result + ", serviceId=" + serviceId + ", instanceId=" + instanceId + + ", version=" + version + "]"; + } +} diff --git a/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/User.java b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/User.java new file mode 100644 index 00000000000..5c93dddf0d8 --- /dev/null +++ b/demo/demo-edge/model/src/main/java/org/apache/servicecomb/demo/edge/model/User.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.demo.edge.model; + +public class User { + private String name; + + private int age; + + private String userId; + + private String body1; + + private String body2; + + private String body3; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getBody1() { + return body1; + } + + public void setBody1(String body1) { + this.body1 = body1; + } + + public String getBody2() { + return body2; + } + + public void setBody2(String body2) { + this.body2 = body2; + } + + public String getBody3() { + return body3; + } + + public void setBody3(String body3) { + this.body3 = body3; + } +} diff --git a/demo/demo-edge/pom.xml b/demo/demo-edge/pom.xml new file mode 100644 index 00000000000..34cdcd1fccc --- /dev/null +++ b/demo/demo-edge/pom.xml @@ -0,0 +1,68 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-edge + Java Chassis::Demo::Edge + pom + + + + org.apache.servicecomb + solution-basic + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + + + model + edge-service + business-1.0.0 + business-1-1-0 + business-2.0.0 + authentication + consumer + + + 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 new file mode 100644 index 00000000000..6c39fb4f5ad --- /dev/null +++ b/demo/demo-filter/filter-client/pom.xml @@ -0,0 +1,108 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-filter + 3.4.0-SNAPSHOT + + filter-client + Java Chassis::Demo::Filter::Client + + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + foundation-test-scaffolding + + + + + org.apache.servicecomb.demo.filter.FilterClient + + + + + + 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-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 new file mode 100644 index 00000000000..ab454f7c9e2 --- /dev/null +++ b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/FilterClient.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.demo.filter; + +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 FilterClient { + private static final Logger LOGGER = LoggerFactory.getLogger(FilterClient.class); + + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder(FilterClient.class).web(WebApplicationType.NONE).run(args); + + run(); + } catch (Throwable e) { + TestMgr.check("success", "failed"); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + } + + public static void run() throws Exception { + 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 new file mode 100644 index 00000000000..3d64b25b426 --- /dev/null +++ b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/ClientExceptionSchema.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.filter.client; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.provider.consumer.InvokerUtils; +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.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.client.RestOperations; + +@RestSchema(schemaId = "ClientExceptionSchema") +@RequestMapping(path = "/exception", produces = MediaType.APPLICATION_JSON_VALUE) +public class ClientExceptionSchema { + interface IExceptionSchema { + boolean blockingException(); + + CompletableFuture reactiveException(); + } + + private IExceptionSchema exceptionSchema; + + private RestOperations restTemplate = RestTemplateBuilder.create(); + + @RpcReference(microserviceName = "com.servicecomb.filterServer", schemaId = "ExceptionSchema") + public void setExceptionSchema(IExceptionSchema exceptionSchema) { + this.exceptionSchema = exceptionSchema; + } + + @GetMapping(path = "/blockingExceptionRestTemplate") + public boolean blockingExceptionRestTemplate() { + return restTemplate.getForObject( + "servicecomb://com.servicecomb.filterServer/exception/blockingException", boolean.class); + } + + @GetMapping(path = "/blockingExceptionReference") + public boolean blockingExceptionReference() { + return exceptionSchema.blockingException(); + } + + @GetMapping(path = "/blockingExceptionInvoker") + public boolean blockingExceptionInvoker() { + return InvokerUtils.syncInvoke("com.servicecomb.filterServer", + "ExceptionSchema", "blockingException", null, boolean.class); + } + + @GetMapping(path = "/reactiveExceptionReference") + public CompletableFuture reactiveExceptionReference() { + return exceptionSchema.reactiveException(); + } +} 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 new file mode 100644 index 00000000000..159c52fe340 --- /dev/null +++ b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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.RestOperations; + +import jakarta.ws.rs.core.Response.Status; + +@RestSchema(schemaId = "RetryClientSchema") +@RequestMapping(path = "/retry", produces = MediaType.APPLICATION_JSON_VALUE) +public class RetryClientSchema { + interface RetrySchemaInf { + CompletableFuture successWhenRetryAsync(); + } + + @RpcReference(microserviceName = "com.servicecomb.filterServer", schemaId = "RetrySchema") + private RetrySchemaInf retrySchemaInf; + + 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://com.servicecomb.filterServer/retry/governance/successWhenRetry", + Boolean.class); + } + + @GetMapping(path = "/governance/successWhenRetryAsync") + public CompletableFuture successWhenRetryAsync() { + return retrySchemaInf.successWhenRetryAsync(); + } +} diff --git a/demo/demo-filter/filter-client/src/main/resources/log4j2.xml b/demo/demo-filter/filter-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-filter/filter-client/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/demo/demo-filter/filter-client/src/main/resources/microservice.yaml b/demo/demo-filter/filter-client/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..72f90936c83 --- /dev/null +++ b/demo/demo-filter/filter-client/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: filtertest + # test service name with dot + name: com.servicecomb.filterClient + version: 0.0.1 + + registry: + sc: + address: http://127.0.0.1:30100 + rest: + address: 0.0.0.0:8082 + server: + compression: true + highway: + address: 0.0.0.0:7072 + + 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-edge/pom.xml b/demo/demo-filter/filter-edge/pom.xml new file mode 100644 index 00000000000..798f7b7697e --- /dev/null +++ b/demo/demo-filter/filter-edge/pom.xml @@ -0,0 +1,112 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-filter + 3.4.0-SNAPSHOT + + filter-edge + Java Chassis::Demo::Filter::Edge + + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + edge-core + + + org.apache.servicecomb + foundation-test-scaffolding + + + + + org.apache.servicecomb.demo.filter.FilterEdge + + + + + + 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-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 new file mode 100644 index 00000000000..8ea4b7cb22d --- /dev/null +++ b/demo/demo-filter/filter-edge/src/main/java/org/apache/servicecomb/demo/filter/FilterEdge.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.demo.filter; + +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 FilterEdge { + private static final Logger LOGGER = LoggerFactory.getLogger(FilterEdge.class); + + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder(FilterEdge.class).web(WebApplicationType.NONE).run(args); + + run(); + } catch (Throwable e) { + TestMgr.check("success", "failed"); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + } + + public static void run() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("filterEdge"); + } +} diff --git a/demo/demo-filter/filter-edge/src/main/resources/log4j2.xml b/demo/demo-filter/filter-edge/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-filter/filter-edge/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml b/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..128a825e6d6 --- /dev/null +++ b/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: filtertest + # test service name with dot + name: com.servicecomb.filterEdge + version: 0.0.1 + + registry: + sc: + address: http://127.0.0.1:30100 + + rest: + address: 0.0.0.0:9090 + server: + compression: true + + edge: + filter: + addHeader: + allowedHeaders: X-B3-TraceId + + invocation: + exception: + print-stack-trace: true + + http: + dispatcher: + edge: + default: + enabled: true + prefix: service + withVersion: false + prefixSegmentCount: 2 + url: + enabled: true + mappings: + filterClient: + prefixSegmentCount: 0 + path: "/retry/.*" + 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 new file mode 100644 index 00000000000..b3f257fd875 --- /dev/null +++ b/demo/demo-filter/filter-server/pom.xml @@ -0,0 +1,102 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-filter + 3.4.0-SNAPSHOT + + filter-server + Java Chassis::Demo::Filter::Server + + + + org.apache.servicecomb.demo + demo-schema + + + + + org.apache.servicecomb.demo.filter.FilterServer + + + + + 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-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 new file mode 100644 index 00000000000..c947b0e7d0b --- /dev/null +++ b/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/FilterServer.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.demo.filter; + +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 FilterServer { + private static final Logger LOGGER = LoggerFactory.getLogger(FilterServer.class); + + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder(FilterServer.class).web(WebApplicationType.NONE).run(args); + + run(); + } catch (Throwable e) { + TestMgr.check("success", "failed"); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + } + + public static void run() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("filterServer"); + } +} 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 new file mode 100644 index 00000000000..980681b6b96 --- /dev/null +++ b/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/ExceptionSchema.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.filter.server; + +import java.util.concurrent.CompletableFuture; + +import jakarta.ws.rs.core.Response.Status; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +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; + +@RestSchema(schemaId = "ExceptionSchema") +@RequestMapping(path = "/exception", produces = MediaType.APPLICATION_JSON_VALUE) +public class ExceptionSchema { + @GetMapping(path = "/blockingException") + public boolean blockingException() { + throw new InvocationException(Status.SERVICE_UNAVAILABLE, new CommonExceptionData("Blocking Exception")); + } + + @GetMapping(path = "/reactiveException") + public CompletableFuture reactiveException() { + throw new InvocationException(Status.SERVICE_UNAVAILABLE, new CommonExceptionData("Reactive Exception")); + } +} 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 new file mode 100644 index 00000000000..603f2a79e55 --- /dev/null +++ b/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/RetrySchema.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.filter.server; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; + +import jakarta.ws.rs.core.Response.Status; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +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; + +// test cases for retry +@RestSchema(schemaId = "RetrySchema") +@RequestMapping(path = "/retry", produces = MediaType.APPLICATION_JSON_VALUE) +public class RetrySchema { + private AtomicLong counter = new AtomicLong(0); + + @GetMapping(path = "/governance/successWhenRetry") + public boolean successWhenRetry() { + if (counter.getAndIncrement() % 3 != 0) { + throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "try again later."); + } + return true; + } + + @GetMapping(path = "/governance/successWhenRetryAsync") + public CompletableFuture successWhenRetryAsync() { + 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; + } +} diff --git a/demo/demo-filter/filter-server/src/main/resources/log4j2.xml b/demo/demo-filter/filter-server/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-filter/filter-server/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/demo/demo-filter/filter-server/src/main/resources/microservice.yaml b/demo/demo-filter/filter-server/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..bb96cf5f576 --- /dev/null +++ b/demo/demo-filter/filter-server/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: filtertest + # test service name with dot + name: com.servicecomb.filterServer + version: 0.0.1 + + registry: + sc: + address: http://127.0.0.1:30100 + + rest: + address: 0.0.0.0:8080 + server: + compression: true + 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 +ssl.checkCN.host: true + +#########certificates config +ssl.trustStore: trust.jks +ssl.trustStoreType: JKS +ssl.trustStoreValue: Changeme_123 +ssl.keyStore: server.p12 +ssl.keyStoreType: PKCS12 +ssl.keyStoreValue: Changeme_123 +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 new file mode 100644 index 00000000000..3936a85f517 --- /dev/null +++ b/demo/demo-filter/filter-tests/pom.xml @@ -0,0 +1,192 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-filter + 3.4.0-SNAPSHOT + + filter-tests + Java Chassis::Demo::Filter::Tests + + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + registry-local + + + + + org.apache.servicecomb.demo.filter.FilterTests + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service30100 + + alias + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + filter-server:${project.version} + filter-server + + alias + + + -Dservicecomb.registry.sc.address=http://service30100:30100 + + /maven/maven/filter-server-${project.version}.jar + + + service30100:service30100 + + + ServiceComb is ready + + + 8080 + + + + + + 8080:8080 + + + + + filter-client:${project.version} + filter-client + + alias + + + -Dservicecomb.registry.sc.address=http://service30100:30100 + + /maven/maven/filter-client-${project.version}.jar + + + service30100:service30100 + + + ServiceComb is ready + + + 8082 + + + + + + 8082:8082 + + + + + filter-edge:${project.version} + filter-edge + + alias + + + -Dservicecomb.registry.sc.address=http://service30100:30100 + + /maven/maven/filter-edge-${project.version}.jar + + + service30100:service30100 + + + 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-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 new file mode 100644 index 00000000000..0c7551b685d --- /dev/null +++ b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/FilterTests.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.demo.filter; + +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 FilterTests { + private static final Logger LOGGER = LoggerFactory.getLogger(FilterTests.class); + + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder(FilterTests.class).web(WebApplicationType.NONE).run(args); + + run(); + } catch (Throwable e) { + TestMgr.check("success", "failed"); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + } + + public static void run() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("filterTests"); + } +} 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 new file mode 100644 index 00000000000..e404b38b8a9 --- /dev/null +++ b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestExceptionSchemaFromClient.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.filter.tests; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +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.stereotype.Component; +import org.springframework.web.client.RestOperations; + +@Component +public class TestExceptionSchemaFromClient implements CategorizedTestCase { + + RestOperations restTemplate = RestTemplateBuilder.create(); + + private static final String SERVER = "servicecomb://com.servicecomb.filterClient"; + + @Override + public String getMicroserviceName() { + return "filterEdge"; + } + + @Override + public void testAllTransport() throws Exception { + testBlockingExceptionRestTemplate(); + testBlockingExceptionReference(); + testBlockingExceptionInvoker(); + testReactiveExceptionReference(); + } + + private void testBlockingExceptionRestTemplate() { + try { + restTemplate.getForObject(SERVER + "/exception/blockingExceptionRestTemplate", + boolean.class); + } catch (InvocationException e) { + TestMgr.check(503, e.getStatus().getStatusCode()); + TestMgr.check("Blocking Exception", ((CommonExceptionData) e.getErrorData()).getMessage()); + } + } + + private void testBlockingExceptionReference() { + try { + restTemplate.getForObject(SERVER + "/exception/blockingExceptionReference", + boolean.class); + } catch (InvocationException e) { + TestMgr.check(503, e.getStatus().getStatusCode()); + TestMgr.check("Blocking Exception", ((CommonExceptionData) e.getErrorData()).getMessage()); + } + } + + private void testBlockingExceptionInvoker() { + try { + restTemplate.getForObject(SERVER + "/exception/blockingExceptionInvoker", + boolean.class); + } catch (InvocationException e) { + TestMgr.check(503, e.getStatus().getStatusCode()); + TestMgr.check("Blocking Exception", ((CommonExceptionData) e.getErrorData()).getMessage()); + } + } + + private void testReactiveExceptionReference() { + try { + restTemplate.getForObject(SERVER + "/exception/reactiveExceptionReference", + boolean.class); + } catch (InvocationException e) { + TestMgr.check(503, e.getStatus().getStatusCode()); + TestMgr.check("Reactive Exception", ((CommonExceptionData) e.getErrorData()).getMessage()); + } + } +} 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 new file mode 100644 index 00000000000..944f7831910 --- /dev/null +++ b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromClient.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.demo.filter.tests; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +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; + +@Component +public class TestRetrySchemaFromClient implements CategorizedTestCase { + interface RetrySchemaInf { + boolean successWhenRetry(); + + CompletableFuture successWhenRetryAsync(); + } + + @RpcReference(microserviceName = "com.servicecomb.filterClient", schemaId = "RetryClientSchema") + private RetrySchemaInf retrySchemaInf; + + RestOperations restTemplate = RestTemplateBuilder.create(); + + private static final String SERVER = "servicecomb://com.servicecomb.filterClient"; + + @Override + public String getMicroserviceName() { + return "com.servicecomb.filterClient"; + } + + @Override + public void testAllTransport() throws Exception { + testRetryGovernanceRestTemplate(); + testRetryGovernanceRpc(); + } + + private void testRetryGovernanceRpc() throws Exception { + TestMgr.check(retrySchemaInf.successWhenRetry(), true); + TestMgr.check(retrySchemaInf.successWhenRetry(), true); + + TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true); + TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true); + } + + private void testRetryGovernanceRestTemplate() { + TestMgr.check(restTemplate.getForObject(SERVER + "/retry/governance/successWhenRetry", boolean.class), true); + TestMgr.check(restTemplate.getForObject(SERVER + "/retry/governance/successWhenRetry", boolean.class), true); + } +} 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 new file mode 100644 index 00000000000..b28eaff3cbc --- /dev/null +++ b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.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.demo.filter.tests; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +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 = "com.servicecomb.filterEdge", schemaId = "RetryClientSchema") + private RetrySchemaInf retrySchemaInf; + + RestOperations restTemplate = RestTemplateBuilder.create(); + + RestTemplate springRestTemplate = new RestTemplate(); + + 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 "com.servicecomb.filterEdge"; + } + + @Override + public void testRestTransport() throws Exception { + testRetryGovernanceFromEdgeDefaultDispatcher(); + testRetryGovernanceRestTemplate(); + testRetryGovernanceRpc(); + testEdgeRetryGovernanceRpc(); + } + + private void testRetryGovernanceRpc() throws Exception { + TestMgr.check(retrySchemaInf.successWhenRetry(), true); + TestMgr.check(retrySchemaInf.successWhenRetry(), true); + + TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true); + 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); + TestMgr.check(restTemplate.getForObject( + SERVER + "/retry/governance/successWhenRetry", boolean.class), true); + } + + private void testRetryGovernanceFromEdgeDefaultDispatcher() { + TestMgr.check(springRestTemplate.getForObject( + EDGE_SERVER + "/service/com.servicecomb.filterClient/retry/governance/successWhenRetry", boolean.class), true); + TestMgr.check(springRestTemplate.getForObject( + EDGE_SERVER + "/service/com.servicecomb.filterClient/retry/governance/successWhenRetry", boolean.class), true); + } +} diff --git a/demo/demo-filter/filter-tests/src/main/resources/log4j2.xml b/demo/demo-filter/filter-tests/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-filter/filter-tests/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml b/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..5694d54ac56 --- /dev/null +++ b/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: filtertest + # test service name with dot + name: com.servicecomb.filterTests + version: 0.0.1 + + registry: + sc: + address: http://127.0.0.1:30100 + + invocation: + exception: + print-stack-trace: true 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/registry.yaml b/demo/demo-filter/filter-tests/src/main/resources/registry.yaml new file mode 100644 index 00000000000..1e09e18eea2 --- /dev/null +++ b/demo/demo-filter/filter-tests/src/main/resources/registry.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. +## --------------------------------------------------------------------------- +com.servicecomb.filterEdge: + - id: "001" + version: "1.0" + appid: filtertest + instances: + - endpoints: + - rest://127.0.0.1:9090 diff --git a/demo/demo-filter/filter-tests/src/test/java/org/apache/servicecomb/demo/filter/retry/FilterTestsIT.java b/demo/demo-filter/filter-tests/src/test/java/org/apache/servicecomb/demo/filter/retry/FilterTestsIT.java new file mode 100644 index 00000000000..d09de401c20 --- /dev/null +++ b/demo/demo-filter/filter-tests/src/test/java/org/apache/servicecomb/demo/filter/retry/FilterTestsIT.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.filter.retry; + +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.filter.FilterTests; +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 = FilterTests.class) +public class FilterTestsIT { + + @BeforeEach + public void setUp() { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + FilterTests.run(); + + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} diff --git a/demo/demo-filter/pom.xml b/demo/demo-filter/pom.xml new file mode 100644 index 00000000000..45e0206faf1 --- /dev/null +++ b/demo/demo-filter/pom.xml @@ -0,0 +1,62 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-filter + Java Chassis::Demo::Filter + pom + + filter-server + filter-client + filter-edge + filter-tests + + + + org.apache.servicecomb + solution-basic + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + diff --git a/demo/demo-jaxrs/jaxrs-client/pom.xml b/demo/demo-jaxrs/jaxrs-client/pom.xml index 58792693053..d25a3cdecca 100644 --- a/demo/demo-jaxrs/jaxrs-client/pom.xml +++ b/demo/demo-jaxrs/jaxrs-client/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-jaxrs - 0.3.0-SNAPSHOT - - jaxrs-client + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + + org.apache.servicecomb.demo + demo-jaxrs + 3.4.0-SNAPSHOT + + jaxrs-client + Java Chassis::Demo::JAXRS::Client - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-jaxrs - - - io.servicecomb - provider-springmvc - - + + + org.apache.servicecomb.demo + demo-schema + + - - io.servicecomb.demo.jaxrs.client.JaxrsClient - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - + + org.apache.servicecomb.demo.jaxrs.JaxrsClient + + + + docker + + 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 + + + + + diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/io/servicecomb/demo/jaxrs/client/CodeFirstRestTemplateJaxrs.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/io/servicecomb/demo/jaxrs/client/CodeFirstRestTemplateJaxrs.java deleted file mode 100644 index 4a1f78cf033..00000000000 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/io/servicecomb/demo/jaxrs/client/CodeFirstRestTemplateJaxrs.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.client; - -import org.springframework.web.client.RestTemplate; - -import io.servicecomb.demo.CodeFirstRestTemplate; -import io.servicecomb.demo.TestMgr; - -public class CodeFirstRestTemplateJaxrs extends CodeFirstRestTemplate { - @Override - protected void testExtend(RestTemplate template, String cseUrlPrefix) { - super.testExtend(template, cseUrlPrefix); - - testDefaultPath(template, cseUrlPrefix); - } - - private void testDefaultPath(RestTemplate template, String cseUrlPrefix) { - int result = - template.getForObject(cseUrlPrefix.substring(0, cseUrlPrefix.length() - 1), Integer.class); - TestMgr.check(100, result); - } -} diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/io/servicecomb/demo/jaxrs/client/JaxrsClient.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/io/servicecomb/demo/jaxrs/client/JaxrsClient.java deleted file mode 100644 index 67e0c471989..00000000000 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/io/servicecomb/demo/jaxrs/client/JaxrsClient.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.client; - -import java.util.HashMap; -import java.util.Map; - -import javax.ws.rs.core.MediaType; - -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; - -import io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.core.CseContext; -import io.servicecomb.demo.CodeFirstRestTemplate; -import io.servicecomb.demo.DemoConst; -import io.servicecomb.demo.TestMgr; -import io.servicecomb.demo.compute.Person; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.provider.springmvc.reference.RestTemplateBuilder; - -public class JaxrsClient { - private static RestTemplate templateNew = RestTemplateBuilder.create(); - - public static void main(String[] args) throws Exception { - init(); - - run(); - - TestMgr.summary(); - } - - public static void init() throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } - - public static void run() throws Exception { - CodeFirstRestTemplate codeFirstClient = new CodeFirstRestTemplateJaxrs(); - codeFirstClient.testCodeFirst(templateNew, "jaxrs", "/codeFirstJaxrs/"); - testCompute(templateNew); - } - - private static void testCompute(RestTemplate template) throws Exception { - String microserviceName = "jaxrs"; - for (String transport : DemoConst.transports) { - CseContext.getInstance().getConsumerProviderManager().setTransport(microserviceName, transport); - TestMgr.setMsg(microserviceName, transport); - - String cseUrlPrefix = "cse://" + microserviceName; - - testGet(template, cseUrlPrefix); - testPost(template, cseUrlPrefix); - testPut(template, cseUrlPrefix); - testDelete(template, cseUrlPrefix); - testExchange(template, cseUrlPrefix); - testRawJsonParam(template, cseUrlPrefix); - } - } - - private static void testGet(RestTemplate template, String cseUrlPrefix) { - Map params = new HashMap<>(); - params.put("a", "5"); - params.put("b", "3"); - int result = - template.getForObject(cseUrlPrefix + "/compute/reduce?a={a}&b={b}", Integer.class, params); - TestMgr.check(2, result); - - result = template.getForObject(cseUrlPrefix + "/compute/reduce?a={a}&b={b}", Integer.class, 5, 4); - TestMgr.check(1, result); - - result = template.getForObject(cseUrlPrefix + "/compute/reduce?a=5&b=6", - Integer.class); - TestMgr.check(-1, result); - } - - private static void testPost(RestTemplate template, String cseUrlPrefix) { - Map params = new HashMap<>(); - params.put("a", "5"); - params.put("b", "3"); - int result = - template.postForObject(cseUrlPrefix + "/compute/add", params, Integer.class); - TestMgr.check(8, result); - - Person person = new Person(); - person.setName("world"); - Person resultPerson = - template.postForObject(cseUrlPrefix + "/compute/sayhello", person, Person.class); - TestMgr.check("hello world", resultPerson.getName()); - - HttpHeaders headers = new HttpHeaders(); - headers.add("prefix", "haha"); - HttpEntity reqEntity = new HttpEntity<>(person, headers); - TestMgr.check("haha world", - template.postForObject(cseUrlPrefix + "/compute/saysomething", reqEntity, String.class)); - } - - private static void testPut(RestTemplate template, String cseUrlPrefix) { - template.put(cseUrlPrefix + "/compute/sayhi/{name}", null, "world"); - } - - private static void testDelete(RestTemplate 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) { - HttpHeaders headers = new HttpHeaders(); - headers.add("Accept", MediaType.APPLICATION_JSON); - Person person = new Person(); - person.setName("world"); - HttpEntity requestEntity = new HttpEntity<>(person, headers); - ResponseEntity resEntity = template.exchange(cseUrlPrefix + "/compute/sayhello", - HttpMethod.POST, - requestEntity, - Person.class); - TestMgr.check("hello world", resEntity.getBody()); - - ResponseEntity resEntity2 = - template.exchange(cseUrlPrefix + "/compute/addstring?s=abc&s=def", HttpMethod.DELETE, null, String.class); - TestMgr.check("abcdef", resEntity2.getBody()); - } - - private static void testRawJsonParam(RestTemplate template, String cseUrlPrefix) throws Exception { - Map person = new HashMap<>(); - person.put("name", "Tom"); - String jsonPerson = RestObjectMapper.INSTANCE.writeValueAsString(person); - TestMgr.check("hello Tom", - template.postForObject(cseUrlPrefix + "/compute/testrawjson", jsonPerson, String.class)); - } -} 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 new file mode 100644 index 00000000000..2428e654774 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsClient.java @@ -0,0 +1,555 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +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; +import org.apache.servicecomb.demo.RestObjectMapperWithStringMapper; +import org.apache.servicecomb.demo.RestObjectMapperWithStringMapperNotWriteNull; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.compute.Person; +import org.apache.servicecomb.demo.jaxrs.client.CodeFirstRestTemplateJaxrs; +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.common.utils.BeanUtils; +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.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.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; + +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response.Status; + +@SpringBootApplication +public class JaxrsClient { + private static final Logger LOGGER = LoggerFactory.getLogger(JaxrsClient.class); + + private static RestOperations templateNew = RestTemplateBuilder.create(); + + public static void main(String[] args) throws Exception { + new SpringApplicationBuilder(JaxrsClient.class).web(WebApplicationType.NONE).run(args); + + init(); + + try { + run(); + } catch (Exception 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): 540 -------------"); + } + + public static void init() throws Exception { + RestObjectMapperFactory.setDefaultRestObjectMapper(new RestObjectMapperWithStringMapper()); + RestObjectMapperFactory.setConsumerWriterMapper(new RestObjectMapperWithStringMapperNotWriteNull()); + } + + public static void run() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("jaxrs"); + + CodeFirstRestTemplate codeFirstClient = BeanUtils.getBean(CodeFirstRestTemplateJaxrs.class); + codeFirstClient.testCodeFirst(templateNew, "jaxrs", "/codeFirstJaxrs/"); + testCompute(templateNew); + testValidator(templateNew); + testJaxRSDefaultValuesAllTransport(templateNew); + testSpringMvcDefaultValuesJavaPrimitiveAllTransport(templateNew); + + DefaultModelServiceClient.run(); + ValidationServiceClient.run(); + + testOnlyRest(templateNew); + } + + private static void testOnlyRest(RestOperations template) { + String microserviceName = "jaxrs"; + String cseUrlPrefix = "cse://" + microserviceName; + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, "rest"); + + testGetRest(template, cseUrlPrefix); + testSpringMvcDefaultValuesJavaPrimitiveRest(templateNew); + } + + private static void testCompute(RestOperations template) throws Exception { + String microserviceName = "jaxrs"; + for (String transport : DemoConst.transports) { + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); + TestMgr.setMsg(microserviceName, transport); + + String cseUrlPrefix = "cse://" + microserviceName; + + testGetAllTransport(template, cseUrlPrefix); + testPost(template, cseUrlPrefix); + testPut(template, cseUrlPrefix); + testDelete(template, cseUrlPrefix); + testExchange(template, cseUrlPrefix); + testRawJsonParam(template, cseUrlPrefix); + } + } + + private static void testValidator(RestOperations template) { + String microserviceName = "jaxrs"; + for (String transport : DemoConst.transports) { + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); + TestMgr.setMsg(microserviceName, transport); + + String cseUrlPrefix = "cse://" + microserviceName + "/validator/"; + + testValidatorAddSuccess(template, cseUrlPrefix); + if ("rest".equals(transport)) { + testValidatorAddFail(template, cseUrlPrefix); + testValidatorSayHiFail(template, cseUrlPrefix); + testValidatorExchangeFail(template, cseUrlPrefix); + testJaxRSDefaultValuesRest(template); + } else if ("highway".equals(transport)) { + testValidatorAddFail(template, cseUrlPrefix); + testValidatorSayHiFail(template, cseUrlPrefix); + testValidatorExchangeFail(template, cseUrlPrefix); + } + testValidatorSayHiSuccess(template, cseUrlPrefix); + testValidatorExchangeSuccess(template, cseUrlPrefix); + } + } + + private static void testJaxRSDefaultValuesRest(RestOperations template) { + String result; + String microserviceName = "jaxrs"; + String cseUrlPrefix = "cse://" + microserviceName + "/JaxRSDefaultValues/"; + boolean failed = false; + try { + result = template.getForObject(cseUrlPrefix + "/query2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + + failed = false; + try { + result = template.getForObject(cseUrlPrefix + "/query2?d=2&e=2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(failed, true); + + failed = false; + try { + result = template.getForObject(cseUrlPrefix + "/query2?a=&d=2&e=2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(failed, true); + + result = template.getForObject(cseUrlPrefix + "/query2?d=30&e=2", String.class); + TestMgr.check("Hello 20bobo40302", result); + + failed = false; + try { + result = template.getForObject(cseUrlPrefix + "/query3?a=2&b=2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(failed, true); + } + + private static void testJaxRSDefaultValuesAllTransport(RestOperations template) { + String microserviceName = "jaxrs"; + for (String transport : DemoConst.transports) { + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); + TestMgr.setMsg(microserviceName, transport); + + String cseUrlPrefix = "cse://" + microserviceName + "/JaxRSDefaultValues/"; + + //default values + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap map = new LinkedMultiValueMap<>(); + HttpEntity> request = new HttpEntity<>(map, headers); + String result = template.postForObject(cseUrlPrefix + "/form", request, String.class); + // 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); + result = template.postForObject(cseUrlPrefix + "/header", entity, String.class); + TestMgr.check("Hello 20bobo30", result); + + result = template.getForObject(cseUrlPrefix + "/query?d=10", String.class); + TestMgr.check("Hello 20bobo4010", result); + boolean failed = false; + try { + result = template.getForObject(cseUrlPrefix + "/query2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + + failed = false; + try { + result = template.getForObject(cseUrlPrefix + "/query2?d=2&e=2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(failed, true); + + failed = false; + try { + result = template.getForObject(cseUrlPrefix + "/query2?a=&d=2&e=2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(failed, true); + + result = template.getForObject(cseUrlPrefix + "/query2?d=30&e=2", String.class); + TestMgr.check("Hello 20bobo40302", result); + + failed = false; + try { + result = template.getForObject(cseUrlPrefix + "/query3?a=2&b=2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(failed, true); + + result = template.getForObject(cseUrlPrefix + "/query3?a=30&b=2", String.class); + TestMgr.check("Hello 302", result); + + result = template.getForObject(cseUrlPrefix + "/query3?a=30", String.class); + TestMgr.check("Hello 30null", result); + + //input values + headers = new HttpHeaders(); + headers.setContentType(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED); + Map params = new HashMap<>(); + params.put("a", "30"); + params.put("b", "sam"); + HttpEntity> requestPara = new HttpEntity<>(params, headers); + result = template.postForObject(cseUrlPrefix + "/form", requestPara, String.class); + TestMgr.check("Hello 30sam", result); + + headers = new HttpHeaders(); + headers.add("a", "30"); + headers.add("b", "sam"); + headers.add("c", "40"); + entity = new HttpEntity<>(null, headers); + result = template.postForObject(cseUrlPrefix + "/header", entity, String.class); + TestMgr.check("Hello 30sam40", result); + + result = template.getForObject(cseUrlPrefix + "/query?a=3&b=sam&c=5&d=30", String.class); + TestMgr.check("Hello 3sam530", result); + + result = template.getForObject(cseUrlPrefix + "/query2?a=3&b=4&c=5&d=30&e=2", String.class); + TestMgr.check("Hello 345302", result); + } + } + + private static void testGetRest(RestOperations template, String cseUrlPrefix) { + Map params = new HashMap<>(); + params.put("a", "5"); + params.put("b", "3"); + int result = + template.getForObject(cseUrlPrefix + "/compute/reduce?a={a}&b={b}", Integer.class, params); + TestMgr.check(2, result); + + result = template.getForObject(cseUrlPrefix + "/compute/reduce?a={a}&b={b}", Integer.class, 5, 4); + TestMgr.check(1, result); + + result = template.getForObject(cseUrlPrefix + "/compute/reduce?a=5&b=6", + Integer.class); + TestMgr.check(-1, result); + } + + private static void testGetAllTransport(RestOperations template, String cseUrlPrefix) { + Map params = new HashMap<>(); + params.put("a", "5"); + params.put("b", "3"); + int result = + template.getForObject(cseUrlPrefix + "/compute/reduce?a={a}&b={b}", Integer.class, params); + TestMgr.check(2, result); + + result = template.getForObject(cseUrlPrefix + "/compute/reduce?a={a}&b={b}", Integer.class, 5, 4); + TestMgr.check(1, result); + + result = template.getForObject(cseUrlPrefix + "/compute/reduce?a=5&b=6", + Integer.class); + TestMgr.check(-1, result); + } + + private static void testPost(RestOperations template, String cseUrlPrefix) { + Map params = new HashMap<>(); + params.put("a", "5"); + params.put("b", "3"); + int result = + template.postForObject(cseUrlPrefix + "/compute/add", params, Integer.class); + TestMgr.check(8, result); + + Person person = new Person(); + person.setName("world"); + Person resultPerson = + template.postForObject(cseUrlPrefix + "/compute/sayhello", person, Person.class); + TestMgr.check("hello world", resultPerson.getName()); + + HttpHeaders headers = new HttpHeaders(); + headers.add("prefix", "haha"); + HttpEntity reqEntity = new HttpEntity<>(person, headers); + TestMgr.check("haha world", + template.postForObject(cseUrlPrefix + "/compute/saysomething", reqEntity, String.class)); + } + + private static void testPut(RestOperations template, String cseUrlPrefix) { + template.put(cseUrlPrefix + "/compute/sayhi/{name}", null, "world"); + } + + 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(RestOperations template, String cseUrlPrefix) { + HttpHeaders headers = new HttpHeaders(); + headers.add("Accept", MediaType.APPLICATION_JSON); + Person person = new Person(); + person.setName("world"); + HttpEntity requestEntity = new HttpEntity<>(person, headers); + ResponseEntity resEntity = template.exchange(cseUrlPrefix + "/compute/sayhello", + HttpMethod.POST, + requestEntity, + Person.class); + TestMgr.check("hello world", resEntity.getBody()); + + ResponseEntity resEntity2 = + template.exchange(cseUrlPrefix + "/compute/addstring?s=abc&s=def", HttpMethod.DELETE, null, String.class); + TestMgr.check("abcdef", resEntity2.getBody()); + } + + private static void testRawJsonParam(RestOperations template, String cseUrlPrefix) throws Exception { + Map person = new HashMap<>(); + person.put("name", "Tom"); + String jsonPerson = RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(person); + TestMgr.check("hello Tom", + template.postForObject(cseUrlPrefix + "/compute/testrawjson", jsonPerson, String.class)); + } + + @SuppressWarnings({"rawtypes"}) + private static void testValidatorAddFail(RestOperations template, String cseUrlPrefix) { + Map params = new HashMap<>(); + params.put("a", "5"); + params.put("b", "3"); + boolean isExcep = false; + try { + template.postForObject(cseUrlPrefix + "add", params, Integer.class); + } catch (InvocationException e) { + isExcep = true; + TestMgr.check(400, e.getStatus().getStatusCode()); + TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); + // 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 + CommonExceptionData data = (CommonExceptionData) e.getErrorData(); + TestMgr.check("invalid parameters.", data.getMessage()); + TestMgr.check("add.b", + ((Map) ((List) data.getDynamic("validateDetail")).get(0)).get("propertyPath").toString()); + } else { + // rest decode/encode 'Object' using json without type information, got map. Users can got runtime type by adding @JsonTypeInfo to the model. + Map data = (Map) e.getErrorData(); + TestMgr.check("invalid parameters.", data.get("message").toString()); + TestMgr.check("add.b", + ((Map) ((List) data.get("validateDetail")).get(0)).get("propertyPath").toString()); + } + } + + TestMgr.check(true, isExcep); + } + + private static void testValidatorAddSuccess(RestOperations template, String cseUrlPrefix) { + Map params = new HashMap<>(); + params.put("a", "5"); + params.put("b", "20"); + int result = template.postForObject(cseUrlPrefix + "add", params, Integer.class); + TestMgr.check(25, result); + } + + @SuppressWarnings({"rawtypes"}) + private static void testValidatorSayHiFail(RestOperations template, String cseUrlPrefix) { + boolean isExcep = false; + try { + template.exchange(cseUrlPrefix + "sayhi/{name}", HttpMethod.PUT, null, String.class, "te"); + } catch (InvocationException e) { + isExcep = true; + TestMgr.check(400, e.getStatus().getStatusCode()); + TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); + // 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(); + TestMgr.check("invalid parameters.", data.getMessage()); + TestMgr.check("sayHi.name", + ((Map) ((List) data.getDynamic("validateDetail")).get(0)).get("propertyPath").toString()); + } else { + Map data = (Map) e.getErrorData(); + TestMgr.check("invalid parameters.", data.get("message").toString()); + TestMgr.check("sayHi.name", + ((Map) ((List) data.get("validateDetail")).get(0)).get("propertyPath").toString()); + } + } + TestMgr.check(true, isExcep); + } + + 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.getStatusCode().value()); + TestMgr.check("world sayhi", responseEntity.getBody()); + } + + @SuppressWarnings({"rawtypes"}) + private static void testValidatorExchangeFail(RestOperations template, String cseUrlPrefix) { + HttpHeaders headers = new HttpHeaders(); + headers.add("Accept", MediaType.APPLICATION_JSON); + Student student = new Student(); + student.setName(""); + student.setAge(25); + boolean isExcep = false; + try { + HttpEntity requestEntity = new HttpEntity<>(student, headers); + template.exchange(cseUrlPrefix + "/sayhello", + HttpMethod.POST, + requestEntity, + Student.class); + } catch (InvocationException e) { + isExcep = true; + TestMgr.check(400, e.getStatus().getStatusCode()); + TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); + // 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(); + TestMgr.check("invalid parameters.", data.getMessage()); + TestMgr.check("sayHello.student.age", + ((Map) ((List) data.getDynamic("validateDetail")).get(0)).get("propertyPath").toString()); + } else { + Map data = (Map) e.getErrorData(); + TestMgr.check("invalid parameters.", data.get("message").toString()); + TestMgr.check("sayHello.student.age", + ((Map) ((List) data.get("validateDetail")).get(0)).get("propertyPath").toString()); + } + } + TestMgr.check(true, isExcep); + } + + private static void testValidatorExchangeSuccess(RestOperations template, String cseUrlPrefix) { + Student student = new Student(); + student.setName("test"); + student.setAge(15); + Student result = template.postForObject(cseUrlPrefix + "sayhello", student, Student.class); + TestMgr.check("hello test 15", result); + } + + private static void testSpringMvcDefaultValuesJavaPrimitiveRest(RestOperations template) { + String microserviceName = "jaxrs"; + String cseUrlPrefix = "cse://" + microserviceName + "/JaxRSDefaultValues/"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap map = new LinkedMultiValueMap<>(); + HttpEntity> request = new HttpEntity<>(map, headers); + + //default values with primitive + String result = template.postForObject(cseUrlPrefix + "/javaprimitiveint", request, String.class); + // 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); + + result = template.postForObject(cseUrlPrefix + "/javaprimitivestr", request, String.class); + TestMgr.check("Hello", result); + + result = template.postForObject(cseUrlPrefix + "/javaprimitivecomb", request, String.class); + TestMgr.check("Hello nullnull", result); + + result = template.postForObject(cseUrlPrefix + "/allprimitivetypes", null, String.class); + TestMgr.check("Hello false,\0,0,0,0,0,0.0,0.0,null", result); + } + + private static void testSpringMvcDefaultValuesJavaPrimitiveAllTransport(RestOperations template) { + String microserviceName = "jaxrs"; + for (String transport : DemoConst.transports) { + CategorizedTestCaseRunner.changeTransport(microserviceName, transport); + + String cseUrlPrefix = "cse://" + microserviceName + "/JaxRSDefaultValues/"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap map = new LinkedMultiValueMap<>(); + HttpEntity> request = new HttpEntity<>(map, headers); + + //default values with primitive + String result = template.postForObject(cseUrlPrefix + "/javaprimitiveint", request, String.class); + // 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); + + result = template.postForObject(cseUrlPrefix + "/javaprimitivestr", request, String.class); + TestMgr.check("Hello", result); + + result = template.postForObject(cseUrlPrefix + "/javaprimitivecomb", request, String.class); + TestMgr.check("Hello nullnull", result); + + result = template.postForObject(cseUrlPrefix + "/allprimitivetypes", null, String.class); + TestMgr.check("Hello false,\0,0,0,0,0,0.0,0.0,null", 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 new file mode 100644 index 00000000000..66c5ab958b4 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CodeFirstRestTemplateJaxrs.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.demo.jaxrs.client; + +import org.apache.servicecomb.demo.CodeFirstRestTemplate; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestOperations; + +@Component +public class CodeFirstRestTemplateJaxrs extends CodeFirstRestTemplate { + @Override + protected void testAllTransport(String microserviceName, RestOperations template, String cseUrlPrefix) { + testDefaultPath(template, cseUrlPrefix); + test404(template); + + super.testAllTransport(microserviceName, template, 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, RestOperations template, String cseUrlPrefix) { + super.testOnlyRest(microserviceName, template, cseUrlPrefix); + } + + + 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.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 new file mode 100644 index 00000000000..9614164a05f --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CustomLoadbalanceExtensionsFactory.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.jaxrs.client; + +import org.apache.servicecomb.loadbalance.Configuration; +import org.apache.servicecomb.loadbalance.ExtensionsFactory; +import org.apache.servicecomb.loadbalance.RoundRobinRuleExt; +import org.apache.servicecomb.loadbalance.RuleExt; +import org.springframework.stereotype.Component; + +@Component +public class CustomLoadbalanceExtensionsFactory implements ExtensionsFactory { + + static class MyCustomRule extends RoundRobinRuleExt { + + } + + @Override + public boolean isSupport(String key, String value) { + return (Configuration.RULE_STRATEGY_NAME.equals(key) && + "mycustomrule".equals(value)); + } + + @Override + public RuleExt createLoadBalancerRule(String ruleName) { + return new MyCustomRule(); + } +} 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 new file mode 100644 index 00000000000..5eba5b94c95 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java @@ -0,0 +1,258 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.List; +import java.util.Map; + +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +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.springmvc.reference.RestTemplateBuilder; +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.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 RestOperations template = RestTemplateBuilder.create(); + + @Override + public void testAllTransport() throws Exception { + testErrorCode(); + testErrorCodeWithHeader(); + testErrorCodeWithHeaderJAXRS(); + testErrorCodeWithHeaderJAXRSUsingRowType(); + testNoClientErrorCode(); + } + + @Override + public void testRestTransport() throws Exception { + testErrorCodeWrongType(); + } + + @Override + public void testHighwayTransport() throws Exception { + + } + + + 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); + try { + template + .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 { + template + .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", entity, MultiResponse200.class); + TestMgr.fail("expect failed."); + } catch (InvocationException e) { + TestMgr.check(e.getStatusCode(), 400); + } + + // wrong type + body = "{\"message\":\"hello\",\"code\":\"200\"}"; + entity = new HttpEntity<>(body, headers); + 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() { + MultiRequest request = new MultiRequest(); + + request.setCode(200); + ResponseEntity result = template + .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", request, MultiResponse200.class); + TestMgr.check(result.getStatusCode().value(), 200); + TestMgr.check(result.getBody().getMessage(), "success result"); + + request.setCode(400); + MultiResponse400 t400 = null; + try { + template.postForEntity(SERVER + "/MultiErrorCodeService/errorCode", request, MultiResponse400.class); + } catch (InvocationException e) { + t400 = (MultiResponse400) e.getErrorData(); + } + TestMgr.check(t400.getCode(), 400); + TestMgr.check(t400.getMessage(), "bad request"); + + request.setCode(500); + MultiResponse500 t500 = null; + try { + template.postForEntity(SERVER + "/MultiErrorCodeService/errorCode", request, MultiResponse400.class); + } catch (InvocationException e) { + t500 = (MultiResponse500) e.getErrorData(); + } + TestMgr.check(t500.getCode(), 500); + TestMgr.check(t500.getMessage(), "internal error"); + } + + private static void testErrorCodeWithHeader() { + MultiRequest request = new MultiRequest(); + + request.setCode(200); + ResponseEntity result = template + .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeader", request, MultiResponse200.class); + 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); + + request.setCode(400); + MultiResponse400 t400 = null; + try { + template.postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeader", request, MultiResponse400.class); + } catch (InvocationException e) { + t400 = (MultiResponse400) e.getErrorData(); + TestMgr.check(e.getStatus().getStatusCode(), Status.BAD_REQUEST.getStatusCode()); + } + TestMgr.check(t400.getCode(), 400); + TestMgr.check(t400.getMessage(), "bad request"); + + request.setCode(500); + MultiResponse500 t500 = null; + try { + template.postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeader", request, MultiResponse400.class); + } catch (InvocationException e) { + t500 = (MultiResponse500) e.getErrorData(); + TestMgr.check(e.getStatus().getStatusCode(), Status.INTERNAL_SERVER_ERROR.getStatusCode()); + } + TestMgr.check(t500.getCode(), 500); + TestMgr.check(t500.getMessage(), "internal error"); + } + + private static void testErrorCodeWithHeaderJAXRS() { + MultiRequest request = new MultiRequest(); + + request.setCode(200); + request.setMessage("success result"); + ResponseEntity result = template + .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeaderJAXRS", request, MultiResponse200.class); + 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); + + request.setCode(400); + request.setMessage("bad request"); + MultiResponse400 t400 = null; + try { + template + .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeaderJAXRS", request, MultiResponse400.class); + } catch (InvocationException e) { + t400 = (MultiResponse400) e.getErrorData(); + TestMgr.check(e.getStatus().getStatusCode(), Status.BAD_REQUEST.getStatusCode()); + } + TestMgr.check(t400.getCode(), 400); + TestMgr.check(t400.getMessage(), "bad request"); + + request.setCode(500); + request.setMessage("internal error"); + MultiResponse500 t500 = null; + try { + template + .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeaderJAXRS", request, MultiResponse400.class); + } catch (InvocationException e) { + t500 = (MultiResponse500) e.getErrorData(); + TestMgr.check(e.getStatus().getStatusCode(), Status.INTERNAL_SERVER_ERROR.getStatusCode()); + } + TestMgr.check(t500.getCode(), 500); + TestMgr.check(t500.getMessage(), "internal error"); + } + + private static void testErrorCodeWithHeaderJAXRSUsingRowType() { + JsonObject requestJson = new JsonObject(); + requestJson.put("code", 200); + requestJson.put("message", "test message"); + + ResponseEntity result = template + .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeaderJAXRS", requestJson, MultiResponse200.class); + 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); + + MultiRequest request = new MultiRequest(); + request.setCode(200); + request.setMessage("test message"); + String stringRequest = Json.encode(request); + // wrap request to JsonObject + result = template + .postForEntity(SERVER + "/MultiErrorCodeService/errorCodeWithHeaderJAXRS", new JsonObject(stringRequest), + MultiResponse200.class); + 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); + } + + private static void testNoClientErrorCode() { + JsonObject requestJson = new JsonObject(); + requestJson.put("code", 200); + requestJson.put("message", "test message"); + + @SuppressWarnings("rawtypes") + ResponseEntity listResult = template + .postForEntity(SERVER + "/MultiErrorCodeService/noClientErrorCode", requestJson, List.class); + TestMgr.check(listResult.getStatusCode().value(), 200); + Map mapResult = + RestObjectMapperFactory.getRestObjectMapper().convertValue(listResult.getBody().get(0), Map.class); + TestMgr.check(mapResult.get("message"), "test message"); + TestMgr.check(mapResult.get("code"), 200); + TestMgr.check(mapResult.get("t200"), 200); + + try { + requestJson.put("code", 400); + template + .postForEntity(SERVER + "/MultiErrorCodeService/noClientErrorCode", requestJson, Object.class); + } catch (InvocationException e) { + TestMgr.check(e.getStatusCode(), 400); + mapResult = RestObjectMapperFactory.getRestObjectMapper().convertValue(e.getErrorData(), Map.class); + TestMgr.check(mapResult.get("message"), "test message"); + TestMgr.check(mapResult.get("code"), 400); + TestMgr.check(mapResult.get("t400"), 400); + } + } +} diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/SchemeInterfaceJaxrs.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/SchemeInterfaceJaxrs.java new file mode 100644 index 00000000000..a074ad90423 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/SchemeInterfaceJaxrs.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.jaxrs.client; + +import org.springframework.data.domain.Page; + +public interface SchemeInterfaceJaxrs { + int add(int a, int b); + + int reduce(int a, int b); + + Page interfaceModel(Page model); +} 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 new file mode 100644 index 00000000000..7d8017eb38d --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestClientTimeout.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.jaxrs.client; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.validator.Student; +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.RestOperations; + +@Component +public class TestClientTimeout implements CategorizedTestCase { + private static RestOperations template = RestTemplateBuilder.create(); + + public void testAllTransport() throws Exception { + testClientTimeOut(template); + } + + private static void testClientTimeOut(RestOperations template) { + String microserviceName = "jaxrs"; + + String cseUrlPrefix = "cse://" + microserviceName + "/clientreqtimeout/"; + + testClientTimeoutSayHi(template, cseUrlPrefix); + testClientTimeoutAdd(template, cseUrlPrefix); + } + + private static void testClientTimeoutSayHi(RestOperations template, String cseUrlPrefix) { + Student student = new Student(); + student.setName("timeout"); + student.setAge(30); + Student result = template.postForObject(cseUrlPrefix + "sayhello", student, Student.class); + TestMgr.check("hello timeout 30", result); + } + + private static void testClientTimeoutAdd(RestOperations template, String cseUrlPrefix) { + Map params = new HashMap<>(); + params.put("a", "5"); + params.put("b", "20"); + boolean failed = false; +// long failures = 0; +// ServiceCombServerStats serviceCombServerStats = null; + try { +// serviceCombServerStats = getServiceCombServerStats(); +// failures = serviceCombServerStats.getContinuousFailureCount(); + template.postForObject(cseUrlPrefix + "add", params, Integer.class); + } catch (InvocationException e) { + failed = true; + // implement timeout with same error code and message for rest and highway + TestMgr.check(408, e.getStatus().getStatusCode()); + // Request Timeout or Invocation Timeout + TestMgr.check(true, + e.getErrorData().toString().contains("Timeout.")); + } + + TestMgr.check(true, failed); + } +} diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestCodeFirstJaxrs.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestCodeFirstJaxrs.java new file mode 100644 index 00000000000..4e6cbd97deb --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestCodeFirstJaxrs.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.jaxrs.client; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.core.provider.consumer.InvokerUtils; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.server.User; +import org.apache.servicecomb.foundation.vertx.http.ReadStreamPart; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.type.TypeReference; + +@Component +public class TestCodeFirstJaxrs implements CategorizedTestCase { + interface DownloadInf { + ReadStreamPart testDeleteAfterFinished(String name, String content); + } + + private static final String SERVICE_NAME = "jaxrs"; + + private static final String SCHEMA_ID = "codeFirst"; + + @RpcReference(microserviceName = SERVICE_NAME, schemaId = SCHEMA_ID) + private DownloadInf downloadInf; + + @Override + public void testAllTransport() throws Exception { + testCodeFirstJaxrs(); + testResponseLong(); + } + + @Override + public void testHighwayTransport() throws Exception { + // test only once + testInstanceIsolation(); + } + + private void testInstanceIsolation() { + AtomicInteger e503Business = new AtomicInteger(0); + AtomicInteger e503CircuitBreaker = new AtomicInteger(0); + + for (int i = 0; i < 30; i++) { + try { + InvokerUtils.syncInvoke(SERVICE_NAME, SCHEMA_ID, "instanceIsolationTest", null, + String.class); + } catch (InvocationException e) { + if (e.getStatusCode() == 503) { + if ("CommonExceptionData [message=business]".equals(e.getErrorData().toString())) { + e503Business.getAndIncrement(); + } else if ("CommonExceptionData [message=instance isolation circuitBreaker is open.]".equals( + e.getErrorData().toString())) { + e503CircuitBreaker.getAndIncrement(); + } else { + TestMgr.fail("not expected message"); + } + } else { + TestMgr.fail("not expected code"); + } + } + } + TestMgr.check(true, e503Business.get() >= 10); + TestMgr.check(true, e503CircuitBreaker.get() >= 10); + } + + @Override + public void testRestTransport() throws Exception { + testDeleteAfterFinished(); + } + + private void testDeleteAfterFinished() throws Exception { + ReadStreamPart part = downloadInf.testDeleteAfterFinished("hello", "hello content"); + TestMgr.check(part.saveAsString().get(), "hello content"); + File systemTempFile = new File(System.getProperty("java.io.tmpdir")); + File file = new File(systemTempFile, "hello"); + TestMgr.check(file.exists(), false); + } + + private void testResponseLong() { + Object result = InvokerUtils.syncInvoke(SERVICE_NAME, SCHEMA_ID, "responseLong", null, + Object.class); + TestMgr.check(result, Long.MAX_VALUE); + } + + // invoke CodeFirstJaxrs + private void testCodeFirstJaxrs() { + Map swaggerArguments = new HashMap<>(); + Map userMap = new HashMap<>(); + User user = new User(); + user.setName("hello"); + userMap.put("user", user); + swaggerArguments.put("userMap", userMap); + TypeReference> type = new TypeReference>() { + }; + Map result = InvokerUtils.syncInvoke(SERVICE_NAME, SCHEMA_ID, "testUserMap", swaggerArguments, + type.getType()); + TestMgr.check(result.get("user").getName(), userMap.get("user").getName()); + } +} diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestCodeFirstJaxrsReactive.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestCodeFirstJaxrsReactive.java new file mode 100644 index 00000000000..2fdaffe3edb --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestCodeFirstJaxrsReactive.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.jaxrs.client; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +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 TestCodeFirstJaxrsReactive implements CategorizedTestCase { + interface AddOperation { + CompletableFuture add(int a, int b); + } + + @RpcReference(microserviceName = "jaxrs", schemaId = "codeFirst") + AddOperation addOperation; + + @Override + public void testAllTransport() throws Exception { + final int count = 10; + CountDownLatch latch = new CountDownLatch(count); + AtomicInteger result = new AtomicInteger(0); + + for (int i = 0; i < count; i++) { + new Thread(() -> addOperation.add(1, 2) + .whenComplete((r, e) -> addOperation.add(r, r).whenComplete((r1, e1) -> { + result.addAndGet(r1); + latch.countDown(); + }))).start(); + } + + latch.await(3, TimeUnit.SECONDS); + TestMgr.check(count * 6, result.get()); + } +} diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestDynamicConfig.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestDynamicConfig.java new file mode 100644 index 00000000000..9dfa234a923 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestDynamicConfig.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.Arrays; + +import org.apache.servicecomb.config.inject.InjectProperties; +import org.apache.servicecomb.config.inject.InjectProperty; +import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.stereotype.Component; + +@Component +public class TestDynamicConfig implements BootListener { + @InjectProperties(prefix = "jaxrstest.jaxrsclient") + public static class Configuration { + /* + * 方法的 prefix 属性值 "override" 会覆盖标注在类定义的 @InjectProperties + * 注解的 prefix 属性值。 + * + * keys属性可以为一个字符串数组,下标越小优先级越高。 + * + * 这里会按照如下顺序的属性名称查找配置属性,直到找到已被配置的配置属性,则停止查找: + * 1) jaxrstest.jaxrsclient.override.high + * 2) jaxrstest.jaxrsclient.override.low + * + * 测试用例: + * jaxrstest.jaxrsclient.override.high: hello high + * jaxrstest.jaxrsclient.override.low: hello low + * 预期: + * hello high + */ + @InjectProperty(prefix = "jaxrstest.jaxrsclient.override", keys = {"high", "low"}) + public String strValue; + + /** + * keys支持通配符,并在可以在将配置属性注入的时候指定通配符的代入对象。 + * + * 测试用例: + * jaxrstest.jaxrsclient.k.value: 3 + * 预期: + * 3 + */ + @InjectProperty(keys = "${key}.value") + public int intValue; + + /** + * 通配符的代入对象可以是一个字符串List,优先级遵循数组元素下标越小优先级越高策略。 + * + * 测试用例: + * jaxrstest.jaxrsclient.l1-1: 3.0 + * jaxrstest.jaxrsclient.l1-2: 2.0 + * + * 预期: + * 3.0 + */ + @InjectProperty(keys = "${full-list}") + public float floatValue; + + /** + * keys属性也支持多个通配符,优先级如下:首先通配符的优先级从左到右递减, + * 然后如果通配符被代入List,遵循List中元素index越小优先级越高策略。 + * + * 测试用例: + * jaxrstest.jaxrsclient.low-1.a.high-1.b: 1 + * jaxrstest.jaxrsclient.low-1.a.high-2.b: 2 + * jaxrstest.jaxrsclient.low-2.a.high-1.b: 3 + * jaxrstest.jaxrsclient.low-2.a.high-2.b: 4 + * 预期: + * 1 + */ + @InjectProperty(keys = "${low-list}.a.${high-list}.b") + public long longValue; + + /** + * 可以通过注解的defaultValue属性指定默认值。如果字段未关联任何配置属性, + * 定义的默认值会生效,否则默认值会被覆盖。 + * + * 测试用例: + * 预期: + * abc + */ + @InjectProperty(defaultValue = "abc") + public String strDef; + } + + @Override + public void onAfterRegistry(BootEvent event) { + Configuration configuration = SCBEngine.getInstance().getPriorityPropertyManager() + .createConfigObject(Configuration.class, + "key", "k", + "low-list", Arrays.asList("low-1", "low-2"), + "high-list", Arrays.asList("high-1", "high-2"), + "full-list", Arrays.asList("l1-1", "l1-2") + ); + TestMgr.check(configuration.strValue, "hello high"); + TestMgr.check(configuration.intValue, 3); + TestMgr.check(configuration.floatValue, 3.0); + TestMgr.check(configuration.longValue, 1); + TestMgr.check(configuration.strDef, "abc"); + } +} 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 new file mode 100644 index 00000000000..591302058de --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFormRequestSchema.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.jaxrs.client; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; + + +@Component +public class TestFormRequestSchema implements CategorizedTestCase { + + private static final Logger LOGGER = LoggerFactory.getLogger(TestFormRequestSchema.class); + + private final RestOperations restTemplate = RestTemplateBuilder.create(); + + @Override + public void testRestTransport() throws Exception { + testFormRequestFail(); + // testFormRequestFail会关闭连接,防止下个测试用例失败,睡眠2s + Thread.sleep(2000); + testFormRequestSuccess(); + testFormRequestBufferSize(); + } + + // formSize is less than default maxFormAttributeSize , success + private void testFormRequestSuccess() { + try { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap formData = new LinkedMultiValueMap<>(); + formData.add("formData", "a".repeat(512)); + HttpEntity> requestEntity = new HttpEntity<>(formData, headers); + ResponseEntity responseEntity = restTemplate + .postForEntity("cse://jaxrs/form/formRequest", requestEntity, String.class); + TestMgr.check(responseEntity.getBody(), "formRequest success : 512"); + } catch (Throwable e) { + LOGGER.error("testFormRequestSuccess-->", e); + TestMgr.failed("", e); + } + } + + // formSize is greater than default maxFormAttributeSize , throw exception + private void testFormRequestFail() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap formData = new LinkedMultiValueMap<>(); + formData.add("formData", "a".repeat(1688)); + HttpEntity> requestEntity = new HttpEntity<>(formData, headers); + try { + restTemplate.postForEntity("cse://jaxrs/form/formRequest", requestEntity, String.class); + TestMgr.fail("Size exceed allowed maximum capacity"); + } catch (Throwable e) { + 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 new file mode 100644 index 00000000000..8c0933172a6 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestQueryParamSchema.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.jaxrs.client; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +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; + +@Component +public class TestQueryParamSchema implements CategorizedTestCase { + public interface QueryParamService { + String testQueryEncode(String param); + } + + private final RestOperations restTemplate = RestTemplateBuilder.create(); + + @RpcReference(microserviceName = "jaxrs", schemaId = "QueryParamSchema") + private QueryParamService queryParamService; + + @Override + public void testAllTransport() throws Exception { + testQueryParamEncodingPlus(); + } + + private void testQueryParamEncodingPlus() { + // should encode + + TestMgr.check("a+b", + restTemplate.getForObject("servicecomb://jaxrs/queryParam/testQueryEncode?param={1}", String.class, "a+b")); + // if not encoded, + should be blank + TestMgr.check("a b", + restTemplate.getForObject("servicecomb://jaxrs/queryParam/testQueryEncode?param=a+b", String.class)); + + // rpc should encode + TestMgr.check("a+b", queryParamService.testQueryEncode("a+b")); + } +} 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 new file mode 100644 index 00000000000..19aa1c66ee8 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestQueryParamWithListSchema.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; + +@Component +public class TestQueryParamWithListSchema implements CategorizedTestCase { + private RestOperations restTemplate = RestTemplateBuilder.create(); + + @Override + public void testAllTransport() throws Exception { + testMulti(); + testCSV(); + testSSV(); + testPipes(); + } + + @Override + public void testRestTransport() throws Exception { + testMultiRest(); + testCSVRest(); + testSSVRest(); + testPipesRest(); + } + + @Override + // highway do not handle empty/default/null + public void testHighwayTransport() throws Exception { + testMultiHighway(); + testCSVHighway(); + testSSVHighway(); + testPipesHighway(); + } + + private void testCSVHighway() { + TestMgr.check("null", + restTemplate.getForObject("cse://jaxrs/queryList/queryListCSV?", String.class)); + } + + private void testCSVRest() { + TestMgr.check("0:[]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListCSV?", String.class)); + } + + private void testSSV() { + TestMgr.check("1:[1%202]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList=1%202", String.class)); + 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)); + + TestMgr.check("2:[1, 2]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList={1}", String.class, "1 2")); + TestMgr.check("2:[, ]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList={1}", String.class, " ")); + TestMgr.check("1:[]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?queryList=", String.class)); + } + + private void testPipesHighway() { + TestMgr.check("null", + restTemplate.getForObject("cse://jaxrs/queryList/queryListPIPES?", String.class)); + } + + private void testPipesRest() { + TestMgr.check("0:[]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListPIPES?", String.class)); + } + + private void testPipes() { + TestMgr.check("2:[1, 2]", + restTemplate + .getForObject("cse://jaxrs/queryList/queryListPIPES?queryList={1}", String.class, "1|2")); + TestMgr.check("2:[, ]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListPIPES?queryList={1}", String.class, "|")); + TestMgr.check("1:[]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListPIPES?queryList=", String.class)); + } + + private void testSSVHighway() { + TestMgr.check("null", + restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?", String.class)); + } + + private void testSSVRest() { + TestMgr.check("0:[]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListSSV?", String.class)); + } + + private void testCSV() { + TestMgr.check("2:[1, 2]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListCSV?queryList=1,2", String.class)); + TestMgr.check("2:[, ]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListCSV?queryList=,", String.class)); + TestMgr.check("1:[]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListCSV?queryList=", String.class)); + } + + private void testMultiHighway() { + TestMgr.check("null", + restTemplate.getForObject("cse://jaxrs/queryList/queryListMULTI?", String.class)); + } + + private void testMultiRest() { + TestMgr.check("0:[]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListMULTI?", String.class)); + } + + private void testMulti() { + TestMgr.check("2:[1, 2]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListMULTI?queryList=1&queryList=2", String.class)); + TestMgr.check("2:[, ]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListMULTI?queryList=&queryList=", String.class)); + TestMgr.check("1:[]", + restTemplate.getForObject("cse://jaxrs/queryList/queryListMULTI?queryList=", String.class)); + } +} 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 new file mode 100644 index 00000000000..6126c053ad3 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestReactiveSchema.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.jaxrs.client; + +import java.util.Map; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +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.RestOperations; + +@Component +public class TestReactiveSchema implements CategorizedTestCase { + @SuppressWarnings("rawtypes") + public void testRestTransport() throws Exception { + 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"), + "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/TestSchemeInterfaceJaxrs.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestSchemeInterfaceJaxrs.java new file mode 100644 index 00000000000..dd53006df29 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestSchemeInterfaceJaxrs.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Component; + +@Component +public class TestSchemeInterfaceJaxrs implements CategorizedTestCase { + @RpcReference(schemaId = "SchemeInterfaceJaxrs", microserviceName = "jaxrs") + private SchemeInterfaceJaxrs jaxrs; + + public void testRestTransport() throws Exception { + List contents = new ArrayList<>(); + contents.add("hello"); + Sort sort = Sort.by(new String[0]); + Pageable pageable = PageRequest.of(1, 10, sort); + Page pages = new PageImpl<>(contents, pageable, 1); + Page result = jaxrs.interfaceModel(pages); + TestMgr.check("hello", result.stream().findFirst().get()); + } + + public void testAllTransport() throws Exception { + TestMgr.check(3, jaxrs.add(1, 2)); + TestMgr.check(0, jaxrs.add(-1, 1)); + + try { + jaxrs.reduce(1, 3); + TestMgr.failed("should throw exception", new Exception()); + } catch (InvocationException e) { + TestMgr.check( + "Consumer method org.apache.servicecomb.demo.jaxrs.client.SchemeInterfaceJaxrs:reduce " + + "not exist in contract, microserviceName=jaxrs, schemaId=SchemeInterfaceJaxrs.", + ((CommonExceptionData) e.getError()).getMessage()); + } + } +} diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamPojoClient.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamPojoClient.java new file mode 100644 index 00000000000..5f34487335a --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamPojoClient.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.demo.jaxrs.client.beanParam; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.Invoker; +import org.springframework.stereotype.Component; + +@Component +public class BeanParamPojoClient implements CategorizedTestCase { + private BeanParamTestServiceIntf beanParamTestServiceIntf; + + public BeanParamPojoClient() { + beanParamTestServiceIntf = Invoker.createProxy("jaxrs", "beanParamTest", BeanParamTestServiceIntf.class); + } + + @Override + public void testRestTransport() throws Exception { + testBeanParam(); + testUpload(); + } + + @Override + public void testHighwayTransport() throws Exception { + + } + + @Override + public void testAllTransport() throws Exception { + testBeanParam(); + } + + private void testBeanParam() { + String result = beanParamTestServiceIntf.beanParameterTest("querySwaggerValue", 2, "pathSwaggerValue", 10, "extra"); + TestMgr.check( + "invocationContextConsistency=true|testBeanParameter=TestBeanParameter{queryStr='querySwaggerValue', headerInt=2, " + + "pathStr='pathSwaggerValue', cookieLong=10}|extraQuery=extra", + result); + } + + 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))); + + String result = beanParamTestServiceIntf.beanParameterTestUpload( + bufferedInputStream0, "queryTest", bufferedInputStream1, bufferedInputStream2, "ex"); + TestMgr.check( + "testBeanParameter=TestBeanParameterWithUpload{queryStr='queryTest'}|extraQuery=ex|up0=up0|up1=up1|up2=up2", + result); + } +} 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 new file mode 100644 index 00000000000..decb4a4207f --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamRestTemplateClient.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.demo.jaxrs.client.beanParam; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.springmvc.reference.CseHttpEntity; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; + +@Component +public class BeanParamRestTemplateClient implements CategorizedTestCase { + RestOperations restTemplate; + + public BeanParamRestTemplateClient() { + restTemplate = RestTemplateBuilder.create(); + } + + + @Override + public void testRestTransport() throws Exception { + testBeanParam(); + testUpload(); + } + + @Override + public void testHighwayTransport() throws Exception { + + } + + @Override + public void testAllTransport() throws Exception { + testBeanParam(); + } + + private void testBeanParam() { + HttpHeaders headers = new HttpHeaders(); + headers.add("Cookie", "cookieSwaggerLong=11"); + headers.add("headerSwaggerInt", "2"); + HttpEntity requestEntity1 = new CseHttpEntity<>(headers); + ResponseEntity result = restTemplate.exchange( + "cse://jaxrs/beanParamTest/pathSwaggerValue/simple?querySwaggerStr=querySwaggerValue&extraQuery=extra", + HttpMethod.GET, + requestEntity1, + String.class); + TestMgr.check( + "invocationContextConsistency=true|testBeanParameter=TestBeanParameter{queryStr='querySwaggerValue', headerInt=2, " + + "pathStr='pathSwaggerValue', cookieLong=11}|extraQuery=extra", + result.getBody()); + } + + 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))); + + HashMap formData = new HashMap<>(); + formData.put("up0", bufferedInputStream0); + formData.put("up1", bufferedInputStream1); + formData.put("up2", bufferedInputStream2); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + HttpEntity> entity = new HttpEntity<>(formData, headers); + + String result = restTemplate.postForObject("cse://jaxrs/beanParamTest/upload?query=fromTemplate&extraQuery=ex", + entity, + String.class); + TestMgr.check( + "testBeanParameter=TestBeanParameterWithUpload{queryStr='fromTemplate'}|extraQuery=ex|up0=up0|up1=up1|up2=up2", + result); + } +} diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamTestServiceIntf.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamTestServiceIntf.java new file mode 100644 index 00000000000..4149adc66a9 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/beanParam/BeanParamTestServiceIntf.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.jaxrs.client.beanParam; + +import java.io.InputStream; + +public interface BeanParamTestServiceIntf { + String beanParameterTest(String querySwaggerStr, Integer headerSwaggerInt, String pathSwaggerStr, + long cookieSwaggerLong, String extraQuery); + + String beanParameterTestUpload(InputStream up0, String query, InputStream up1, InputStream up2, String extraQuery); +} 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 new file mode 100644 index 00000000000..02d1ee1d97e --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/injectBean/TestInjectBeanSchema.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.jaxrs.client.injectBean; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; + +@Component +public class TestInjectBeanSchema implements CategorizedTestCase { + RestOperations restTemplate = RestTemplateBuilder.create(); + + @Override + public void testAllTransport() throws Exception { + boolean result = restTemplate.getForObject("cse://jaxrs/injectSet", boolean.class); + TestMgr.check(true, result); + } +} diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/pojoDefault/DefaultModel.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/pojoDefault/DefaultModel.java new file mode 100644 index 00000000000..1c5d64e64cf --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/pojoDefault/DefaultModel.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.jaxrs.client.pojoDefault; + +public class DefaultModel { + private int index; + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } +} 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 new file mode 100644 index 00000000000..a221853b6a8 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/pojoDefault/DefaultModelServiceClient.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.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.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.web.client.RestOperations; + +public class DefaultModelServiceClient { + private static RestOperations template = RestTemplateBuilder.create(); + + private static String urlPrefix = "cse://jaxrs/DefaultModelService"; + + public static void run() { + // highway do not support this feature + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport.jaxrs", "rest"); + testDefaultModelService(); + } + + private static void testDefaultModelService() { + DefaultModel model = new DefaultModel(); + model.setIndex(400); + DefaultResponseModel result = template.postForObject(urlPrefix + "/model", model, DefaultResponseModel.class); + TestMgr.check(result.getAge(), 200); + TestMgr.check(result.getIndex(), 400); + TestMgr.check(result.getName(), "World"); + TestMgr.check(result.getDesc(), null); + } +} 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 new file mode 100644 index 00000000000..4b99fff0359 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/validation/ValidationServiceClient.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.demo.jaxrs.client.validation; + +import java.util.ArrayList; + +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.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.RestOperations; + +import jakarta.ws.rs.core.Response.Status; + +public class ValidationServiceClient { + private static RestOperations template = RestTemplateBuilder.create(); + + private static String urlPrefix = "cse://jaxrs/ValidationService"; + + public static void run() { + // highway do not support this feature + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport.jaxrs", "rest"); + testValidation(); + } + + private static void testValidation() { + ValidationModel model = new ValidationModel(); + model.setAge(20); + model.setMembers(new ArrayList<>()); + model.setName("name"); + ValidationModel result = template.postForObject(urlPrefix + "/validate", model, ValidationModel.class); + TestMgr.check(result.getAge(), 20); + TestMgr.check(result.getName(), "name"); + TestMgr.check(result.getMembers().size(), 0); + + try { + model.setAge(null); + template.postForObject(urlPrefix + "/validate", model, ValidationModel.class); + TestMgr.check(false, true); + } catch (InvocationException e) { + TestMgr.check(400, e.getStatus().getStatusCode()); + TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); + TestMgr.check(e.getErrorData().toString().contains("propertyPath=errorCode.request.age"), true); + } + + try { + model.setAge(20); + model.setMembers(null); + template.postForObject(urlPrefix + "/validate", model, ValidationModel.class); + TestMgr.check(false, true); + } catch (InvocationException e) { + TestMgr.check(400, e.getStatus().getStatusCode()); + TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); + TestMgr.check(e.getErrorData().toString().contains("propertyPath=errorCode.request.members"), true); + } + + try { + template.postForObject(urlPrefix + "/validate", null, ValidationModel.class); + TestMgr.check(false, true); + } catch (InvocationException e) { + TestMgr.check(400, e.getStatus().getStatusCode()); + TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); + TestMgr.check(e.getErrorData().toString().contains("propertyPath=errorCode.request"), true); + } + + String strResult = template.getForObject(urlPrefix + "/validateQuery?name=a", String.class); + TestMgr.check(strResult, "a"); + + try { + template.getForObject(urlPrefix + "/validateQuery", String.class); + TestMgr.check(false, true); + } catch (InvocationException e) { + TestMgr.check(400, e.getStatus().getStatusCode()); + TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); + TestMgr.check(((CommonExceptionData) e.getErrorData()).getMessage().contains("Parameter is not valid"), true); + } + + Teacher teacher = new Teacher(); + teacher.setName("teacher"); + teacher.setAge("20"); + Teacher response = template.postForObject(urlPrefix + "/sayTeacherInfo", teacher, Teacher.class); + TestMgr.check(response.getName(), "teacher"); + + try { + teacher = new Teacher(); + teacher.setAge("20"); + template.postForObject(urlPrefix + "/sayTeacherInfo", teacher, Teacher.class); + TestMgr.fail("Name should not empty"); + } catch (InvocationException e) { + TestMgr.check(400, e.getStatus().getStatusCode()); + TestMgr.check(e.getErrorData().toString().contains("must not be blank"), true); + } + + // jax-rs body default required = false + response = template.postForObject(urlPrefix + "/sayTeacherInfo", null, Teacher.class); + TestMgr.check(null, response); + } +} 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 new file mode 100644 index 00000000000..ea5c2072ff5 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultResponseModel.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pojoDefault; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Null; + +public class DefaultResponseModel { + @Min(20) + @Max(2000) + @Null + private Integer age = 200; + + @Min(2) + @Max(30) + @Null + private String name = "World"; + + private int index; + + private String desc = "Hello"; + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + @Override + public String toString() { + return "index=" + index + ";name=" + name + ";age=" + age; + } +} diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/resources/log4j2.xml b/demo/demo-jaxrs/jaxrs-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + 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 826358ae3c5..07493f07ef8 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/resources/microservice.yaml +++ b/demo/demo-jaxrs/jaxrs-client/src/main/resources/microservice.yaml @@ -1,12 +1,94 @@ -APPLICATION_ID: jaxrstest -service_description: - name: jaxrsClient - version: 0.0.1 -cse: +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: jaxrstest + name: jaxrsClient + version: 0.0.1 + registry: + sc: + address: http://127.0.0.1:30100 + enableSwaggerRegistration: true + + matchGroup: + instanceIsolationTest: | + matches: + - apiPath: + prefix: "/codeFirstJaxrs/instanceIsolationTest" + instanceIsolation: + instanceIsolationTest: | + minimumNumberOfCalls: 10 + slidingWindowSize: 20 + slidingWindowType: COUNT_BASED + failureRateThreshold: 50 + slowCallRateThreshold: 100 + slowCallDurationThreshold: 3000 + waitDurationInOpenState: 200 + permittedNumberOfCallsInHalfOpenState: 10 + + request: + timeout: 30000 + jaxrs: + timeout: 30000 + clientreqtimeout: + timeout: 30000 + sayHello: + timeout: 30000 + add: + timeout: 1000 + + invocation: + timeout: + check: + enabled: false + + # use service center to find schema info + openAPI: registry: - address: http://127.0.0.1:9980 - handler: - chain: - Consumer: - default: bizkeeper-consumer,loadbalance + # 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 + # nacos: + # serverAddr: http://127.0.0.1:8848 + # group: jaxrstest + # dataId: jaxrsclient + # namespace: public + # contentType: properties # can be properties, yaml, raw. If using raw, property [group].[dataId]=value. + # addPrefix: false # if true [group].[dataId] will added as properties/yaml items prefix. Will not influence raw. + + # for integration test not install any config server +jaxrstest: + jaxrsclient: + override.high: hello high + override.low: hello low + k.value: 3 + l1-1: 3.0 + l1-2: 2.0 + low-1.a.high-1.b: 1 + low-1.a.high-2.b: 2 + low-2.a.high-1.b: 3 + low-2.a.high-2.b: 4 + +# test configurations. diff --git a/demo/demo-jaxrs/jaxrs-client/src/test/java/org/apache/servicecomb/demo/jaxrs/JaxrsIT.java b/demo/demo-jaxrs/jaxrs-client/src/test/java/org/apache/servicecomb/demo/jaxrs/JaxrsIT.java new file mode 100644 index 00000000000..c1bb351ba5d --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-client/src/test/java/org/apache/servicecomb/demo/jaxrs/JaxrsIT.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.jaxrs; + +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 = JaxrsClient.class) +public class JaxrsIT { + + @BeforeEach + public void setUp() { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + JaxrsClient.run(); + + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} diff --git a/demo/demo-jaxrs/jaxrs-server/pom.xml b/demo/demo-jaxrs/jaxrs-server/pom.xml index 9d70a78ae29..cc80ad1521c 100644 --- a/demo/demo-jaxrs/jaxrs-server/pom.xml +++ b/demo/demo-jaxrs/jaxrs-server/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-jaxrs - 0.3.0-SNAPSHOT - - jaxrs-server - - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-jaxrs - - - - - io.servicecomb.demo.jaxrs.server.JaxrsServer - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-build-config - 0.3.0-SNAPSHOT - - - - - - - - - - docker - - - - io.fabric8 - docker-maven-plugin - - - org.commonjava.maven.plugins - directory-maven-plugin - - - - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + + org.apache.servicecomb.demo + demo-jaxrs + 3.4.0-SNAPSHOT + + jaxrs-server + Java Chassis::Demo::JAXRS::Server + + + org.apache.servicecomb.demo + demo-schema + + + + org.glassfish.jersey.core + jersey-client + + + org.glassfish.jersey.core + jersey-common + + + org.apache.servicecomb + swagger-invocation-validator + + + + org.apache.servicecomb.demo.jaxrs.JaxrsServer + + + + + 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-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java deleted file mode 100644 index aac98ca3f54..00000000000 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.server; - -import java.util.Date; -import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -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 io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.demo.compute.Person; -import io.servicecomb.demo.server.User; -import io.servicecomb.provider.rest.common.RestSchema; -import io.servicecomb.swagger.extend.annotations.ResponseHeaders; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.context.ContextUtils; -import io.servicecomb.swagger.invocation.context.InvocationContext; -import io.servicecomb.swagger.invocation.response.Headers; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ResponseHeader; - -@RestSchema(schemaId = "codeFirst") -@Path("/codeFirstJaxrs") -@Produces(MediaType.APPLICATION_JSON) -public class CodeFirstJaxrs { - // public Response getUserResponse() { - // - // } - @ApiResponse(code = 200, response = User.class, message = "") - @ResponseHeaders({@ResponseHeader(name = "h1", response = String.class), - @ResponseHeader(name = "h2", response = String.class)}) - @Path("/cseResponse") - @GET - public Response cseResponse(InvocationContext c1) { - Response response = Response.createSuccess(Status.ACCEPTED, new User()); - Headers headers = response.getHeaders(); - headers.addHeader("h1", "h1v " + c1.getContext().toString()); - - InvocationContext c2 = ContextUtils.getInvocationContext(); - headers.addHeader("h2", "h2v " + c2.getContext().toString()); - - return response; - } - - @Path("/testUserMap") - @POST - public Map testUserMap(Map userMap) { - return userMap; - } - - @Path("/textPlain") - @POST - @Consumes(MediaType.TEXT_PLAIN) - public String textPlain(String body) { - return body; - } - - @Path("/bytes") - @POST - public byte[] bytes(byte[] input) { - input[0] = (byte) (input[0] + 1); - return input; - } - - @Path("/addDate") - @POST - public Date addDate(@FormParam("date") Date date, @QueryParam("seconds") long seconds) { - return new Date(date.getTime() + seconds * 1000); - } - - @GET - public int defaultPath() { - return 100; - } - - @Path("/add") - @POST - public int add(@FormParam("a") int a, @FormParam("b") int b) { - return a + b; - } - - @Path("/reduce") - @GET - @ApiImplicitParams({@ApiImplicitParam(name = "a", dataType = "integer", format = "int32", paramType = "query")}) - public int reduce(HttpServletRequest request, @CookieParam("b") int b) { - int a = Integer.parseInt(request.getParameter("a")); - return a - b; - } - - @Path("/sayhello") - @POST - public Person sayHello(Person user) { - user.setName("hello " + user.getName()); - return user; - } - - @SuppressWarnings("unchecked") - @Path("/testrawjson") - @POST - public String testRawJsonString(String jsonInput) { - Map person; - try { - person = RestObjectMapper.INSTANCE.readValue(jsonInput.getBytes(), Map.class); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - return "hello " + person.get("name"); - } - - @Path("/saysomething") - @POST - public String saySomething(@HeaderParam("prefix") String prefix, Person user) { - return prefix + " " + user.getName(); - } - - @Path("/sayhi/{name}") - @PUT - public String sayHi(@PathParam("name") String name) { - ContextUtils.getInvocationContext().setStatus(202); - return name + " sayhi"; - } - - @Path("/sayhi/{name}/v2") - @PUT - public String sayHi2(@PathParam("name") String name) { - return name + " sayhi 2"; - } - - @Path("/istrue") - @GET - public boolean isTrue() { - return true; - } - - @Path("/addstring") - @DELETE - @Produces(MediaType.TEXT_PLAIN) - public String addString(@QueryParam("s") List s) { - String result = ""; - for (String x : s) { - result += x; - } - return result; - } -} diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/ComputeImpl.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/ComputeImpl.java deleted file mode 100644 index 7d72701b5ad..00000000000 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/ComputeImpl.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.server; - -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.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.demo.compute.Person; -import io.servicecomb.provider.rest.common.RestSchema; -import io.servicecomb.swagger.invocation.context.ContextUtils; - -@RestSchema(schemaId = "compute") -@Path("/compute") -@Produces(MediaType.APPLICATION_JSON) -public class ComputeImpl { - private static final Logger LOGGER = LoggerFactory.getLogger(ComputeImpl.class); - - @Path("/add") - @POST - public int add(@FormParam("a") int a, @FormParam("b") int b) { - return a + b; - } - - @Path("/reduce") - @GET - public int reduce(@Context HttpServletRequest request) { - int a = Integer.parseInt(request.getParameter("a")); - int b = Integer.parseInt(request.getParameter("b")); - return a - b; - } - - @Path("/sayhello") - @POST - public Person sayHello(Person user) { - user.setName("hello " + user.getName()); - return user; - } - - @SuppressWarnings("unchecked") - @Path("/testrawjson") - @POST - public String testRawJsonString(String jsonInput) { - Map person; - try { - person = RestObjectMapper.INSTANCE.readValue(jsonInput.getBytes(), Map.class); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - return "hello " + person.get("name"); - } - - @Path("/saysomething") - @POST - public String saySomething(@HeaderParam("prefix") String prefix, Person user) { - return prefix + " " + user.getName(); - } - - @Path("/sayhi/{name}") - @PUT - public void sayHi(@PathParam("name") String name) { - LOGGER.info(name + " sayhi"); - ContextUtils.getInvocationContext().setStatus(202); - } - - @Path("/sayhi/{name}/v2") - @PUT - public void sayHi2(@PathParam("name") String name) { - LOGGER.info(name + " sayhi 2"); - } - - @Path("/sayhei") - @DELETE - public void sayHei(@QueryParam("name") String name) { - LOGGER.info(name + " sayhei"); - } - - @Path("/istrue") - @GET - public boolean isTrue() { - return true; - } - - @Path("/addstring") - @DELETE - @Produces(MediaType.TEXT_PLAIN) - public String addString(@QueryParam("s") String[] s) { - String result = ""; - for (String x : s) { - result += x; - } - return result; - } -} diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/JaxrsServer.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/JaxrsServer.java deleted file mode 100644 index 67fecfeadff..00000000000 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/JaxrsServer.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.server; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class JaxrsServer { - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} 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 new file mode 100644 index 00000000000..4cd136d0fbc --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsServer.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.jaxrs; + +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.demo.RestObjectMapperWithStringMapper; +import org.apache.servicecomb.demo.RestObjectMapperWithStringMapperNotWriteNull; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class JaxrsServer { + public static void main(String[] args) throws Exception { + new SpringApplicationBuilder(JaxrsServer.class).web(WebApplicationType.NONE).run(args); + + RestObjectMapperFactory.setDefaultRestObjectMapper(new RestObjectMapperWithStringMapper()); + RestObjectMapperFactory.setConsumerWriterMapper(new RestObjectMapperWithStringMapperNotWriteNull()); + } +} 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 new file mode 100644 index 00000000000..8626f86087a --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java @@ -0,0 +1,249 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +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.common.part.FilePart; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; +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.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(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(CoreConst.SRC_MICROSERVICE)); + + InvocationContext c2 = ContextUtils.getInvocationContext(); + response.setHeader("h2", "h2v " + c2.getContext().get(CoreConst.SRC_MICROSERVICE)); + + return response; + } + + @Path("/testUserMap") + @POST + public Map testUserMap(Map userMap) { + return userMap; + } + + @Path("/textPlain") + @POST + @Consumes(MediaType.TEXT_PLAIN) + public String textPlain(String body) { + return body; + } + + @Path("/bytes") + @POST + public byte[] bytes(byte[] input) { + input[0] = (byte) (input[0] + 1); + return input; + } + + @Path("/addDate") + @POST + public Date addDate(@FormParam("date") Date date, @QueryParam("seconds") long seconds) { + return new Date(date.getTime() + seconds * 1000); + } + + @GET + public int defaultPath() { + return 100; + } + + @Path("/add") + @POST + public int add(@FormParam("a") int a, @FormParam("b") int b) { + return a + b; + } + + @Path("/reduce") + @GET + @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; + } + + @Path("/sayhello") + @POST + public Person sayHello(Person user) { + user.setName("hello " + user.getName()); + return user; + } + + @SuppressWarnings("unchecked") + @Path("/testrawjson") + @POST + public String testRawJsonString(String jsonInput) { + Map person; + try { + person = RestObjectMapperFactory.getRestObjectMapper() + .readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + return "hello " + person.get("name"); + } + + @Path("/saysomething") + @POST + public String saySomething(@HeaderParam("prefix") String prefix, Person user) { + return prefix + " " + user.getName(); + } + + @Path("/sayhi/{name}") + @PUT + public String sayHi(@PathParam("name") String name) { + ContextUtils.getInvocationContext().setStatus(202); + return name + " sayhi"; + } + + @Path("/sayhi/{name}/v2") + @PUT + public String sayHi2(@PathParam("name") String name) { + return name + " sayhi 2"; + } + + @Path("/istrue") + @GET + public boolean isTrue() { + return true; + } + + @Path("/addstring") + @DELETE + @Produces(MediaType.TEXT_PLAIN) + public String addString(@QueryParam("s") List s) { + StringBuilder result = new StringBuilder(); + for (String x : s) { + result.append(x); + } + return result.toString(); + } + + @Path("/ignore") + @POST + public OutputModelForTestIgnore testModelWithIgnoreField(InputModelForTestIgnore input) { + return new OutputModelForTestIgnore("output_id", input.getInputId(), input.getContent(), input.getInputObject(), + input.getInputJsonObject(), input.getInputIgnoreInterface(), + new Person("outputSomeone"), new JsonObject("{\"OutputJsonKey\" : \"OutputJsonValue\"}"), () -> { + }); + } + + @SuppressWarnings("unchecked") + @Path("/rawJsonAnnotation") + @POST + public String testRawJsonAnnotation(@RawJsonRequestBody String jsonInput) { + Map person; + try { + person = RestObjectMapperFactory.getRestObjectMapper() + .readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + return "hello " + person.get("name"); + } + + @Path("/traceId") + @GET + public String getTraceId() { + return ContextUtils.getInvocationContext().getContext(CoreConst.TRACE_ID_NAME); + } + + @GET + @Path("/responseLong") + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Object.class)), description = "") + public Response responseLong() { + return Response.createSuccess(Long.MAX_VALUE); + } + + @Path("/download/testDeleteAfterFinished") + @GET + public Part testDeleteAfterFinished(@QueryParam("name") String name, @QueryParam("content") String content) + throws IOException { + File file = createTempFile(name, content); + + return new FilePart(null, file) + .setDeleteAfterFinished(true) + .setSubmittedFileName(name); + } + + @Path("/instanceIsolationTest") + @GET + public String instanceIsolationTest() { + throw new InvocationException(503, "", "business"); + } + + private File createTempFile(String name, String content) throws IOException { + File systemTempFile = new File(System.getProperty("java.io.tmpdir")); + File file = new File(systemTempFile, name); + FileUtils.write(file, content, StandardCharsets.UTF_8, false); + return file; + } +} 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 new file mode 100644 index 00000000000..e5735de44fe --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/ComputeImpl.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.demo.jaxrs.server; + +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.demo.compute.Person; +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 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) +public class ComputeImpl { + private static final Logger LOGGER = LoggerFactory.getLogger(ComputeImpl.class); + + @Path("/add") + @POST + public int add(@FormParam("a") int a, @FormParam("b") int b) { + return a + 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")); + return a - b; + } + + @Path("/sayhello") + @POST + public Person sayHello(Person user) { + user.setName("hello " + user.getName()); + return user; + } + + @SuppressWarnings("unchecked") + @Path("/testrawjson") + @POST + public String testRawJsonString(String jsonInput) { + Map person; + try { + person = RestObjectMapperFactory.getRestObjectMapper() + .readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + return "hello " + person.get("name"); + } + + @Path("/saysomething") + @POST + public String saySomething(@HeaderParam("prefix") String prefix, Person user) { + return prefix + " " + user.getName(); + } + + @Path("/sayhi/{name}") + @PUT + public void sayHi(@PathParam("name") String name) { + LOGGER.info(name + " sayhi"); + ContextUtils.getInvocationContext().setStatus(202); + } + + @Path("/sayhi/{name}/v2") + @PUT + public void sayHi2(@PathParam("name") String name) { + LOGGER.info(name + " sayhi 2"); + } + + @Path("/sayhei") + @DELETE + public void sayHei(@QueryParam("name") String name) { + LOGGER.info(name + " sayhei"); + } + + @Path("/istrue") + @GET + public boolean isTrue() { + return true; + } + + @Path("/addstring") + @DELETE + @Produces(MediaType.TEXT_PLAIN) + public String addString(@QueryParam("s") String[] s) { + StringBuilder result = new StringBuilder(); + for (String x : s) { + result.append(x); + } + return result.toString(); + } +} 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 new file mode 100644 index 00000000000..2670681217a --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FormRequestSchema.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.jaxrs.server; + +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) +public class FormRequestSchema { + + @Path("/formRequest") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public String formRequestSuccess(@FormParam("formData") String formData) throws Exception { + 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 new file mode 100644 index 00000000000..17769d3c291 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/JaxRSDefaultValues.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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; + +@RestSchema(schemaId = "JaxRSDefaultValues") +@Path("/JaxRSDefaultValues") +@Produces(MediaType.APPLICATION_JSON) +public class JaxRSDefaultValues { + + @Path("/form") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public String form(@DefaultValue("20") @FormParam("a") int a, @DefaultValue("bobo") @FormParam("b") String b) { + return "Hello " + a + b; + } + + @Path("/header") + @POST + public String header(@DefaultValue("20") @HeaderParam("a") int a, @DefaultValue("bobo") @HeaderParam("b") String b, + @DefaultValue("30") @HeaderParam("c") Integer c) { + return "Hello " + a + b + c; + } + + @Path("/query") + @GET + public String query(@DefaultValue("20") @QueryParam("a") int a, @DefaultValue("bobo") @QueryParam("b") String b, + @DefaultValue("40") @QueryParam("c") Integer c, @QueryParam("d") int d) { + return "Hello " + a + b + c + d; + } + + @Path("/query2") + @GET + public String query2(@QueryParam("e") int e, @DefaultValue("20") @QueryParam("a") int a, + @DefaultValue("bobo") @QueryParam("b") String b, + @DefaultValue("40") @QueryParam("c") Integer c, @Min(value = 20) @Max(value = 30) @QueryParam("d") int d) { + return "Hello " + a + b + c + d + e; + } + + @Path("/query3") + @GET + public String query3(@QueryParam("a") @Min(value = 20) int a, @QueryParam("b") String b) { + return "Hello " + a + b; + } + + @Path("/packages") + @GET + public String queryPackages(HttpServletRequest httpRequest, + @Max(value = 2147483647L) @Min(value = -1L) @NotNull @QueryParam("pageNo") Integer pageNo, + @Max(value = 2147483647L) @Min(value = -1L) @NotNull @QueryParam("pageSize") Integer pageSize, + @Size(max = 64, min = 0) @QueryParam("packageName") String packageName, + @Max(value = 127L) @Min(value = 0L) @QueryParam("packageType") Integer packageType, + @Max(value = 2147483647L) @Min(value = 1L) @QueryParam("roleID") Integer roleID, + @Max(value = 2147483647L) @Min(value = 1L) @QueryParam("categoryID") Integer categoryID, + @Max(value = 127L) @Min(value = 0L) @QueryParam("appType") @DefaultValue("1") Integer appType, + @Max(value = 2L) @Min(value = 1L) @QueryParam("packageScope") Integer packageScope) { + return "" + appType; + } + + @Path("/javaprimitiveint") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public String jaxRsJavaPrimitiveInt(@FormParam("a") int a, @DefaultValue("bobo") @FormParam("b") String b) { + return "Hello " + a + b; + } + + @Path("/javaprimitivenumber") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public String jaxRsJavaPrimitiveNumber(@QueryParam("a") float a, @QueryParam("b") boolean b) { + return "Hello " + a + b; + } + + @Path("/javaprimitivestr") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public String jaxRsJavaPrimitiveStr(@FormParam("b") int b, @FormParam("a") String a) { + if (a == null || a.equals("")) { + return "Hello"; + } + return "Hello " + b + a; + } + + @Path("/javaprimitivecomb") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public String jaxRsJavaPrimitiveCombnation(@QueryParam("a") Integer a, @QueryParam("b") Float b) { + return "Hello " + a + b; + } + + @Path("/allprimitivetypes") + @POST + public String allprimitivetypes(@QueryParam("pBoolean") boolean pBoolean, + @QueryParam("pChar") char pChar, + @QueryParam("pByte") byte pByte, + @QueryParam("pShort") short pShort, + @QueryParam("pInt") int pInt, + @QueryParam("pLong") long pLong, + @QueryParam("pFloat") float pFloat, + @QueryParam("pDouble") double pDouble, + @QueryParam("pDoubleWrap") Double pDoubleWrap) { + return "Hello " + pBoolean + "," + + pChar + "," + + pByte + "," + + pShort + "," + + pInt + "," + + pLong + "," + + pFloat + "," + + pDouble + "," + + pDoubleWrap; + } +} 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 new file mode 100644 index 00000000000..80985d68541 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/QueryParamSchema.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.jaxrs.server; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; + +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "QueryParamSchema") +@Path("/queryParam") +public class QueryParamSchema { + @Path("testQueryEncode") + @GET + public String testQueryEncode(@QueryParam("param") String param) { + return param; + } +} 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 new file mode 100644 index 00000000000..d15849423ac --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/QueryParamWithListSchema.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.demo.jaxrs.server; + +import java.util.List; + +import org.apache.servicecomb.provider.rest.common.RestSchema; + +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( + @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( + @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( + @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( + @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 new file mode 100644 index 00000000000..2143d27a04c --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/ReactiveSchema.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.demo.jaxrs.server; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +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") +@Produces(MediaType.APPLICATION_JSON) +public class ReactiveSchema { + // reactive is configured to run in event-loop thread pool + @Path("/testSyncInvokeInEventLoop") + @GET + public int testSyncInvokeInEventLoop(@QueryParam("a") int a, @QueryParam("b") int b) { + 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 new file mode 100644 index 00000000000..2f17c2fc6f8 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/RequestClientTimeOut.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.demo.jaxrs.server; + +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; + + +@RestSchema(schemaId = "clientreqtimeout") +@Path("/clientreqtimeout") +@Produces(MediaType.APPLICATION_JSON) +public class RequestClientTimeOut { + + @Path("/add") + @POST + public int add(@FormParam("a") int a, @FormParam("b") int b) throws InterruptedException { + Thread.sleep(2000); + return a + b; + } + + @Path("/sayhello") + @POST + public Student sayHello(Student student) throws InterruptedException { + student.setName("hello " + student.getName()); + student.setAge(student.getAge()); + return student; + } +} 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 new file mode 100644 index 00000000000..c9c9b311535 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/SchemeInterfaceJaxrs.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.demo.jaxrs.server; + +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; + +@Path("/jaxrs/schemaInterface") +@Produces(MediaType.APPLICATION_JSON) +public interface SchemeInterfaceJaxrs { + @Path("/add") + @GET + int add(@Min(-100) @RequestParam("a") int a, @Min(1) @RequestParam("b") int b); + + @Path("/interfaceModel") + @GET + Page interfaceModel(Page model); +} diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/SchemeInterfaceJaxrsImpl.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/SchemeInterfaceJaxrsImpl.java new file mode 100644 index 00000000000..a551644834e --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/SchemeInterfaceJaxrsImpl.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.jaxrs.server; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.data.domain.Page; + +@RestSchema(schemaId = "SchemeInterfaceJaxrs", schemaInterface = SchemeInterfaceJaxrs.class) +public class SchemeInterfaceJaxrsImpl implements SchemeInterfaceJaxrs { + @Override + public int add(int a, int b) { + return a + b; + } + + public int reduce(int a, int b) { + return a - b; + } + + public int add(String a, String b) { + return Integer.parseInt(a) + Integer.parseInt(b); + } + + @Override + public Page interfaceModel(Page model) { + return model; + } +} 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 new file mode 100644 index 00000000000..a5c5f9b8332 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/Validator.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.jaxrs.server; + +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; +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.hibernate.validator.constraints.Length; + +@RestSchema(schemaId = "validator") +@Path("/validator") +@Produces(MediaType.APPLICATION_JSON) +public class Validator { + + @Path("/add") + @POST + public int add(@FormParam("a") int a, @Min(20) @FormParam("b") int b) { + return a + b; + } + + @Path("/sayhi/{name}") + @PUT + public String sayHi(@Length(min = 3) @PathParam("name") String name) { + ContextUtils.getInvocationContext().setStatus(202); + return name + " sayhi"; + } + + @Path("/sayhello") + @POST + public Student sayHello(@Valid Student student) { + student.setName("hello " + student.getName()); + student.setAge(student.getAge()); + return student; + } +} 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 new file mode 100644 index 00000000000..8f037cff595 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/BeanParamTestService.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.demo.jaxrs.server.beanParam; + +import java.io.IOException; +import java.util.Scanner; + +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; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + +@RestSchema(schemaId = "beanParamTest") +@Path("/beanParamTest") +public class BeanParamTestService { + @Path("/{pathSwaggerStr}/simple") + @GET + public String beanParameterTest(InvocationContext invocationContext, @BeanParam TestBeanParameter testBeanParameter, + @QueryParam("extraQuery") String extraQuery) { + return String.format("invocationContextConsistency=%b|testBeanParameter=%s|extraQuery=%s", + ContextUtils.getInvocationContext() == invocationContext, + testBeanParameter.toString(), + extraQuery); + } + + @Path("/upload") + @Consumes(MediaType.MULTIPART_FORM_DATA) + @POST + public String beanParameterTestUpload(@FormParam("up0") Part up0, + @BeanParam TestBeanParameterWithUpload testBeanParameter, + @QueryParam("extraQuery") String extraQuery) throws IOException { + return String.format("testBeanParameter=%s|extraQuery=%s|up0=%s|up1=%s|up2=%s", + testBeanParameter.toString(), + extraQuery, + getUploadContent(up0), + getUploadContent(testBeanParameter.getUp1()), + getUploadContent(testBeanParameter.getUp2())); + } + + public String getUploadContent(Part upload) throws IOException { + StringBuilder result = new StringBuilder(); + try (Scanner scanner = new Scanner(upload.getInputStream())) { + while (scanner.hasNext()) { + result.append(scanner.next()); + } + } + return result.toString(); + } +} 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 new file mode 100644 index 00000000000..3ccff412a5e --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/TestBeanParameter.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.beanParam; + +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; + +public class TestBeanParameter { + @DefaultValue("defaultQueryValue") + @QueryParam("querySwaggerStr") + private String queryStr; + + private Integer headerInt; + + private String pathStr; + + @CookieParam("cookieSwaggerLong") + private long cookieLong; + + @JsonIgnore + private String ignoredField; + + public String getQueryStr() { + return queryStr; + } + + public void setQueryStr(String queryStr) { + this.queryStr = queryStr; + } + + public Integer getHeaderInt() { + return headerInt; + } + + @DefaultValue("12") + @HeaderParam("headerSwaggerInt") + public void setHeaderInt(Integer headerInt) { + this.headerInt = headerInt; + } + + public String getPathStr() { + return pathStr; + } + + @PathParam("pathSwaggerStr") + public void setPathStr(String pathStr) { + this.pathStr = pathStr; + } + + public long getCookieLong() { + return cookieLong; + } + + public void + setCookieLong(long cookieLong) { + this.cookieLong = cookieLong; + } + + public String getIgnoredField() { + return ignoredField; + } + + public void setIgnoredField(String ignoredField) { + this.ignoredField = ignoredField; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("TestBeanParameter{"); + sb.append("queryStr='").append(queryStr).append('\''); + sb.append(", headerInt=").append(headerInt); + sb.append(", pathStr='").append(pathStr).append('\''); + sb.append(", cookieLong=").append(cookieLong); + sb.append('}'); + return sb.toString(); + } +} 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 new file mode 100644 index 00000000000..b6e3685ef47 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/beanParam/TestBeanParameterWithUpload.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.demo.jaxrs.server.beanParam; + +import jakarta.servlet.http.Part; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.QueryParam; + +public class TestBeanParameterWithUpload { + @QueryParam("query") + private String queryStr; + + @FormParam("up1") + private Part up1; + + private Part up2; + + public String getQueryStr() { + return queryStr; + } + + public void setQueryStr(String queryStr) { + this.queryStr = queryStr; + } + + public Part getUp1() { + return up1; + } + + public void setUp1(Part up1) { + this.up1 = up1; + } + + public Part getUp2() { + return up2; + } + + @FormParam("up2") + public void setUp2(Part up2) { + this.up2 = up2; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("TestBeanParameterWithUpload{"); + sb.append("queryStr='").append(queryStr).append('\''); + sb.append('}'); + return sb.toString(); + } +} diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBean.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBean.java new file mode 100644 index 00000000000..60ef5241e9a --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBean.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.demo.jaxrs.server.injectBean; + +import org.springframework.stereotype.Component; + +@Component +public class InjectBean { + private boolean set = true; + + public boolean isSet() { + return set; + } +} 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 new file mode 100644 index 00000000000..59395b02687 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBeanSchema.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.jaxrs.server.injectBean; + +import java.util.List; + +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; +import org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher; + +@RestSchema(schemaId = "InjectBeanSchema") +@Path("/") +public class InjectBeanSchema { + @Path("/injectSet") + @GET + public boolean injectSet() { + List services = SPIServiceUtils.getOrLoadSortedService(VertxHttpDispatcher.class); + for (VertxHttpDispatcher service : services) { + if (service instanceof InjectBeanVertxHttpDispatcher) { + return ((InjectBeanVertxHttpDispatcher) service).isSet(); + } + } + return false; + } +} diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBeanVertxHttpDispatcher.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBeanVertxHttpDispatcher.java new file mode 100644 index 00000000000..347ba9473ab --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/injectBean/InjectBeanVertxHttpDispatcher.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.demo.jaxrs.server.injectBean; + +import org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher; +import org.springframework.beans.factory.annotation.Autowired; + +import io.vertx.ext.web.Router; + +public class InjectBeanVertxHttpDispatcher implements VertxHttpDispatcher { + private InjectBean injectBean; + + @Autowired + public void setInjectBean(InjectBean injectBean) { + this.injectBean = injectBean; + } + + @Override + public void init(Router router) { + + } + + public boolean isSet() { + return injectBean.isSet(); + } +} 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 new file mode 100644 index 00000000000..9a5c6be64b0 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/MultiErrorCodeService.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.demo.jaxrs.server.multiErrorCode; + +import java.util.ArrayList; +import java.util.List; + +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.invocation.Response; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +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") +public class MultiErrorCodeService { + @Path("/errorCode") + @POST + @ApiResponses({ + @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(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(jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR, r); + } else { + MultiResponse200 r = new MultiResponse200(); + r.setCode(200); + r.setMessage("success result"); + return r; + } + } + + @Path("/errorCodeWithHeader") + @POST + @ApiResponses({ + @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) { + MultiResponse400 r = new MultiResponse400(); + r.setCode(400); + r.setMessage("bad request"); + response.setStatus(Status.BAD_REQUEST); + // 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.setResult(new InvocationException(Status.BAD_REQUEST, r)); + response.setHeader("x-code", "400"); + } else if (request.getCode() == 500) { + MultiResponse500 r = new MultiResponse500(); + r.setCode(500); + r.setMessage("internal error"); + response.setStatus(Status.INTERNAL_SERVER_ERROR); + response.setResult(new InvocationException(Status.INTERNAL_SERVER_ERROR, r)); + response.setHeader("x-code", "500"); + } else { + MultiResponse200 r = new MultiResponse200(); + r.setCode(200); + r.setMessage("success result"); + response.setStatus(Status.OK); + // If error code is OK family(like 200), we can use the target type. + response.setResult(r); + response.setHeader("x-code", "200"); + } + return response; + } + + // using JAX-RS providers, users need to add dependencies for implementations, see pom for an example. + @Path("/errorCodeWithHeaderJAXRS") + @POST + @ApiResponses({ + @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 = jakarta.ws.rs.core.Response.status(Status.BAD_REQUEST) + .entity(new InvocationException(Status.BAD_REQUEST, r)) + .header("x-code", "400") + .build(); + } else if (request.getCode() == 500) { + MultiResponse500 r = new MultiResponse500(); + r.setCode(request.getCode()); + r.setMessage(request.getMessage()); + response = jakarta.ws.rs.core.Response.status(Status.INTERNAL_SERVER_ERROR) + .entity(new InvocationException(Status.INTERNAL_SERVER_ERROR, r)) + .header("x-code", "500") + .build(); + } else { + MultiResponse200 r = new MultiResponse200(); + r.setCode(request.getCode()); + r.setMessage(request.getMessage()); + // If error code is OK family(like 200), we can use the target type. + response = jakarta.ws.rs.core.Response.status(Status.OK) + .entity(r) + .header("x-code", "200") + .build(); + } + return response; + } + + @Path("/noClientErrorCode") + @POST + @ApiResponses({ + @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(implementation = NoClientErrorCode400.class)), description = "")}) + public List noClientErrorCode(MultiRequest request) { + if (request.getCode() == 400) { + NoClientErrorCode400 r = new NoClientErrorCode400(); + r.setCode(request.getCode()); + r.setMessage(request.getMessage()); + r.setT400(400); + throw new InvocationException(Status.BAD_REQUEST, r); + } else { + NoClientErrorCode200 r = new NoClientErrorCode200(); + r.setCode(request.getCode()); + r.setMessage(request.getMessage()); + r.setT200(200); + List result = new ArrayList<>(); + result.add(r); + return result; + } + } +} diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/NoClientErrorCode200.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/NoClientErrorCode200.java new file mode 100644 index 00000000000..df11cf750fa --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/NoClientErrorCode200.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.jaxrs.server.multiErrorCode; + +public class NoClientErrorCode200 { + private String message; + + private int code; + + private long t200; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public long getT200() { + return t200; + } + + public void setT200(long t200) { + this.t200 = t200; + } +} diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/NoClientErrorCode400.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/NoClientErrorCode400.java new file mode 100644 index 00000000000..cb482288e9d --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/multiErrorCode/NoClientErrorCode400.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.jaxrs.server.multiErrorCode; + +public class NoClientErrorCode400 { + private String message; + + private int code; + + private long t400; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public long getT400() { + return t400; + } + + public void setT400(long t400) { + this.t400 = t400; + } +} 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 new file mode 100644 index 00000000000..36020868cbd --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultModelService.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.demo.jaxrs.server.pojoDefault; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "DefaultModelService") +@Path("DefaultModelService") +public class DefaultModelService { + @Path("/model") + @POST + public DefaultResponseModel errorCode(DefaultRequestModel request) { + DefaultResponseModel model = new DefaultResponseModel(); + model.setIndex(request.getIndex()); + model.setAge(request.getAge()); + model.setName(request.getName()); + model.setDesc(null); + return model; + } +} 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 new file mode 100644 index 00000000000..b8053ca8086 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultRequestModel.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pojoDefault; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Null; + +public class DefaultRequestModel { + @Min(20) + @Max(2000) + @Null + private Integer age = 200; + + @Min(2) + @Max(30) + @Null + private String name = "World"; + + private int index; + + private String desc; + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + @Override + public String toString() { + return "index=" + index + ";name=" + name + ";age=" + age; + } +} 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 new file mode 100644 index 00000000000..9c505699866 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/pojoDefault/DefaultResponseModel.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.pojoDefault; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Null; + +public class DefaultResponseModel { + @Min(20) + @Max(2000) + @Null + private Integer age = 200; + + @Min(2) + @Max(30) + @Null + private String name = "World"; + + private int index; + + private String desc; + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + @Override + public String toString() { + return "index=" + index + ";name=" + name + ";age=" + age; + } +} 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 new file mode 100644 index 00000000000..7b18a4f7322 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/validation/ValidationService.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.jaxrs.server.validation; + +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; + +@RestSchema(schemaId = "ValidationService") +@Path("ValidationService") +public class ValidationService { + @Path("/validate") + @POST + public ValidationModel errorCode(@NotNull @Valid ValidationModel request) { + return request; + } + + @Path("/validateQuery") + @GET + public String queryValidate(@NotEmpty @QueryParam("name") String name) { + return name; + } + + @Path("/sayTeacherInfo") + @POST + public Teacher sayTeacherInfo(@Valid Teacher teacher) { + return teacher; + } +} 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 new file mode 100644 index 00000000000..a3a19c72aa3 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher @@ -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.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 new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-jaxrs/jaxrs-server/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + 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 2db5b32e45f..475708a55b1 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/resources/microservice.yaml +++ b/demo/demo-jaxrs/jaxrs-server/src/main/resources/microservice.yaml @@ -1,16 +1,55 @@ -APPLICATION_ID: jaxrstest -service_description: - name: jaxrs - version: 0.0.2 -cse: +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: - registry: - address: http://127.0.0.1:9980 + application: jaxrstest + name: jaxrs + version: 0.0.2 + registry: + sc: + address: http://127.0.0.1:30100 + enableSwaggerRegistration: true rest: address: 0.0.0.0:8080 + server: + maxFormAttributeSize: 1024 # for testing, and bigger than netty buffer allocator + maxFormBufferedBytes: 100 highway: address: 0.0.0.0:7070 - handler: - chain: - Provider: - default: bizkeeper-provider + uploads: + directory: target + + codec: + printErrorMessage: true + + executors.Provider.ReactiveSchema: servicecomb.executor.reactive + + invocation: + 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 3479461367e..014baac0318 100644 --- a/demo/demo-jaxrs/pom.xml +++ b/demo/demo-jaxrs/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.3.0-SNAPSHOT - - demo-jaxrs - pom - - jaxrs-server - jaxrs-client + 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.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-jaxrs + Java Chassis::Demo::JAXRS + pom + + jaxrs-server + jaxrs-client - + + + org.apache.servicecomb + solution-basic + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + swagger-generator-spring-data + + + org.springframework.data + spring-data-commons + + + 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 new file mode 100644 index 00000000000..a585a17b7ce --- /dev/null +++ b/demo/demo-local-registry/README.md @@ -0,0 +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. diff --git a/demo/demo-local-registry/demo-local-registry-client/pom.xml b/demo/demo-local-registry/demo-local-registry-client/pom.xml new file mode 100644 index 00000000000..352e0cf26aa --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-client/pom.xml @@ -0,0 +1,106 @@ + + + + + 4.0.0 + demo-local-registry-client + Java Chassis::Demo::Local Registry client + + org.apache.servicecomb.demo + demo-local-registry + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.localRegistryClient.LocalRegistryClientApplication + + + + org.apache.servicecomb.demo + demo-schema + + + + + + docker + + 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 + + + + + + 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 new file mode 100644 index 00000000000..05973bd18de --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/CodeFirstService.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.localRegistryClient; + +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") +public interface CodeFirstService { + @GET + @Path("getName") + String getName(@QueryParam("name") String name); +} 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 new file mode 100644 index 00000000000..06a24601e03 --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryServerTest.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.demo.localRegistryClient; + +import java.util.List; + +import org.apache.servicecomb.demo.CategorizedTestCase; +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.registry.DiscoveryManager; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +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(); + testCodeFirstGetName(); + testGetAllMicroservice(); + } + + private void testGetAllMicroservice() { + 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() { + TestMgr.check("2", codeFirstService.getName("2")); + TestMgr.check("2", codeFirstServiceBean.getName("2")); + TestMgr.check("2", codeFirstServiceBean2.getName("2")); + } + + private void testServerGetName() { + 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 new file mode 100644 index 00000000000..28c5d3a1f25 --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/java/org/apache/servicecomb/demo/localRegistryClient/RegistryBeansConfiguration.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.demo.localRegistryClient; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.localregistry.RegistryBean; +import org.apache.servicecomb.localregistry.RegistryBean.Instance; +import org.apache.servicecomb.localregistry.RegistryBean.Instances; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@SuppressWarnings("unused") +public class RegistryBeansConfiguration { + @Bean + public RegistryBean demoLocalRegistryServerBean() { + List endpoints = new ArrayList<>(); + endpoints.add("rest://localhost:8080"); + List instances = new ArrayList<>(); + instances.add(new Instance().setEndpoints(endpoints)); + + return new RegistryBean() + .setServiceName("demo-local-registry-server-bean") + .setId("002") + .setVersion("0.0.3") + .setAppId("demo-local-registry") + .addSchemaId("ServerEndpoint") + .addSchemaInterface("CodeFirstEndpoint", CodeFirstService.class) + .setInstances(new Instances().setInstances(instances)); + } + + @Bean + public RegistryBean demoLocalRegistryServerBean2() { + List endpoints = new ArrayList<>(); + endpoints.add("rest://localhost:8080"); + List instances = new ArrayList<>(); + instances.add(new Instance().setEndpoints(endpoints)); + + return new RegistryBean() + .setServiceName("demo-local-registry-server-bean2") + .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 new file mode 100644 index 00000000000..fcf4bb18309 --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-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. +## --------------------------------------------------------------------------- + +server: + port: 8082 + +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/log4j2.xml b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 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-bean/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-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 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/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/registry.yaml b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/registry.yaml new file mode 100644 index 00000000000..cb5a3bcd4d9 --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/registry.yaml @@ -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. +## --------------------------------------------------------------------------- + +demo-local-registry-server: + - id: "001" + version: "1.0" + appid: demo-local-registry + schemaIds: + - ServerEndpoint + - CodeFirstEndpoint + instances: + - endpoints: + - 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 new file mode 100644 index 00000000000..f8f12fd9cfa --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-client/src/test/java/org/apache/servicecomb/demo/localRegistryClient/LocalRegistryIT.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.localRegistryClient; + +import org.apache.servicecomb.demo.TestMgr; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class LocalRegistryIT { + @BeforeEach + public void setUp() throws Exception { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + 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 new file mode 100644 index 00000000000..8832ac2dc8a --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-server/pom.xml @@ -0,0 +1,106 @@ + + + + + 4.0.0 + demo-local-registry-server + Java Chassis::Demo::Local Registry Server + + org.apache.servicecomb.demo + demo-local-registry + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.localRegistryServer.LocalRegistryServerApplication + + + + jakarta.ws.rs + jakarta.ws.rs-api + + + 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-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 new file mode 100644 index 00000000000..06fdf6b7939 --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/CodeFirstEndpoint.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.localRegistryServer; + +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 = "CodeFirstEndpoint") +@RequestMapping(path = "/register/url/codeFirst", produces = MediaType.APPLICATION_JSON) +public class CodeFirstEndpoint { + @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-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/SelfServiceInvoker.java b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/SelfServiceInvoker.java new file mode 100644 index 00000000000..0790cfeec7c --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/SelfServiceInvoker.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.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-local-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-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 new file mode 100644 index 00000000000..2db9d87dd05 --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/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.localRegistryServer; + +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-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 new file mode 100644 index 00000000000..1478383598e --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-server/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- + +server: + port: 8080 + +# java-chassis configurations +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/demo-local-registry-server/src/main/resources/log4j2.xml b/demo/demo-local-registry/demo-local-registry-server/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-local-registry/demo-local-registry-server/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/demo/demo-local-registry/pom.xml b/demo/demo-local-registry/pom.xml new file mode 100644 index 00000000000..c33eebf22e7 --- /dev/null +++ b/demo/demo-local-registry/pom.xml @@ -0,0 +1,66 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-local-registry + Java Chassis::Demo::Local Registry + pom + + demo-local-registry-server + demo-local-registry-client + + + + + org.apache.servicecomb + solution-basic + + + org.apache.servicecomb + java-chassis-spring-boot-starter-servlet + + + org.apache.servicecomb + registry-local + + + 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-local/pom.xml b/demo/demo-local/pom.xml deleted file mode 100644 index db7ce9f294e..00000000000 --- a/demo/demo-local/pom.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.3.0-SNAPSHOT - - demo-local - - - io.servicecomb.demo - pojo-server - - - io.servicecomb.demo - pojo-client - - - - - io.servicecomb.demo.local.DemoLocal - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - - diff --git a/demo/demo-local/src/main/java/io/servicecomb/demo/local/DemoLocal.java b/demo/demo-local/src/main/java/io/servicecomb/demo/local/DemoLocal.java deleted file mode 100644 index e87586e9a49..00000000000 --- a/demo/demo-local/src/main/java/io/servicecomb/demo/local/DemoLocal.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.local; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class DemoLocal { - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - - // TODO:当前未实现本地调用 - // DemoClient.runTest(); - } -} 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 new file mode 100644 index 00000000000..3577950317f --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-client/pom.xml @@ -0,0 +1,134 @@ + + + + + 4.0.0 + demo-multi-registries-client + Java Chassis::Demo::Multi Registries client + + org.apache.servicecomb.demo + demo-multi-registries + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.registry.MultiRegistriesClientApplication + + + + org.apache.servicecomb.demo + demo-schema + + + + + + docker + + 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 + + + + + + diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/IServerEndpoint.java b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/IServerEndpoint.java new file mode 100644 index 00000000000..b4ac30465d0 --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/IServerEndpoint.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.registry; + +public interface IServerEndpoint { + String getName(String name); +} diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/IServiceCenterEndpoint.java b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/IServiceCenterEndpoint.java new file mode 100644 index 00000000000..42878945bc2 --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/IServiceCenterEndpoint.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.demo.registry; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; + +public interface IServiceCenterEndpoint { + // java name can not be `x-domain-name`, so interfaces define all parameters. + @GetMapping(path = "/instances") + Object getInstances(@RequestParam(name = "appId") String appId, + @RequestParam(name = "serviceName") String serviceName, + @RequestParam(name = "global") String global, + @RequestParam(name = "version") String version, + @RequestHeader(name = "x-domain-name") String domain); +} 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 new file mode 100644 index 00000000000..abbd66a3d1b --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/MultiRegistriesServerTestCase.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.demo.registry; + +import java.util.List; + +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.DiscoveryInstance; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; + +@Component +public class MultiRegistriesServerTestCase implements CategorizedTestCase { + RestOperations template = RestTemplateBuilder.create(); + + private DiscoveryManager discoveryManager; + + @Autowired + public void setDiscoveryManager(DiscoveryManager discoveryManager) { + this.discoveryManager = discoveryManager; + } + + @Override + public void testRestTransport() throws Exception { + testServerGetName(); + testGetAllMicroservice(); + } + + private void testGetAllMicroservice() { + 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() { + // invoke demo-multi-registries-server + TestMgr.check("2", template + .getForObject("cse://demo-multi-registries-server/register/url/prefix/getName?name=2", + String.class)); + } + + @Override + public void testHighwayTransport() throws Exception { + + } + + @Override + public void testAllTransport() throws Exception { + + } +} 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 new file mode 100644 index 00000000000..0bf58aa7c3f --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/SchemaDiscoveryTestCase.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.demo.registry; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.springframework.stereotype.Component; + +@Component +public class SchemaDiscoveryTestCase implements CategorizedTestCase { + // 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")); + } + + @Override + public void testHighwayTransport() throws Exception { + + } + + @Override + public void testAllTransport() throws Exception { + + } + + @Override + public String getMicroserviceName() { + return "thirdParty-service-center"; + } +} 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 new file mode 100644 index 00000000000..491732f92bc --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/ServiceCenterEndpoint.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.registry; + +import jakarta.ws.rs.core.MediaType; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +// do not know how to write schemas? code first and generate it. +@RestSchema(schemaId = "ServiceCenterEndpoint") +@RequestMapping(path = "/v4/default/registry", produces = MediaType.APPLICATION_JSON) +public class ServiceCenterEndpoint implements IServiceCenterEndpoint { + @GetMapping(path = "/instances") + public Object getInstances(@RequestParam(name = "appId") String appId, + @RequestParam(name = "serviceName") String serviceName, + @RequestParam(name = "global") String global, + @RequestParam(name = "version") String version, + @RequestHeader(name = "x-domain-name") String domain) { + return null; + } +} diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/ServiceCenterTestCase.java b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/ServiceCenterTestCase.java new file mode 100644 index 00000000000..c84bdc2fe0e --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/java/org/apache/servicecomb/demo/registry/ServiceCenterTestCase.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.registry; + +import java.util.List; +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.springframework.stereotype.Component; + +@Component +public class ServiceCenterTestCase implements CategorizedTestCase { + @RpcReference(microserviceName = "thirdParty-service-center", schemaId = "ServiceCenterEndpoint") + IServiceCenterEndpoint serviceCenterEndpoint; + + @Override + public void testRestTransport() throws Exception { + // invoke service-center(3rd-parties) + @SuppressWarnings("unchecked") + Map> result = (Map>) serviceCenterEndpoint.getInstances( + "demo-multi-registries", + "demo-multi-registries-server", + "true", + "0.0.2", + "default"); + TestMgr.check(result.get("instances").size(), 1); + } + + @Override + public void testHighwayTransport() throws Exception { + + } + + @Override + public void testAllTransport() throws Exception { + + } + + @Override + public String getMicroserviceName() { + return "thirdParty-service-center"; + } +} 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 new file mode 100644 index 00000000000..e440ff6c897 --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-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. +## --------------------------------------------------------------------------- + +server: + port: 8082 + +servicecomb: + service: + 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/log4j2.xml b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 new file mode 100644 index 00000000000..88332e3259a --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/microservices/thirdParty-service-center/ServiceCenterEndpoint.yaml @@ -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. +## --------------------------------------------------------------------------- + +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.demo.localRegistryServer.ServerEndpoint + version: 1.0.0 +servers: + - url: /v4/default/registry +paths: + /instances: + get: + operationId: getInstances + parameters: + - name: appId + in: query + required: true + schema: + type: string + - name: serviceName + in: query + required: true + schema: + type: string + - name: global + in: query + required: true + schema: + type: string + - name: version + in: query + required: true + schema: + type: string + - name: x-domain-name + in: header + required: true + schema: + 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 new file mode 100644 index 00000000000..9d39b1b8bd1 --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/registry.yaml @@ -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. +## --------------------------------------------------------------------------- + +thirdParty-service-center: + - id: "001" + version: "4.0.0" + appid: demo-multi-registries + schemaIds: + - ServiceCenterEndpoint + instances: + - endpoints: + - rest://localhost:30100 +thirdParty-no-schema-server: + - id: "002" + version: "4.0.0" + appid: demo-multi-registries + schemaIds: + - 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 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 new file mode 100644 index 00000000000..1c716783c2d --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/test/java/org/apache/servicecomb/demo/registry/MultiRegistriesIT.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.registry; + +import org.apache.servicecomb.demo.TestMgr; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class MultiRegistriesIT { + @BeforeEach + public void setUp() throws Exception { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + 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 new file mode 100644 index 00000000000..4a0b37f4433 --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-server/pom.xml @@ -0,0 +1,106 @@ + + + + + 4.0.0 + demo-multi-registries-server + Java Chassis::Demo::Multi Registries Server + + org.apache.servicecomb.demo + demo-multi-registries + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.registry.MultiRegistriesServerApplication + + + + jakarta.ws.rs + jakarta.ws.rs-api + + + 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-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/SelfServiceInvoker.java b/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/SelfServiceInvoker.java new file mode 100644 index 00000000000..b15bb7c0df9 --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/SelfServiceInvoker.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.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-multi-registries-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-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 new file mode 100644 index 00000000000..eb594aaba57 --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/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.registry; + +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-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 new file mode 100644 index 00000000000..1104cebdbd7 --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-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-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/demo-multi-registries-server/src/main/resources/log4j2.xml b/demo/demo-multi-registries/demo-multi-registries-server/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-multi-registries/demo-multi-registries-server/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/demo/demo-multi-registries/pom.xml b/demo/demo-multi-registries/pom.xml new file mode 100644 index 00000000000..13f34c9c7af --- /dev/null +++ b/demo/demo-multi-registries/pom.xml @@ -0,0 +1,70 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-multi-registries + Java Chassis::Demo::Multi Registry + pom + + demo-multi-registries-server + demo-multi-registries-client + + + + + org.apache.servicecomb + solution-basic + + + org.apache.servicecomb + java-chassis-spring-boot-starter-servlet + + + org.apache.servicecomb + registry-local + + + org.apache.servicecomb + registry-service-center + + + 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-multi-service-center/demo-multi-service-center-client/pom.xml b/demo/demo-multi-service-center/demo-multi-service-center-client/pom.xml new file mode 100644 index 00000000000..2669f28a874 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/pom.xml @@ -0,0 +1,184 @@ + + + + + 4.0.0 + demo-multi-service-center-client + Java Chassis::Demo::Multi Service Centers::Client + + org.apache.servicecomb.demo + demo-multi-service-center + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.multiServiceCenterClient.Application + + + + + org.apache.servicecomb + service-center-client + + + org.apache.servicecomb.demo + demo-schema + + + + + + docker + + demo-multi-registries-server + + + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service30100 + + alias + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + servicecomb/service-center + service40100 + + alias + + server is ready + + + 30100 + + + + + + 40100:30100 + + + + + demo-multi-service-center-servera:${project.version} + demo-multi-service-center-servera + + alias + + + -Dservicecomb.registry.sc.address=http://service30100:30100 + + /maven/maven/demo-multi-service-center-servera-${project.version}.jar + + + service30100:service30100 + + + ServiceComb is ready + + + 8080 + + + + + + 8080:8080 + + + + + demo-multi-service-center-serverb:${project.version} + demo-multi-service-center-serverb + + alias + + + -Dservicecomb.registry.sc.address=http://service40100:30100 + + /maven/maven/demo-multi-service-center-serverb-${project.version}.jar + + + service40100:service40100 + + + ServiceComb is ready + + + 8082 + + + + + + 8082:8082 + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + 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 new file mode 100644 index 00000000000..4264651164e --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/Application.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.demo.multiServiceCenterClient; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +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 Application { + + public static void main(final String[] args) throws Exception { + new SpringApplicationBuilder().sources(Application.class) + .web(WebApplicationType.NONE).build().run(args); + + runTest(); + } + + public static void runTest() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("demo-multi-service-center-serverA"); + testTransportThreads(); + TestMgr.summary(); + if (!TestMgr.errors().isEmpty()) { + throw new IllegalStateException("tests failed"); + } + } + + private static void testTransportThreads() throws Exception { + Set threadSet = Thread.getAllStackTraces().keySet(); + List expectedThread = new ArrayList<>(); + threadSet.forEach(thread -> { + if (thread.getName().startsWith("transport-vert.x-eventloop-thread")) { + expectedThread.add(thread.getName()); + } + }); + // After SSL improvements https://github.com/eclipse-vertx/vert.x/pull/4468 introduced in 4.3.4 + // SSL helper will use `internal-blocking` thread init ssl engine + // transport-vert.x-eventloop-thread-0 + // transport-vert.x-eventloop-thread-1 + // transport-vert.x-eventloop-thread-2 + // transport-vert.x-eventloop-thread-3 + // transport-vert.x-eventloop-thread-4 + // transport-vert.x-eventloop-thread-5 + // transport-vert.x-internal-blocking-0 + // transport-vert.x-internal-blocking-1 + TestMgr.check(6, expectedThread.size()); + } +} diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/IConfigurationEndpoint.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/IConfigurationEndpoint.java new file mode 100644 index 00000000000..b237db532ba --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/IConfigurationEndpoint.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.demo.multiServiceCenterClient; + +import java.util.List; + +public interface IConfigurationEndpoint { + String getValue(String key, int type); + + List getValueList(String key, int type); +} diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/IServerEndpoint.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/IServerEndpoint.java new file mode 100644 index 00000000000..9d10324777a --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/IServerEndpoint.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.multiServiceCenterClient; + +public interface IServerEndpoint { + String getName(String name); +} 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 new file mode 100644 index 00000000000..3b11e0582d8 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ArrayList; +import java.util.Arrays; +import java.util.List; +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.foundation.common.event.SimpleEventBus; +import org.apache.servicecomb.http.client.auth.DefaultRequestAuthHeaderProvider; +import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; +import org.apache.servicecomb.service.center.client.ServiceCenterAddressManager; +import org.apache.servicecomb.service.center.client.RegistrationEvents; +import org.apache.servicecomb.service.center.client.RegistrationEvents.HeartBeatEvent; +import org.apache.servicecomb.service.center.client.RegistrationEvents.MicroserviceInstanceRegistrationEvent; +import org.apache.servicecomb.service.center.client.RegistrationEvents.MicroserviceRegistrationEvent; +import org.apache.servicecomb.service.center.client.RegistrationEvents.SchemaRegistrationEvent; +import org.apache.servicecomb.service.center.client.ServiceCenterClient; +import org.apache.servicecomb.service.center.client.ServiceCenterDiscovery; +import org.apache.servicecomb.service.center.client.ServiceCenterDiscovery.SubscriptionKey; +import org.apache.servicecomb.service.center.client.ServiceCenterRegistration; +import org.apache.servicecomb.service.center.client.model.Microservice; +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; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import com.google.common.hash.Hashing; + +@Component +public class RegistryClientTest implements CategorizedTestCase { + private List events = new ArrayList<>(); + + private CountDownLatch registrationCounter = new CountDownLatch(1); + + // 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(), "", ""); + SSLProperties sslProperties = new SSLProperties(); + sslProperties.setEnabled(false); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(addressManager, sslProperties, + new DefaultRequestAuthHeaderProvider(), "default", null, environment); + EventBus eventBus = new SimpleEventBus(); + ServiceCenterConfiguration serviceCenterConfiguration = new ServiceCenterConfiguration(); + ServiceCenterRegistration serviceCenterRegistration = new ServiceCenterRegistration(serviceCenterClient, + serviceCenterConfiguration, eventBus); + Microservice microservice = new Microservice(); + microservice.setAppId("app_registry"); + microservice.setServiceName("name_registry"); + microservice.setVersion("1.0.0"); + microservice.setEnvironment("development"); + List schemas = new ArrayList<>(); + schemas.add("SchemaA"); + schemas.add("SchemaB"); + microservice.setSchemas(schemas); + MicroserviceInstance microserviceInstance = new MicroserviceInstance(); + microserviceInstance.setHostName("host_registry"); + List endpoints = new ArrayList<>(); + endpoints.add("rest://127.0.0.1/"); + microserviceInstance.setEndpoints(endpoints); + List schemaInfos = new ArrayList<>(); + SchemaInfo schemaA = new SchemaInfo(); + schemaA.setSchemaId("SchemaA"); + schemaA.setSchema("schema contents in any format"); + schemaA.setSummary( + Hashing.sha256().newHasher().putString("schema contents in any format".toString(), Charsets.UTF_8).hash() + .toString()); + schemaInfos.add(schemaA); + SchemaInfo schemaB = new SchemaInfo(); + schemaB.setSchemaId("SchemaA"); + schemaB.setSchema("schema contents in any format"); + schemaB.setSummary( + Hashing.sha256().newHasher().putString("schema contents in any format".toString(), Charsets.UTF_8).hash() + .toString()); + schemaInfos.add(schemaB); + serviceCenterRegistration.setMicroservice(microservice); + serviceCenterRegistration.setMicroserviceInstance(microserviceInstance); + serviceCenterRegistration.setSchemaInfos(schemaInfos); + eventBus.register(this); + + serviceCenterRegistration.startRegistration(); + registrationCounter.await(30000, TimeUnit.MILLISECONDS); + if (hasRegistered) { + TestMgr.check(events.size() >= 3, true); + TestMgr.check(events.get(0).isSuccess(), true); + TestMgr.check(events.get(0) instanceof MicroserviceRegistrationEvent, true); + TestMgr.check(events.get(1).isSuccess(), true); + TestMgr.check(events.get(1) instanceof MicroserviceInstanceRegistrationEvent, true); + TestMgr.check(events.get(2).isSuccess(), true); + TestMgr.check(events.get(2) instanceof HeartBeatEvent, true); + } else { + TestMgr.check(events.size() >= 4, true); + TestMgr.check(events.get(0).isSuccess(), true); + TestMgr.check(events.get(0) instanceof MicroserviceRegistrationEvent, true); + TestMgr.check(events.get(1).isSuccess(), true); + TestMgr.check(events.get(1) instanceof SchemaRegistrationEvent, true); + TestMgr.check(events.get(2).isSuccess(), true); + TestMgr.check(events.get(2) instanceof MicroserviceInstanceRegistrationEvent, true); + TestMgr.check(events.get(3).isSuccess(), true); + TestMgr.check(events.get(3) instanceof HeartBeatEvent, true); + } + + ServiceCenterDiscovery discovery = new ServiceCenterDiscovery(serviceCenterClient, eventBus); + discovery.updateMyselfServiceId(microservice.getServiceId()); + discovery.startDiscovery(); + SubscriptionKey subscriptionKey = new SubscriptionKey(microservice.getAppId(), microservice.getServiceName()); + discovery.registerIfNotPresent(subscriptionKey); + List instances = discovery.getInstanceCache(subscriptionKey); + TestMgr.check(instances != null, true); + TestMgr.check(instances.size(), 1); + discovery.stop(); + serviceCenterRegistration.stop(); + serviceCenterClient.deleteMicroserviceInstance(microservice.getServiceId(), microserviceInstance.getInstanceId()); + } + + @Subscribe + public void onMicroserviceRegistrationEvent(MicroserviceRegistrationEvent event) { + events.add(event); + } + + @Subscribe + public void onSchemaRegistrationEvent(SchemaRegistrationEvent event) { + events.add(event); + hasRegistered = false; + } + + @Subscribe + public void onMicroserviceInstanceRegistrationEvent(MicroserviceInstanceRegistrationEvent event) { + events.add(event); + } + + @Subscribe + public void onHeartBeatEvent(HeartBeatEvent event) { + events.add(event); + registrationCounter.countDown(); + } +} 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/ServerATest.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerATest.java new file mode 100644 index 00000000000..9ddd4d88f4b --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerATest.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.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component +public class ServerATest implements CategorizedTestCase { + @RpcReference(microserviceName = "demo-multi-service-center-serverA", schemaId = "ServerEndpoint") + private IServerEndpoint serverEndpoint; + + @Override + public void testRestTransport() throws Exception { + TestMgr.check("hello", serverEndpoint.getName("hello")); + } +} diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerBTest.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerBTest.java new file mode 100644 index 00000000000..f76836d4ef8 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/ServerBTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.multiServiceCenterClient; + +import java.util.Arrays; + +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 ServerBTest implements CategorizedTestCase { + @RpcReference(microserviceName = "demo-multi-service-center-serverB", schemaId = "ServerEndpoint") + private IServerEndpoint serverEndpoint; + + @RpcReference(microserviceName = "demo-multi-service-center-serverB", schemaId = "ConfigurationEndpoint") + private IConfigurationEndpoint configurationEndpoint; + + @Override + public void testRestTransport() throws Exception { + TestMgr.check("hello", serverEndpoint.getName("hello")); + + TestMgr.check("key1-boot", configurationEndpoint.getValue("demo.multi.service.center.serverB.key1", 1)); + TestMgr.check("key1-boot", configurationEndpoint.getValue("demo.multi.service.center.serverB.key1", 2)); + TestMgr.check("key1-boot", configurationEndpoint.getValue("demo.multi.service.center.serverB.key1", 3)); + TestMgr.check("key2-override", configurationEndpoint.getValue("demo.multi.service.center.serverB.key2", 1)); + TestMgr.check("key2-override", configurationEndpoint.getValue("demo.multi.service.center.serverB.key2", 2)); + TestMgr.check("key2-override", configurationEndpoint.getValue("demo.multi.service.center.serverB.key2", 3)); + TestMgr.check("key3", configurationEndpoint.getValue("demo.multi.service.center.serverB.key3", 1)); + TestMgr.check("key3", configurationEndpoint.getValue("demo.multi.service.center.serverB.key3", 2)); + TestMgr.check("key3", configurationEndpoint.getValue("demo.multi.service.center.serverB.key3", 3)); + TestMgr.check("key4-boot", configurationEndpoint.getValue("demo.multi.service.center.serverB.key4", 1)); + TestMgr.check("key4-boot", configurationEndpoint.getValue("demo.multi.service.center.serverB.key4", 2)); + TestMgr.check("key4-boot", configurationEndpoint.getValue("demo.multi.service.center.serverB.key4", 3)); + TestMgr.check("key5-high", configurationEndpoint.getValue("demo.multi.service.center.serverB.key5", 1)); + TestMgr.check("key5-high", configurationEndpoint.getValue("demo.multi.service.center.serverB.key5", 2)); + TestMgr.check("key5-high", configurationEndpoint.getValue("demo.multi.service.center.serverB.key5", 3)); + TestMgr.check("key6", configurationEndpoint.getValue("demo.multi.service.center.serverB.key6", 1)); + TestMgr.check("key6", configurationEndpoint.getValue("demo.multi.service.center.serverB.key6", 2)); + TestMgr.check("key6", configurationEndpoint.getValue("demo.multi.service.center.serverB.key6", 3)); + TestMgr.check(Arrays.asList("key71", "key72"), + configurationEndpoint.getValueList("demo.multi.service.center.serverB.key7", 1)); + TestMgr.check(Arrays.asList("key71", "key72"), + configurationEndpoint.getValueList("demo.multi.service.center.serverB.key7", 2)); + } + + @Override + public String getMicroserviceName() { + return "demo-multi-service-center-serverB"; + } +} 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 new file mode 100644 index 00000000000..6daa76e8dee --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: demo-multi-service-center + name: demo-multi-service-center-client + version: 0.0.2 + registry: + sc: + address: http://127.0.0.1:30100 + sc2: + address: http://127.0.0.1:40100 + + transport: + eventloop: + size: 6 diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/log4j2.xml b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/test/java/org/apache/servicecomb/demo/multiServiceCenterClient/MultiServiceCenterIT.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/test/java/org/apache/servicecomb/demo/multiServiceCenterClient/MultiServiceCenterIT.java new file mode 100644 index 00000000000..f7cd5157171 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/test/java/org/apache/servicecomb/demo/multiServiceCenterClient/MultiServiceCenterIT.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.multiServiceCenterClient; + +import org.apache.servicecomb.demo.TestMgr; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class MultiServiceCenterIT { + @BeforeEach + public void setUp() throws Exception { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + Application.main(new String[0]); + + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} 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 new file mode 100644 index 00000000000..ddfd3570a43 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverA/pom.xml @@ -0,0 +1,106 @@ + + + + + 4.0.0 + demo-multi-service-center-servera + Java Chassis::Demo::Multi Service Centers::ServerA + + org.apache.servicecomb.demo + demo-multi-service-center + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.multiServiceCenter.ServerApplication + + + + jakarta.ws.rs + jakarta.ws.rs-api + + + 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-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 new file mode 100644 index 00000000000..4515d6c7575 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerApplication.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.multiServiceCenter; + +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); + } +} 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 new file mode 100644 index 00000000000..16fdb41bdde --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/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.multiServiceCenter; + +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-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 new file mode 100644 index 00000000000..03f54c0db15 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverA/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-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-serverA/src/main/resources/log4j2.xml b/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 new file mode 100644 index 00000000000..66dfd58d9b4 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/pom.xml @@ -0,0 +1,106 @@ + + + + + 4.0.0 + demo-multi-service-center-serverb + Java Chassis::Demo::Multi Service Centers::ServerB + + org.apache.servicecomb.demo + demo-multi-service-center + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.multiServiceCenterServerB.ServerApplication + + + + jakarta.ws.rs + jakarta.ws.rs-api + + + 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-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 new file mode 100644 index 00000000000..edbb116ccca --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ConfigurationEndpoint.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.multiServiceCenterServerB; + +import java.util.List; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +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; + +import jakarta.ws.rs.core.MediaType; + +@RestSchema(schemaId = "ConfigurationEndpoint") +@RequestMapping(path = "/register/url/config", produces = MediaType.APPLICATION_JSON) +public class ConfigurationEndpoint { + private static final Logger LOGGER + = LoggerFactory.getLogger(ServerEndpoint.class); + + private Environment environment; + + @Autowired + public ConfigurationEndpoint(Environment environment) { + this.environment = environment; + } + + @Value("${demo.multi.service.center.serverB.key1}") + private String key1; + + @Value("${demo.multi.service.center.serverB.key2}") + private String key2; + + @Value("${demo.multi.service.center.serverB.key3}") + private String key3; + + @Value("${demo.multi.service.center.serverB.key4}") + private String key4; + + @Value("${demo.multi.service.center.serverB.key5}") + private String key5; + + @Value("${demo.multi.service.center.serverB.key6}") + private String key6; + + @Value("${demo.multi.service.center.serverB.key7}") + private List key7; + + @GetMapping(path = "/config") + public String getValue(@RequestParam(name = "key") String key, @RequestParam(name = "type") int type) { + if (type == 1) { + return environment.getProperty(key); + } else if (type == 2) { + return environment.getProperty(key); + } else { + switch (key) { + case "demo.multi.service.center.serverB.key1": + return key1; + case "demo.multi.service.center.serverB.key2": + return key2; + case "demo.multi.service.center.serverB.key3": + return key3; + case "demo.multi.service.center.serverB.key4": + return key4; + case "demo.multi.service.center.serverB.key5": + return key5; + case "demo.multi.service.center.serverB.key6": + return key6; + default: + return null; + } + } + } + + @SuppressWarnings("unchecked") + @GetMapping(path = "/configList") + public List getValueList(@RequestParam(name = "key") String key, @RequestParam(name = "type") int type) { + if (type == 1) { + return environment.getProperty(key, List.class); + } else { + switch (key) { + case "demo.multi.service.center.serverB.key7": + return key7; + default: + return null; + } + } + } +} 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 new file mode 100644 index 00000000000..49106b7b8dd --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerApplication.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.multiServiceCenterServerB; + +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); + } +} 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 new file mode 100644 index 00000000000..d14c1d4a7b0 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/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.multiServiceCenterServerB; + +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-multi-service-center/demo-multi-service-center-serverB/src/main/resources/application.properties b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/application.properties new file mode 100644 index 00000000000..121f1aa95c6 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/application.properties @@ -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. +## --------------------------------------------------------------------------- + +demo.multi.service.center.serverB.actual.key6=key6 +demo.multi.service.center.serverB.actual.key7.1=key71 +demo.multi.service.center.serverB.actual.key7.2=key72 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 new file mode 100644 index 00000000000..e9850bc8c23 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/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. +## --------------------------------------------------------------------------- + +server: + port: 8082 + +servicecomb: + service: + 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 + +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/log4j2.xml b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/mapping.yaml b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/mapping.yaml new file mode 100644 index 00000000000..01642a348f7 --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/mapping.yaml @@ -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. +# + + +demo.multi.service.center.serverB.actual.key5: + - demo.multi.service.center.serverB.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 new file mode 100644 index 00000000000..2164cf6ac9f --- /dev/null +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +servicecomb-config-order: 1 + +demo.multi.service.center.serverB: + key1: key1-override + key2: key2-override + key5: key5 + actual: + key5: key5-high + 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} diff --git a/demo/demo-multi-service-center/pom.xml b/demo/demo-multi-service-center/pom.xml new file mode 100644 index 00000000000..cf9c101e496 --- /dev/null +++ b/demo/demo-multi-service-center/pom.xml @@ -0,0 +1,67 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-multi-service-center + Java Chassis::Demo::Multi Service Centers + pom + + demo-multi-service-center-serverA + demo-multi-service-center-serverB + demo-multi-service-center-client + + + + + org.apache.servicecomb + solution-basic + + + org.apache.servicecomb + java-chassis-spring-boot-starter-servlet + + + org.apache.servicecomb + registry-service-center + + + 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-multiple/a-client/pom.xml b/demo/demo-multiple/a-client/pom.xml index 91f432afb72..84131a7909c 100644 --- a/demo/demo-multiple/a-client/pom.xml +++ b/demo/demo-multiple/a-client/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-multiple - 0.3.0-SNAPSHOT - - a-client + 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.demo + demo-multiple + 3.4.0-SNAPSHOT + + a-client + Java Chassis::Demo::Multiple::A client + + + org.apache.servicecomb.demo.multiple.a.client.AClientMain + - - io.servicecomb.demo.multiple.a.client.AClientMain - - \ No newline at end of file + diff --git a/demo/demo-multiple/a-client/src/main/java/io/servicecomb/demo/multiple/a/client/AClient.java b/demo/demo-multiple/a-client/src/main/java/io/servicecomb/demo/multiple/a/client/AClient.java deleted file mode 100644 index a0d62c667a4..00000000000 --- a/demo/demo-multiple/a-client/src/main/java/io/servicecomb/demo/multiple/a/client/AClient.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.multiple.a.client; - -import org.springframework.stereotype.Component; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.provider.pojo.RpcReference; - -@Component -public class AClient { - @RpcReference(microserviceName = "${a-server.name}", schemaId = "a-hello") - private AIntf intf; - - public void run() { - String result = intf.hello("serviceComb"); - TestMgr.check("a hello serviceComb", result); - } -} diff --git a/demo/demo-multiple/a-client/src/main/java/io/servicecomb/demo/multiple/a/client/AClientMain.java b/demo/demo-multiple/a-client/src/main/java/io/servicecomb/demo/multiple/a/client/AClientMain.java deleted file mode 100644 index 9f316708c41..00000000000 --- a/demo/demo-multiple/a-client/src/main/java/io/servicecomb/demo/multiple/a/client/AClientMain.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.multiple.a.client; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class AClientMain { - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - - AClient client = BeanUtils.getContext().getBean(AClient.class); - client.run(); - - TestMgr.summary(); - } -} diff --git a/demo/demo-multiple/a-client/src/main/java/io/servicecomb/demo/multiple/a/client/AIntf.java b/demo/demo-multiple/a-client/src/main/java/io/servicecomb/demo/multiple/a/client/AIntf.java deleted file mode 100644 index f13bb68a474..00000000000 --- a/demo/demo-multiple/a-client/src/main/java/io/servicecomb/demo/multiple/a/client/AIntf.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.multiple.a.client; - -public interface AIntf { - String hello(String name); -} diff --git a/demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AClient.java b/demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AClient.java new file mode 100644 index 00000000000..2a19c4febd8 --- /dev/null +++ b/demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AClient.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.multiple.a.client; + +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component +public class AClient { + @RpcReference(microserviceName = "${a-server.name}", schemaId = "a-hello") + private AIntf intf; + + public void run() { + String result = intf.hello("serviceComb"); + TestMgr.check("a hello serviceComb", result); + } +} 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 new file mode 100644 index 00000000000..907df46c2c7 --- /dev/null +++ b/demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AClientMain.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.multiple.a.client; + +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.builder.SpringApplicationBuilder; + +public class AClientMain { + public static void main(String[] args) throws Exception { + new SpringApplicationBuilder(AClientMain.class).web(WebApplicationType.NONE).run(args); + + AClient client = BeanUtils.getContext().getBean(AClient.class); + client.run(); + + TestMgr.summary(); + } +} diff --git a/demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AIntf.java b/demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AIntf.java new file mode 100644 index 00000000000..66b5672d3e5 --- /dev/null +++ b/demo/demo-multiple/a-client/src/main/java/org/apache/servicecomb/demo/multiple/a/client/AIntf.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.multiple.a.client; + +public interface AIntf { + String hello(String name); +} diff --git a/demo/demo-multiple/a-client/src/main/resources/log4j2.xml b/demo/demo-multiple/a-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-multiple/a-client/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 458c46d2b4c..81a782bafc2 100644 --- a/demo/demo-multiple/a-client/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/a-client/src/main/resources/microservice.yaml @@ -1,9 +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. +## --------------------------------------------------------------------------- + a-server.name: a-server -APPLICATION_ID: multiple -service_description: - name: a-client - version: 0.0.1 -cse: + +servicecomb: service: - registry: - address: http://127.0.0.1:9980 + 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 a423f5a7518..d38b12d8d62 100644 --- a/demo/demo-multiple/a-server/pom.xml +++ b/demo/demo-multiple/a-server/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-multiple - 0.3.0-SNAPSHOT - - a-server + 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.demo + demo-multiple + 3.4.0-SNAPSHOT + + a-server + Java Chassis::Demo::Multiple::A server + + + + org.apache.servicecomb.demo.multiple.a.server.AServerMain + - - io.servicecomb.demo.multiple.a.server.AServerMain - - \ No newline at end of file + diff --git a/demo/demo-multiple/a-server/src/main/java/io/servicecomb/demo/multiple/a/server/AImpl.java b/demo/demo-multiple/a-server/src/main/java/io/servicecomb/demo/multiple/a/server/AImpl.java deleted file mode 100644 index da9741001e6..00000000000 --- a/demo/demo-multiple/a-server/src/main/java/io/servicecomb/demo/multiple/a/server/AImpl.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.multiple.a.server; - -import io.servicecomb.provider.pojo.RpcSchema; - -@RpcSchema(schemaId = "a-hello") -public class AImpl { - public String hello(String name) { - return "a hello " + name; - } -} diff --git a/demo/demo-multiple/a-server/src/main/java/io/servicecomb/demo/multiple/a/server/AServerMain.java b/demo/demo-multiple/a-server/src/main/java/io/servicecomb/demo/multiple/a/server/AServerMain.java deleted file mode 100644 index 2c347a18a5f..00000000000 --- a/demo/demo-multiple/a-server/src/main/java/io/servicecomb/demo/multiple/a/server/AServerMain.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.multiple.a.server; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class AServerMain { - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/demo/demo-multiple/a-server/src/main/java/org/apache/servicecomb/demo/multiple/a/server/AImpl.java b/demo/demo-multiple/a-server/src/main/java/org/apache/servicecomb/demo/multiple/a/server/AImpl.java new file mode 100644 index 00000000000..4378a1e049a --- /dev/null +++ b/demo/demo-multiple/a-server/src/main/java/org/apache/servicecomb/demo/multiple/a/server/AImpl.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.multiple.a.server; + +import org.apache.servicecomb.provider.pojo.RpcSchema; + +@RpcSchema(schemaId = "a-hello") +public class AImpl { + public String hello(String name) { + return "a hello " + name; + } +} 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 new file mode 100644 index 00000000000..40c96981c7a --- /dev/null +++ b/demo/demo-multiple/a-server/src/main/java/org/apache/servicecomb/demo/multiple/a/server/AServerMain.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.multiple.a.server; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.builder.SpringApplicationBuilder; + +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/log4j2.xml b/demo/demo-multiple/a-server/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-multiple/a-server/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 cce13fbbcd0..d916c594f79 100644 --- a/demo/demo-multiple/a-server/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/a-server/src/main/resources/microservice.yaml @@ -1,11 +1,28 @@ -APPLICATION_ID: multiple -service_description: - name: a-server - version: 0.0.1 -cse: +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: - registry: - address: http://127.0.0.1:9980 + 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 highway: diff --git a/demo/demo-multiple/b-client/pom.xml b/demo/demo-multiple/b-client/pom.xml index f218fe659bb..4ab02a66f1b 100644 --- a/demo/demo-multiple/b-client/pom.xml +++ b/demo/demo-multiple/b-client/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-multiple - 0.3.0-SNAPSHOT - - b-client + 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.demo + demo-multiple + 3.4.0-SNAPSHOT + + b-client + Java Chassis::Demo::Multiple::B client + + + org.apache.servicecomb.demo.multiple.b.client.BClientMain + - - io.servicecomb.demo.multiple.b.client.BClientMain - - \ No newline at end of file + diff --git a/demo/demo-multiple/b-client/src/main/java/io/servicecomb/demo/multiple/b/client/BClient.java b/demo/demo-multiple/b-client/src/main/java/io/servicecomb/demo/multiple/b/client/BClient.java deleted file mode 100644 index 7d4f9823d09..00000000000 --- a/demo/demo-multiple/b-client/src/main/java/io/servicecomb/demo/multiple/b/client/BClient.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.multiple.b.client; - -import org.springframework.stereotype.Component; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.provider.pojo.RpcReference; - -@Component -public class BClient { - @RpcReference(microserviceName = "${b-server.name}", schemaId = "b-hello") - private BIntf intf; - - public void run() { - String result = intf.hello("serviceComb"); - TestMgr.check("b hello serviceComb", result); - } -} diff --git a/demo/demo-multiple/b-client/src/main/java/io/servicecomb/demo/multiple/b/client/BClientMain.java b/demo/demo-multiple/b-client/src/main/java/io/servicecomb/demo/multiple/b/client/BClientMain.java deleted file mode 100644 index bcc1b897fbb..00000000000 --- a/demo/demo-multiple/b-client/src/main/java/io/servicecomb/demo/multiple/b/client/BClientMain.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.multiple.b.client; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class BClientMain { - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - - BClient client = BeanUtils.getContext().getBean(BClient.class); - client.run(); - - TestMgr.summary(); - } -} diff --git a/demo/demo-multiple/b-client/src/main/java/io/servicecomb/demo/multiple/b/client/BIntf.java b/demo/demo-multiple/b-client/src/main/java/io/servicecomb/demo/multiple/b/client/BIntf.java deleted file mode 100644 index a3352ff25db..00000000000 --- a/demo/demo-multiple/b-client/src/main/java/io/servicecomb/demo/multiple/b/client/BIntf.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.multiple.b.client; - -public interface BIntf { - String hello(String name); -} diff --git a/demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BClient.java b/demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BClient.java new file mode 100644 index 00000000000..577dfb70f13 --- /dev/null +++ b/demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BClient.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.multiple.b.client; + +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component +public class BClient { + @RpcReference(microserviceName = "${b-server.name}", schemaId = "b-hello") + private BIntf intf; + + public void run() { + String result = intf.hello("serviceComb"); + TestMgr.check("b hello serviceComb", result); + } +} 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 new file mode 100644 index 00000000000..a6400d91a4e --- /dev/null +++ b/demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BClientMain.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.multiple.b.client; + +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.builder.SpringApplicationBuilder; + +public class BClientMain { + public static void main(String[] args) throws Exception { + new SpringApplicationBuilder(BClientMain.class).web(WebApplicationType.NONE).run(args); + + BClient client = BeanUtils.getContext().getBean(BClient.class); + client.run(); + + TestMgr.summary(); + } +} diff --git a/demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BIntf.java b/demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BIntf.java new file mode 100644 index 00000000000..221a57b7371 --- /dev/null +++ b/demo/demo-multiple/b-client/src/main/java/org/apache/servicecomb/demo/multiple/b/client/BIntf.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.multiple.b.client; + +public interface BIntf { + String hello(String name); +} diff --git a/demo/demo-multiple/b-client/src/main/resources/log4j2.xml b/demo/demo-multiple/b-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-multiple/b-client/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 687452c6bce..8445ed3f4d8 100644 --- a/demo/demo-multiple/b-client/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/b-client/src/main/resources/microservice.yaml @@ -1,9 +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. +## --------------------------------------------------------------------------- + b-server.name: b-server -APPLICATION_ID: multiple -service_description: - name: b-client - version: 0.0.1 -cse: + +servicecomb: service: - registry: - address: http://127.0.0.1:9980 + 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 aa72d07c851..e33e750d8e4 100644 --- a/demo/demo-multiple/b-server/pom.xml +++ b/demo/demo-multiple/b-server/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-multiple - 0.3.0-SNAPSHOT - - b-server + 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.demo + demo-multiple + 3.4.0-SNAPSHOT + + + b-server + Java Chassis::Demo::Multiple::B server - - io.servicecomb.demo.multiple.b.server.BServerMain - - \ No newline at end of file + + org.apache.servicecomb.demo.multiple.b.server.BServerMain + + diff --git a/demo/demo-multiple/b-server/src/main/java/io/servicecomb/demo/multiple/b/server/BImpl.java b/demo/demo-multiple/b-server/src/main/java/io/servicecomb/demo/multiple/b/server/BImpl.java deleted file mode 100644 index c8153dbee2e..00000000000 --- a/demo/demo-multiple/b-server/src/main/java/io/servicecomb/demo/multiple/b/server/BImpl.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.multiple.b.server; - -import io.servicecomb.provider.pojo.RpcSchema; - -@RpcSchema(schemaId = "b-hello") -public class BImpl { - public String hello(String name) { - return "b hello " + name; - } -} diff --git a/demo/demo-multiple/b-server/src/main/java/io/servicecomb/demo/multiple/b/server/BServerMain.java b/demo/demo-multiple/b-server/src/main/java/io/servicecomb/demo/multiple/b/server/BServerMain.java deleted file mode 100644 index 34f3adca4ba..00000000000 --- a/demo/demo-multiple/b-server/src/main/java/io/servicecomb/demo/multiple/b/server/BServerMain.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.multiple.b.server; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class BServerMain { - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/demo/demo-multiple/b-server/src/main/java/org/apache/servicecomb/demo/multiple/b/server/BImpl.java b/demo/demo-multiple/b-server/src/main/java/org/apache/servicecomb/demo/multiple/b/server/BImpl.java new file mode 100644 index 00000000000..8808731a56d --- /dev/null +++ b/demo/demo-multiple/b-server/src/main/java/org/apache/servicecomb/demo/multiple/b/server/BImpl.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.multiple.b.server; + +import org.apache.servicecomb.provider.pojo.RpcSchema; + +@RpcSchema(schemaId = "b-hello") +public class BImpl { + public String hello(String name) { + return "b hello " + name; + } +} 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 new file mode 100644 index 00000000000..267925aceae --- /dev/null +++ b/demo/demo-multiple/b-server/src/main/java/org/apache/servicecomb/demo/multiple/b/server/BServerMain.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.multiple.b.server; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.builder.SpringApplicationBuilder; + +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/log4j2.xml b/demo/demo-multiple/b-server/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-multiple/b-server/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 ad998231c1f..695ed8f50a8 100644 --- a/demo/demo-multiple/b-server/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/b-server/src/main/resources/microservice.yaml @@ -1,11 +1,28 @@ -APPLICATION_ID: multiple -service_description: - name: b-server - version: 0.0.1 -cse: +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: - registry: - address: http://127.0.0.1:9980 + 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 highway: diff --git a/demo/demo-multiple/multiple-client/pom.xml b/demo/demo-multiple/multiple-client/pom.xml index 5d9f00fea92..7dcbb6f14f2 100644 --- a/demo/demo-multiple/multiple-client/pom.xml +++ b/demo/demo-multiple/multiple-client/pom.xml @@ -1,12 +1,13 @@ - + - 4.0.0 - - io.servicecomb.demo - demo-multiple - 0.3.0-SNAPSHOT - - multiple-client - - - io.servicecomb.demo - a-client - - - io.servicecomb.demo - b-client - - + 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.demo + demo-multiple + 3.4.0-SNAPSHOT + + multiple-client + Java Chassis::Demo::Multiple::Client + + + org.apache.servicecomb.demo + a-client + + + org.apache.servicecomb.demo + b-client + + - - io.servicecomb.demo.multiple.client.MultipleClient - - - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-run-config - 0.3.0-SNAPSHOT - - - - - - + + org.apache.servicecomb.demo.multiple.client.MultipleClient + - - - docker - - multiple-server - - -Dcse.highway.address=0.0.0.0:7070 - -Dcse.rest.address=0.0.0.0:8080 - - - - - - io.fabric8 - docker-maven-plugin - - - - - + + + docker + + 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 + + + + + - \ No newline at end of file + diff --git a/demo/demo-multiple/multiple-client/src/main/java/io/servicecomb/demo/multiple/client/MultipleClient.java b/demo/demo-multiple/multiple-client/src/main/java/io/servicecomb/demo/multiple/client/MultipleClient.java deleted file mode 100644 index b01e32129ab..00000000000 --- a/demo/demo-multiple/multiple-client/src/main/java/io/servicecomb/demo/multiple/client/MultipleClient.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.demo.multiple.client; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.demo.multiple.a.client.AClient; -import io.servicecomb.demo.multiple.b.client.BClient; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class MultipleClient { - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - - AClient aClient = BeanUtils.getContext().getBean(AClient.class); - BClient bClient = BeanUtils.getContext().getBean(BClient.class); - - aClient.run(); - bClient.run(); - - TestMgr.summary(); - } -} 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 new file mode 100644 index 00000000000..ae804ad8bea --- /dev/null +++ b/demo/demo-multiple/multiple-client/src/main/java/org/apache/servicecomb/demo/multiple/client/MultipleClient.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.demo.multiple.client; + +import org.apache.servicecomb.demo.TestMgr; +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.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(basePackages = {"org.apache.servicecomb.demo.multiple.a.client", + "org.apache.servicecomb.demo.multiple.b.client"}) +public class MultipleClient { + public static void main(String[] args) throws Exception { + new SpringApplicationBuilder(MultipleClient.class).web(WebApplicationType.NONE).run(args); + + runTest(); + } + + public static void runTest() { + AClient aClient = BeanUtils.getContext().getBean(AClient.class); + BClient bClient = BeanUtils.getContext().getBean(BClient.class); + + aClient.run(); + bClient.run(); + + TestMgr.summary(); + } +} diff --git a/demo/demo-multiple/multiple-client/src/main/resources/log4j2.xml b/demo/demo-multiple/multiple-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-multiple/multiple-client/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 566b7b05836..1ece43eac98 100644 --- a/demo/demo-multiple/multiple-client/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/multiple-client/src/main/resources/microservice.yaml @@ -1,7 +1,26 @@ -cse-config-order: 1000 +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: 1000 a-server.name: merged-server b-server.name: merged-server -APPLICATION_ID: multiple -service_description: - name: merged-client - version: 0.0.1 \ No newline at end of file + +servicecomb: + service: + application: multiple + name: merged-client + version: 0.0.1 diff --git a/demo/demo-multiple/multiple-client/src/test/java/org/apache/servicecomb/demo/multiple/client/MultipleIT.java b/demo/demo-multiple/multiple-client/src/test/java/org/apache/servicecomb/demo/multiple/client/MultipleIT.java new file mode 100644 index 00000000000..c5c839ae651 --- /dev/null +++ b/demo/demo-multiple/multiple-client/src/test/java/org/apache/servicecomb/demo/multiple/client/MultipleIT.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.multiple.client; + +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 = MultipleClient.class) +public class MultipleIT { + + @BeforeEach + public void setUp() { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + MultipleClient.runTest(); + + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} diff --git a/demo/demo-multiple/multiple-server/pom.xml b/demo/demo-multiple/multiple-server/pom.xml index 1eadd1b2131..18c7ae19069 100644 --- a/demo/demo-multiple/multiple-server/pom.xml +++ b/demo/demo-multiple/multiple-server/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-multiple - 0.3.0-SNAPSHOT - - multiple-server - - - io.servicecomb.demo - a-server - - - io.servicecomb.demo - b-server - - + 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.demo + demo-multiple + 3.4.0-SNAPSHOT + + multiple-server + Java Chassis::Demo::Multiple::Server + + + org.apache.servicecomb.demo + a-server + + + org.apache.servicecomb.demo + b-server + + - - io.servicecomb.demo.multiple.server.MultipleServer - - - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-build-config - 0.3.0-SNAPSHOT - - - - + + org.apache.servicecomb.demo.multiple.server.MultipleServer + - - + + + + org.springframework.boot + spring-boot-maven-plugin + + + - - - docker - - - - io.fabric8 - docker-maven-plugin - - - org.commonjava.maven.plugins - directory-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 + + + + + - \ No newline at end of file + diff --git a/demo/demo-multiple/multiple-server/src/main/java/io/servicecomb/demo/multiple/server/MultipleServer.java b/demo/demo-multiple/multiple-server/src/main/java/io/servicecomb/demo/multiple/server/MultipleServer.java deleted file mode 100644 index 93f17966399..00000000000 --- a/demo/demo-multiple/multiple-server/src/main/java/io/servicecomb/demo/multiple/server/MultipleServer.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.demo.multiple.server; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class MultipleServer { - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} 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 new file mode 100644 index 00000000000..5b830e46e83 --- /dev/null +++ b/demo/demo-multiple/multiple-server/src/main/java/org/apache/servicecomb/demo/multiple/server/MultipleServer.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.multiple.server; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan(basePackages = {"org.apache.servicecomb.demo.multiple.a.server", + "org.apache.servicecomb.demo.multiple.b.server"}) +public class MultipleServer { + public static void main(String[] args) throws Exception { + new SpringApplicationBuilder(MultipleServer.class).web(WebApplicationType.NONE).run(args); + } +} diff --git a/demo/demo-multiple/multiple-server/src/main/resources/log4j2.xml b/demo/demo-multiple/multiple-server/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-multiple/multiple-server/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 3876c41d358..b1eeade93d7 100644 --- a/demo/demo-multiple/multiple-server/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/multiple-server/src/main/resources/microservice.yaml @@ -1,5 +1,24 @@ -cse-config-order: 1000 -APPLICATION_ID: multiple -service_description: - name: merged-server - version: 0.0.1 \ No newline at end of file +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: 1000 + +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 8001720caf6..7496a2050d7 100644 --- a/demo/demo-multiple/pom.xml +++ b/demo/demo-multiple/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.3.0-SNAPSHOT - - demo-multiple - pom - - a-server - a-client - b-server - b-client - multiple-server - multiple-client - + 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.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-multiple + Java Chassis::Demo::Multiple + pom + + a-server + a-client + b-server + b-client + multiple-server + multiple-client + - - - - io.servicecomb.demo - a-server - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - a-client - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - b-server - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - b-client - 0.3.0-SNAPSHOT - - - + + + + org.apache.servicecomb.demo + a-server + ${project.version} + + + org.apache.servicecomb.demo + a-client + ${project.version} + + + org.apache.servicecomb.demo + b-server + ${project.version} + + + org.apache.servicecomb.demo + b-client + ${project.version} + + + - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-pojo - - + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + - - - - org.apache.maven.plugins - maven-dependency-plugin - - - - \ No newline at end of file + 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-perf-client/pom.xml b/demo/demo-perf-client/pom.xml deleted file mode 100644 index 102758fd9ff..00000000000 --- a/demo/demo-perf-client/pom.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.3.0-SNAPSHOT - - demo-perf-client - - - io.servicecomb.demo - pojo-client - - - - - io.servicecomb.demo.client.perf.PerfClient - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - - diff --git a/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/ClientThread.java b/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/ClientThread.java deleted file mode 100644 index 5e7356fa51f..00000000000 --- a/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/ClientThread.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.client.perf; - -import io.servicecomb.demo.pojo.client.PojoClient; -import io.servicecomb.demo.server.Test; -import io.servicecomb.demo.server.TestRequest; -import io.servicecomb.demo.server.User; -import io.servicecomb.foundation.common.CommonThread; - -public class ClientThread extends CommonThread { - @Override - public void run() { - Test test = PojoClient.test; - - while (isRunning()) { - int idx = 0; - for (;;) { - User user = new User(); - - TestRequest request = new TestRequest(); - request.setUser(user); - request.setIndex(idx); - request.setData(PojoClient.buffer); - - try { - User result = test.wrapParam(request); - - if (result.getIndex() != idx) { - System.out.printf("error result:%s, expect idx %d\n", result, idx); - } - } catch (Throwable e) { - // e.printStackTrace(); - } - } - } - } -} diff --git a/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/ClientVerticle.java b/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/ClientVerticle.java deleted file mode 100644 index 80bdd792e86..00000000000 --- a/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/ClientVerticle.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.client.perf; - -import io.servicecomb.core.CseContext; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.invocation.InvocationFactory; -import io.servicecomb.core.provider.consumer.InvokerUtils; -import io.servicecomb.core.provider.consumer.ReferenceConfig; -import io.servicecomb.demo.pojo.client.PojoClient; -import io.servicecomb.demo.server.Test; -import io.servicecomb.demo.server.TestRequest; -import io.servicecomb.demo.server.User; -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.servicecomb.swagger.invocation.exception.InvocationException; -import io.vertx.core.AbstractVerticle; - -public class ClientVerticle extends AbstractVerticle { - - Test test = PojoClient.test; - - ReferenceConfig config = - CseContext.getInstance().getConsumerProviderManager().setTransport("pojo", Config.getTransport()); - - int idx = 0; - - @Override - public void start() throws Exception { - - vertx.setTimer(100, this::send); - } - - protected void send(Long event) { - User user = new User(); - - TestRequest request = new TestRequest(); - request.setUser(user); - request.setIndex(idx); - request.setData(PojoClient.buffer); - - SchemaMeta schemaMeta = config.getMicroserviceMeta().ensureFindSchemaMeta("server"); - Object[] args = new Object[] {request}; - Invocation invocation = InvocationFactory.forConsumer(config, schemaMeta, "wrapParam", args); - InvokerUtils.reactiveInvoke(invocation, ar -> { - if (ar.isSuccessed()) { - User result = ar.getResult(); - if (result.getIndex() != idx) { - System.out.printf("error result:%s, expect idx %d\n", result, idx); - } - } else { - CommonExceptionData data = - (CommonExceptionData) ((InvocationException) ar.getResult()).getErrorData(); - System.out.println(data.getMessage()); - } - - send(null); - }); - } -} diff --git a/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/Config.java b/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/Config.java deleted file mode 100644 index 9f4e76a3668..00000000000 --- a/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/Config.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.client.perf; - -public class Config { - private static int clientThread; - - private static String transport; - - private static String mode; - - public static String getTransport() { - return transport; - } - - public static void setTransport(String transport) { - Config.transport = transport; - } - - public static int getClientThread() { - return clientThread; - } - - public static void setClientThread(int clientThread) { - Config.clientThread = clientThread; - } - - public static String getMode() { - return mode; - } - - public static void setMode(String mode) { - Config.mode = mode; - } -} diff --git a/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/PerfClient.java b/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/PerfClient.java deleted file mode 100644 index 9af1b7fa8fd..00000000000 --- a/demo/demo-perf-client/src/main/java/io/servicecomb/demo/client/perf/PerfClient.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.client.perf; - -import io.servicecomb.core.CseContext; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.foundation.vertx.VertxUtils; -import io.vertx.core.Vertx; - -public class PerfClient { - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - - System.out.println("mode:" + Config.getMode()); - - CseContext.getInstance().getConsumerProviderManager().setTransport("pojo", Config.getTransport()); - System.out.printf("test %s performance\n", Config.getTransport()); - - if ("reactive".equals(Config.getMode())) { - Vertx vertx = VertxUtils.getOrCreateVertxByName("perfClient", null); - VertxUtils.deployVerticle(vertx, ClientVerticle.class, Config.getClientThread()); - return; - } - - for (int idx = 0; idx < Config.getClientThread(); idx++) { - new ClientThread().start(); - } - } -} diff --git a/demo/demo-perf-client/src/main/resources/META-INF/spring/perf.client.bean.xml b/demo/demo-perf-client/src/main/resources/META-INF/spring/perf.client.bean.xml deleted file mode 100644 index b448f60ae97..00000000000 --- a/demo/demo-perf-client/src/main/resources/META-INF/spring/perf.client.bean.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/demo/demo-perf-client/src/main/resources/config/cse.demo.perf.properties b/demo/demo-perf-client/src/main/resources/config/cse.demo.perf.properties deleted file mode 100644 index a927ae2b3af..00000000000 --- a/demo/demo-perf-client/src/main/resources/config/cse.demo.perf.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -cse.demo.client.thread=100 -cse.demo.client.transport=highway -cse.demo.client.mode=sync -#cse.demo.client.mode=reactive diff --git a/demo/demo-perf-client/src/main/resources/microservice.yaml b/demo/demo-perf-client/src/main/resources/microservice.yaml deleted file mode 100644 index e438f7a9331..00000000000 --- a/demo/demo-perf-client/src/main/resources/microservice.yaml +++ /dev/null @@ -1,24 +0,0 @@ -APPLICATION_ID: pojotest -service_description: - name: perfClient - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:9980 - rest: - client: - thread-count: 10 - connection-pool-per-thread: 1 - server: - thread-count: 10 - highway: - thread-count: 1 - connection-pool-per-thread: 10 - handler: - chain: - Consumer: - default: loadbalance - metrics: - cycle: - ms: 1000 diff --git a/demo/demo-pojo/pojo-client/pom.xml b/demo/demo-pojo/pojo-client/pom.xml index 2a30716419e..582c8c8f511 100644 --- a/demo/demo-pojo/pojo-client/pom.xml +++ b/demo/demo-pojo/pojo-client/pom.xml @@ -1,12 +1,13 @@ - - 4.0.0 - - io.servicecomb.demo - demo-pojo - 0.3.0-SNAPSHOT - - pojo-client + + 4.0.0 + + org.apache.servicecomb.demo + demo-pojo + 3.4.0-SNAPSHOT + + pojo-client + Java Chassis::Demo::POJO::Client + + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + metrics-core + + + org.apache.servicecomb + foundation-test-scaffolding + compile + + - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-pojo - - + + org.apache.servicecomb.demo.pojo.client.PojoClient + - - io.servicecomb.demo.pojo.client.PojoClient - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-run-config - 0.3.0-SNAPSHOT - - - - - - - - - docker - - pojo-server - - -Dcse.highway.address=0.0.0.0:7070 - -Dcse.rest.address=0.0.0.0:8080 - - - - - - io.fabric8 - docker-maven-plugin - - - - - + + + docker + + 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 + + + + + diff --git a/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/BeanRpcTest.java b/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/BeanRpcTest.java deleted file mode 100644 index 6a57cdb995e..00000000000 --- a/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/BeanRpcTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.demo.pojo.client; - -import io.servicecomb.demo.server.Test; -import io.servicecomb.provider.pojo.RpcReference; - -public class BeanRpcTest { - @RpcReference(microserviceName = "pojo") - private Test test; - - public BeanRpcTest() { - System.out.println("init"); - } - - public void init() { - new Thread() { - public void run() { - while (true) { - try { - System.out.println("XXXXXXXXXXXXXXXXXXXXXXXX" + test.getTestString(null)); - break; - } catch (Exception e) { - e.printStackTrace(); - try { - Thread.sleep(1000); - } catch (InterruptedException e1) { - e1.printStackTrace(); - } - continue; - } - } - } - }.start(); - } -} diff --git a/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/CodeFirstPojoClient.java b/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/CodeFirstPojoClient.java deleted file mode 100644 index 1255f6b6f56..00000000000 --- a/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/CodeFirstPojoClient.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.client; - -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; - -import io.servicecomb.core.CseContext; -import io.servicecomb.demo.CodeFirstPojoIntf; -import io.servicecomb.demo.DemoConst; -import io.servicecomb.demo.TestMgr; -import io.servicecomb.demo.compute.Person; -import io.servicecomb.demo.server.User; -import io.servicecomb.provider.pojo.RpcReference; - -public class CodeFirstPojoClient { - @RpcReference(microserviceName = "pojo", schemaId = "io.servicecomb.demo.CodeFirstPojoIntf") - public CodeFirstPojoIntf codeFirstAnnotation; - - @RpcReference(microserviceName = "pojo") - public CodeFirstPojoIntf codeFirstAnnotationEmptySchemaId; - - @Inject - private CodeFirstPojoIntf codeFirstFromXml; - - public void testCodeFirst(String microserviceName) { - for (String transport : DemoConst.transports) { - CseContext.getInstance().getConsumerProviderManager().setTransport(microserviceName, transport); - TestMgr.setMsg(microserviceName, transport); - - testAll(codeFirstAnnotation, transport); - testAll(codeFirstAnnotationEmptySchemaId, transport); - testAll(codeFirstFromXml, transport); - } - } - - protected void testAll(CodeFirstPojoIntf codeFirst, String transport) { - testCodeFirstUserMap(codeFirst); - testCodeFirstUserArray(codeFirst); - testCodeFirstStrings(codeFirst); - testCodeFirstBytes(codeFirst); - testCodeFirstAddDate(codeFirst); - testCodeFirstAddString(codeFirst); - testCodeFirstIsTrue(codeFirst); - testCodeFirstSayHi2(codeFirst); - testCodeFirstSayHi(codeFirst); - testCodeFirstSaySomething(codeFirst); - // testCodeFirstRawJsonString(template, cseUrlPrefix); - testCodeFirstSayHello(codeFirst); - testCodeFirstReduce(codeFirst); - } - - private void testCodeFirstUserMap(CodeFirstPojoIntf codeFirst) { - User user1 = new User(); - user1.setNames(new String[] {"u1", "u2"}); - - User user2 = new User(); - user2.setNames(new String[] {"u3", "u4"}); - - Map userMap = new HashMap<>(); - userMap.put("u1", user1); - userMap.put("u2", user2); - Map result = codeFirst.testUserMap(userMap); - - TestMgr.check("u1", result.get("u1").getNames()[0]); - TestMgr.check("u2", result.get("u1").getNames()[1]); - TestMgr.check("u3", result.get("u2").getNames()[0]); - TestMgr.check("u4", result.get("u2").getNames()[1]); - } - - private void testCodeFirstUserArray(CodeFirstPojoIntf codeFirst) { - User user1 = new User(); - user1.setNames(new String[] {"u1", "u2"}); - - User user2 = new User(); - user2.setNames(new String[] {"u3", "u4"}); - - User[] users = new User[] {user1, user2}; - List result = codeFirst.testUserArray(Arrays.asList(users)); - TestMgr.check("u1", result.get(0).getNames()[0]); - TestMgr.check("u2", result.get(0).getNames()[1]); - TestMgr.check("u3", result.get(1).getNames()[0]); - TestMgr.check("u4", result.get(1).getNames()[1]); - } - - private void testCodeFirstStrings(CodeFirstPojoIntf codeFirst) { - String[] result = codeFirst.testStrings(new String[] {"a", "b"}); - TestMgr.check("aa0", result[0]); - TestMgr.check("b", result[1]); - } - - private void testCodeFirstBytes(CodeFirstPojoIntf codeFirst) { - byte[] input = new byte[] {0, 1, 2}; - byte[] result = codeFirst.testBytes(input); - TestMgr.check(3, result.length); - TestMgr.check(1, result[0]); - TestMgr.check(1, result[1]); - TestMgr.check(2, result[2]); - } - - private void testCodeFirstAddDate(CodeFirstPojoIntf codeFirst) { - Date date = new Date(); - int seconds = 1; - Date result = codeFirst.addDate(date, seconds); - TestMgr.check(new Date(date.getTime() + seconds * 1000), result); - } - - protected void testCodeFirstAddString(CodeFirstPojoIntf codeFirst) { - String result = codeFirst.addString(Arrays.asList("a", "b")); - TestMgr.check("ab", result); - } - - protected void testCodeFirstIsTrue(CodeFirstPojoIntf codeFirst) { - boolean result = codeFirst.isTrue(); - TestMgr.check(true, result); - } - - protected void testCodeFirstSayHi2(CodeFirstPojoIntf codeFirst) { - String result = codeFirst.sayHi2("world"); - TestMgr.check("world sayhi 2", result); - } - - protected void testCodeFirstSayHi(CodeFirstPojoIntf codeFirst) { - String result = codeFirst.sayHi("world"); - TestMgr.check("world sayhi", result); - // TestMgr.check(202, responseEntity.getStatusCode()); - } - - protected void testCodeFirstSaySomething(CodeFirstPojoIntf codeFirst) { - Person person = new Person(); - person.setName("person name"); - - String result = codeFirst.saySomething("prefix prefix", person); - TestMgr.check("prefix prefix person name", result); - } - - protected void testCodeFirstSayHello(CodeFirstPojoIntf codeFirst) { - Person input = new Person(); - input.setName("person name"); - - Person result = codeFirst.sayHello(input); - TestMgr.check("hello person name", result.getName()); - } - - protected void testCodeFirstReduce(CodeFirstPojoIntf codeFirst) { - int result = codeFirst.reduce(5, 3); - TestMgr.check(2, result); - } -} diff --git a/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/PojoClient.java b/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/PojoClient.java deleted file mode 100644 index ba6dc4d3708..00000000000 --- a/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/PojoClient.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.client; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.inject.Inject; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import io.servicecomb.core.CseContext; -import io.servicecomb.core.provider.consumer.InvokerUtils; -import io.servicecomb.demo.DemoConst; -import io.servicecomb.demo.TestMgr; -import io.servicecomb.demo.server.Test; -import io.servicecomb.demo.server.TestRequest; -import io.servicecomb.demo.server.User; -import io.servicecomb.demo.smartcare.Application; -import io.servicecomb.demo.smartcare.Group; -import io.servicecomb.demo.smartcare.SmartCare; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.provider.pojo.RpcReference; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -@Component -public class PojoClient { - private static Logger LOGGER = LoggerFactory.getLogger(PojoClient.class); - - public static CodeFirstPojoClient codeFirstPojoClient; - - // 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") - public static Test test; - - public static Test testFromXml; - - private static SmartCare smartcare; - - public static final byte buffer[] = new byte[1024]; - - static { - Arrays.fill(buffer, (byte) 1); - } - - public static void setTestFromXml(Test testFromXml) { - PojoClient.testFromXml = testFromXml; - } - - @Inject - public void setCodeFirstPojoClient(CodeFirstPojoClient codeFirstPojoClient) { - PojoClient.codeFirstPojoClient = codeFirstPojoClient; - } - - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - - run(); - - TestMgr.summary(); - } - - public static void run() throws Exception { - smartcare = BeanUtils.getBean("smartcare"); - - String microserviceName = "pojo"; - codeFirstPojoClient.testCodeFirst(microserviceName); - - for (String transport : DemoConst.transports) { - CseContext.getInstance().getConsumerProviderManager().setTransport(microserviceName, transport); - TestMgr.setMsg(microserviceName, transport); - LOGGER.info("test {}, transport {}", microserviceName, transport); - - testNull(testFromXml); - testNull(test); - testEmpty(test); - testStringArray(test); - testChinese(test); - testStringHaveSpace(test); - testWrapParam(test); - testSplitParam(test); - testInputArray(test); - - testException(test); - - testSmartCare(smartcare); - - testCommonInvoke(transport); - } - } - - private static void testSmartCare(SmartCare smartCare) { - Group group = new Group(); - group.setName("group0"); - - Application application = new Application(); - application.setName("app0"); - application.setDefaultGroup("group0"); - application.setVersion("v1"); - application.setDynamicFlag(true); - List groups = new ArrayList<>(); - groups.add(group); - application.setGroups(groups); - - TestMgr.check("resultCode: 0\nresultMessage: add application app0 success", - smartCare.addApplication(application)); - TestMgr.check("resultCode: 1\nresultMessage: delete application app0 failed", - smartCare.delApplication("app0")); - } - - 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); - } - - private static void testCommonInvoke(String transport) { - Object result = InvokerUtils.syncInvoke("pojo", "server", "splitParam", new Object[] {2, new User()}); - TestMgr.check("User [name=nameA, users count:0, age=100, index=2]", result); - - result = - InvokerUtils.syncInvoke("pojo", - "0.0.1", - transport, - "server", - "splitParam", - new Object[] {3, new User()}); - TestMgr.check("User [name=nameA, users count:0, age=100, index=3]", result); - } - - private static void testEmpty(Test test) { - TestMgr.check("code is ''", test.getTestString("")); - } - - private static void testNull(Test test) { - TestMgr.check("code is 'null'", test.getTestString(null)); - TestMgr.check(null, test.wrapParam(null)); - } - - 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/BeanRpcTest.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/BeanRpcTest.java new file mode 100644 index 00000000000..caadd95efe7 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/BeanRpcTest.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.demo.pojo.client; + +import org.apache.servicecomb.demo.server.Test; +import org.apache.servicecomb.provider.pojo.RpcReference; + +public class BeanRpcTest { + @RpcReference(microserviceName = "pojo", schemaId = "server") + private Test test; + + public BeanRpcTest() { + System.out.println("init"); + } + + public void init() { + new Thread(() -> { + while (true) { + try { + System.out.println("XXXXXXXXXXXXXXXXXXXXXXXX" + test.getTestString(null)); + break; + } catch (Exception e) { + e.printStackTrace(); + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + continue; + } catch (Throwable e) { + System.out.println("XXXXXXXXXXXXXXXXXXXXXXXX: teset case error"); + e.printStackTrace(); + System.exit(0); + } + } + }).start(); + } +} 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 new file mode 100644 index 00000000000..601901e5884 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClient.java @@ -0,0 +1,325 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import javax.inject.Inject; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.CodeFirstPojoIntf; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.compute.Person; +import org.apache.servicecomb.demo.mapnull.ParseRequest; +import org.apache.servicecomb.demo.mapnull.ParseResponse; +import org.apache.servicecomb.demo.server.MapModel; +import org.apache.servicecomb.demo.server.User; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +import org.springframework.stereotype.Component; + +import io.vertx.core.Vertx; + +@Component +public class CodeFirstPojoClient implements CategorizedTestCase { + @RpcReference(microserviceName = "pojo", schemaId = "org.apache.servicecomb.demo.CodeFirstPojoIntf") + public CodeFirstPojoClientIntf codeFirstAnnotation; + + @RpcReference(microserviceName = "pojo") + public CodeFirstPojoIntf codeFirstAnnotationEmptySchemaId; + + @Inject + private CodeFirstPojoIntf codeFirstFromXml; + + @Override + public void testRestTransport() throws Exception { + testOnlyRest(codeFirstAnnotation); + } + + @Override + public void testAllTransport() throws Exception { + testAll(codeFirstAnnotation); + testAll(codeFirstAnnotationEmptySchemaId); + testAll(codeFirstFromXml); + } + + private void testOnlyRest(CodeFirstPojoIntf codeFirst) { + testCodeFirstStrings(codeFirst); + } + + private void testAll(CodeFirstPojoIntf codeFirst) { + remoteCodeFirstPojo_testParseResponse(codeFirst); + remoteCodeFirstPojo_testMapModel(codeFirst); + remoteCodeFirstPojo_testMap(codeFirst); + testCodeFirstUserMap(codeFirst); + testCodeFirstUserArray(codeFirst); + testCodeFirstStrings(codeFirst); + testCodeFirstBytes(codeFirst); + testCodeFirstAddDate(codeFirst); + testCodeFirstAddString(codeFirst); + testCodeFirstIsTrue(codeFirst); + testCodeFirstSayHi2(codeFirst); + testCodeFirstSayHi(codeFirst); + testCodeFirstSaySomething(codeFirst); + // testCodeFirstRawJsonString(template, cseUrlPrefix); + testCodeFirstSayHello(codeFirst); + testCodeFirstReduce(codeFirst); + testCodeFirstCompletableFuture(codeFirst); + } + + private void testCodeFirstCompletableFuture(CodeFirstPojoIntf codeFirst) { + if (!CodeFirstPojoClientIntf.class.isInstance(codeFirst)) { + return; + } + + 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. + vertx.runOnContext(V -> { + InvocationContext context = new InvocationContext(); + context.addContext("k", "v"); + ContextUtils.setInvocationContext(context); + + ((CodeFirstPojoClientIntf) codeFirst).sayHiAsync("someone") + .thenCompose(result -> { + TestMgr.check("someone sayhi, context k: v", result); + + // new call need set invocation context implicitly or will not + // inherit parent context + ContextUtils.setInvocationContext(context); + + return ((CodeFirstPojoClientIntf) codeFirst).sayHiAsync("someone 1"); + }) + .whenComplete((r, e) -> { + TestMgr.check("someone 1 sayhi, context k: v", r); + latch.countDown(); + }); + + ContextUtils.removeInvocationContext(); + }); + + try { + latch.await(); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + } + + private void remoteCodeFirstPojo_testParseResponse(CodeFirstPojoIntf codeFirst) { + ParseResponse r = codeFirst.parse(new ParseRequest()); + TestMgr.check("", r.getMsgHeader().get("K16")); + TestMgr.check("CMT", r.getMsgHeader().get("K14")); + } + + private void remoteCodeFirstPojo_testMapModel(CodeFirstPojoIntf codeFirst) { + MapModel model = new MapModel(); + model.setName("hello"); + Map userMap = new HashMap<>(); + userMap.put("u1", "u1"); + userMap.put("u2", null); + model.setNames(userMap); + MapModel result = codeFirst.testMapModel(model); + + TestMgr.check(result.getName(), "hello"); + TestMgr.check(result.getNames().get("u1"), "u1"); + TestMgr.check(result.getNames().get("u2"), null); + + model = new MapModel(); + model.setName(null); + userMap = new HashMap<>(); + userMap.put("u1", "u1"); + userMap.put("u2", null); + model.setNames(userMap); + result = codeFirst.testMapModel(model); + + TestMgr.check(result.getName(), null); + TestMgr.check(result.getNames().get("u1"), "u1"); + TestMgr.check(result.getNames().get("u2"), null); + + model = new MapModel(); + model.setName(null); + userMap = new HashMap<>(); + userMap.put("u1", "u1"); + userMap.put("u2", ""); + model.setNames(userMap); + result = codeFirst.testMapModel(model); + + TestMgr.check(result.getName(), null); + TestMgr.check(result.getNames().get("u1"), "u1"); + TestMgr.check(result.getNames().get("u2"), ""); + } + + private void remoteCodeFirstPojo_testMap(CodeFirstPojoIntf codeFirst) { + Map userMap = new HashMap<>(); + userMap.put("u1", "u1"); + userMap.put("u2", null); + Map result = codeFirst.testMap(userMap); + + TestMgr.check(result.get("u1"), "u1"); + TestMgr.check(result.get("u2"), null); + + userMap = new HashMap<>(); + userMap.put("u1", "u1"); + userMap.put("u2", "u2"); + result = codeFirst.testMap(userMap); + + TestMgr.check(result.get("u1"), "u1"); + TestMgr.check(result.get("u2"), "u2"); + + // test large data more than 20M + // can not run the test case in CI , because will cause heap size limit +// char[] data = new char[30 * 1024 * 1024]; +// Arrays.fill(data, 'h'); +// userMap = new HashMap<>(); +// userMap.put("u1", "u1"); +// userMap.put("u2", "u2"); +// userMap.put("u3", new String(data)); +// result = codeFirst.testMap(userMap); +// +// TestMgr.check(result.get("u1"), "u1"); +// TestMgr.check(result.get("u2"), "u2"); +// TestMgr.check(result.get("u3"), new String(data)); + } + + private void testCodeFirstUserMap(CodeFirstPojoIntf codeFirst) { + User user1 = new User(); + user1.setNames(new String[] {"u1", "u2"}); + + User user2 = new User(); + user2.setNames(new String[] {"u3", "u4"}); + + Map userMap = new HashMap<>(); + userMap.put("u1", user1); + userMap.put("u2", user2); + Map result = codeFirst.testUserMap(userMap); + + TestMgr.check("u1", result.get("u1").getNames()[0]); + TestMgr.check("u2", result.get("u1").getNames()[1]); + TestMgr.check("u3", result.get("u2").getNames()[0]); + TestMgr.check("u4", result.get("u2").getNames()[1]); + + userMap = new HashMap<>(); + userMap.put("u1", user1); + userMap.put("u2", null); + result = codeFirst.testUserMap(userMap); + + TestMgr.check(result.get("u1").getNames()[0], "u1"); + TestMgr.check(result.get("u1").getNames()[1], "u2"); + TestMgr.check(result.get("u2"), null); + } + + private void testCodeFirstUserArray(CodeFirstPojoIntf codeFirst) { + User user1 = new User(); + user1.setNames(new String[] {"u1", "u2"}); + + User user2 = new User(); + user2.setNames(new String[] {"u3", "u4"}); + + User[] users = new User[] {user1, user2}; + List result = codeFirst.testUserArray(Arrays.asList(users)); + TestMgr.check("u1", result.get(0).getNames()[0]); + TestMgr.check("u2", result.get(0).getNames()[1]); + TestMgr.check("u3", result.get(1).getNames()[0]); + TestMgr.check("u4", result.get(1).getNames()[1]); + } + + private void testCodeFirstStrings(CodeFirstPojoIntf codeFirst) { + String[] result = codeFirst.testStrings(new String[] {"a", "b"}); + TestMgr.check("aa0", result[0]); + TestMgr.check("b", result[1]); + + result = codeFirst.testStrings(new String[] {"a", ""}); + TestMgr.check("aa0", result[0]); + TestMgr.check("", result[1]); + } + + private void testCodeFirstBytes(CodeFirstPojoIntf codeFirst) { + byte[] input = new byte[] {0, 1, 2}; + byte[] result = codeFirst.testBytes(input); + TestMgr.check(3, result.length); + TestMgr.check(1, result[0]); + TestMgr.check(1, result[1]); + TestMgr.check(2, result[2]); + } + + private void testCodeFirstAddDate(CodeFirstPojoIntf codeFirst) { + Date date = new Date(); + int seconds = 1; + Date result = codeFirst.addDate(date, seconds); + TestMgr.check(new Date(date.getTime() + seconds * 1000), result); + } + + private void testCodeFirstAddString(CodeFirstPojoIntf codeFirst) { + String result = codeFirst.addString(Arrays.asList("a", "b")); + TestMgr.check("ab", result); + } + + private void testCodeFirstIsTrue(CodeFirstPojoIntf codeFirst) { + boolean result = codeFirst.isTrue(); + TestMgr.check(true, result); + } + + private void testCodeFirstSayHi2(CodeFirstPojoIntf codeFirst) { + if (!CodeFirstPojoClientIntf.class.isInstance(codeFirst)) { + return; + } + + String result = ((CodeFirstPojoClientIntf) codeFirst).sayHi2("world"); + TestMgr.check("world sayhi 2", result); + } + + private void testCodeFirstSayHi(CodeFirstPojoIntf codeFirst) { + String result = codeFirst.sayHi("world"); + TestMgr.check("world sayhi, context k: null", result); + // TestMgr.check(202, responseEntity.getStatusCode()); + } + + private void testCodeFirstSaySomething(CodeFirstPojoIntf codeFirst) { + Person person = new Person(); + person.setName("person name"); + + String result = codeFirst.saySomething("prefix prefix", person); + TestMgr.check("prefix prefix person name", result); + } + + private void testCodeFirstSayHello(CodeFirstPojoIntf codeFirst) { + Person input = new Person(); + input.setName("person name"); + + Person result = codeFirst.sayHello(input); + TestMgr.check("hello person name", result.getName()); + + input.setName(""); + + result = codeFirst.sayHello(input); + TestMgr.check("hello ", result.getName()); + } + + private void testCodeFirstReduce(CodeFirstPojoIntf codeFirst) { + int result = codeFirst.reduce(5, 3); + TestMgr.check(2, result); + } +} 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 new file mode 100644 index 00000000000..0f4b6586163 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClientIntf.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.pojo.client; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.demo.CodeFirstPojoIntf; + +import io.swagger.v3.oas.annotations.Operation; + +public interface CodeFirstPojoClientIntf extends CodeFirstPojoIntf { + @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 new file mode 100644 index 00000000000..03fafced0ae --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/PojoClient.java @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.pojo.client; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ForkJoinPool; +import java.util.stream.IntStream; + +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.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.vertx.client.http.HttpClients; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +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 +@ImportResource(value = "classpath*:META-INF/spring/*.bean.xml") +public class PojoClient { + private static final Logger LOGGER = LoggerFactory.getLogger(PojoClient.class); + + // 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", schemaId = "server") + public static Test test; + + public static Test testFromXml; + + private static SmartCare smartcare; + + public static void setTestFromXml(Test testFromXml) { + PojoClient.testFromXml = testFromXml; + } + + public static void main(String[] args) throws Exception { + new SpringApplicationBuilder(PojoClient.class).web(WebApplicationType.NONE).run(args); + + try { + run(); + } 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 { + ForkJoinPool pool = new ForkJoinPool(4); + pool.submit(() -> + IntStream.range(0, 20).parallel().forEach(item -> { + if (Thread.currentThread().getName().equals("main")) { + return; + } + // in web environment, this could be null, here we just mock a null class loader. + Thread.currentThread().setContextClassLoader(null); + TestMgr.check(null, test.postTestStatic(2)); + })).get(); + } + + public static void run() throws Exception { + testHttpClientsIsOk(); + CategorizedTestCaseRunner.runCategorizedTestCase("pojo"); + + smartcare = BeanUtils.getBean("smartcare"); + String microserviceName = "pojo"; + + for (String transport : DemoConst.transports) { + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); + TestMgr.setMsg(microserviceName, transport); + LOGGER.info("test {}, transport {}", microserviceName, transport); + + testContextClassLoaderIsNull(); + testNull(testFromXml); + + boolean checkerDestroyed = true; + // Timer cancel may not destroy thread very fast so check for 3 times. + for (int i = 0; i < 3; i++) { + checkerDestroyed = true; + Set allThreads = Thread.getAllStackTraces().keySet(); + for (Thread t : allThreads) { + if (t.getName().equals("NFLoadBalancer-serverWeightTimer-unknown")) { + checkerDestroyed = false; + Thread.sleep(1000); + } + } + } + TestMgr.check(checkerDestroyed, true); + + testSmartCare(smartcare); + + testCommonInvoke(transport); + + if ("rest".equals(transport)) { + testTraceIdOnNotSetBefore(); + } + testTraceIdOnContextContainsTraceId(); + } + } + + private static void testHttpClientsIsOk() { + 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("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); + } + + /** + * Only in http transport, traceId will be set to invocation if null. + * But in highway, nothing done. + */ + private static void testTraceIdOnNotSetBefore() { + String traceId = test.testTraceId(); + TestMgr.checkNotEmpty(traceId); + } + + private static void testTraceIdOnContextContainsTraceId() { + InvocationContext context = new InvocationContext(); + 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); + ContextUtils.removeInvocationContext(); + } + + private static void testSmartCare(SmartCare smartCare) { + Group group = new Group(); + group.setName("group0"); + + Application application = new Application(); + application.setName("app0"); + application.setDefaultGroup("group0"); + application.setVersion("v1"); + application.setDynamicFlag(true); + List groups = new ArrayList<>(); + groups.add(group); + application.setGroups(groups); + + TestMgr.check("resultCode: 0\nresultMessage: add application app0 success", + smartCare.addApplication(application)); + TestMgr.check("resultCode: 1\nresultMessage: delete application app0 failed", + smartCare.delApplication("app0")); + } + + @SuppressWarnings("rawtypes") + private static void testCommonInvoke(String transport) { + Map arguments = new HashMap<>(); + arguments.put("index", 2); + arguments.put("user", new User()); + 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", + transport, + "server", + "splitParam", + warpArguments, User.class); + TestMgr.check("User [name=nameA, users count:0, age=100, index=3]", result); + } + + 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)); + } +} diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/SchemaInterface.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/SchemaInterface.java new file mode 100644 index 00000000000..39260f3b4a6 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/SchemaInterface.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.demo.pojo.client; + +public interface SchemaInterface { + String echo(String content); + + String echoError(String content); +} diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/SchemeInterfacePojo.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/SchemeInterfacePojo.java new file mode 100644 index 00000000000..f701c9fb1f4 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/SchemeInterfacePojo.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.demo.pojo.client; + +public interface SchemeInterfacePojo { + int reduce(int a, int b); + + int add(int a, int b); +} diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestFlowControl.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestFlowControl.java new file mode 100644 index 00000000000..c076c167297 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestFlowControl.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.pojo.client; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.stereotype.Component; + +@Component +public class TestFlowControl implements CategorizedTestCase { + interface Client { + int foo(int num); + + int bar(int num); + } + + @RpcReference(microserviceName = "pojo", schemaId = "FlowControlSchema") + Client client1; + + @RpcReference(microserviceName = "pojo", schemaId = "FlowControlClientSchema") + Client client2; + + @Override + public void testAllTransport() throws Exception { + testFlowControl((num) -> client1.foo(num), "provider", true); + testFlowControl((num) -> client1.bar(num), "provider", false); + testFlowControl((num) -> client2.foo(num), "consumer", true); + testFlowControl((num) -> client2.bar(num), "consumer", false); + } + + private void testFlowControl(Function function, String role, boolean expected) + throws InterruptedException { + AtomicBoolean failed = new AtomicBoolean(false); + CountDownLatch countDownLatch = new CountDownLatch(10); + for (int i = 0; i < 10; i++) { + new Thread(() -> { + for (int i1 = 0; i1 < 10; i1++) { + try { + int result = function.apply(10); + if (result != 10) { + TestMgr.failed("", new Exception("not expected")); + } + } catch (InvocationException e) { + TestMgr.check(e.getStatusCode(), 429); + TestMgr.check(((CommonExceptionData) e.getErrorData()).getMessage(), + role + " request rejected by flow control."); + failed.set(true); + break; + } catch (Throwable e) { + e.printStackTrace(); + TestMgr.fail("not expected error " + e.getMessage()); + } + } + countDownLatch.countDown(); + }).start(); + } + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check(expected, failed.get()); + } +} diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestNotRecommendedService.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestNotRecommendedService.java new file mode 100644 index 00000000000..59d9a8f92b3 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestNotRecommendedService.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.server.AbstractModel; +import org.apache.servicecomb.demo.server.DefaultAbstractModel; +import org.apache.servicecomb.demo.server.NotRecommendedServiceInf; +import org.apache.servicecomb.demo.server.SecondAbstractModel; +import org.apache.servicecomb.demo.server.WrappedAbstractModel; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component +public class TestNotRecommendedService implements CategorizedTestCase { + @RpcReference(microserviceName = "pojo", schemaId = "NotRecommendedService") + private NotRecommendedServiceInf pojo; + + @Override + public void testRestTransport() throws Exception { + testLongMap(); + testAbstractModel(); + testListAbstractModel(); + testMapAbstractModel(); + testWrappedModel(); + } + + private void testWrappedModel() { + Map data = new HashMap<>(); + AbstractModel model = new DefaultAbstractModel(); + model.setName("hello"); + data.put(100L, model); + + List data2 = new ArrayList<>(); + + AbstractModel model2 = new DefaultAbstractModel(); + model2.setName("hello"); + data2.add(model2); + + WrappedAbstractModel input = new WrappedAbstractModel(); + input.setMapModel(data); + input.setListModel(data2); + AbstractModel secondModel = new SecondAbstractModel(); + secondModel.setName("second"); + input.setModel(secondModel); + input.setName("wrapped"); + + WrappedAbstractModel result = pojo.wrappedAbstractModel(input); + + TestMgr.check(1, result.getMapModel().size()); + TestMgr.check("hello", result.getMapModel().get(result.getMapModel().keySet().iterator().next()).getName()); + + TestMgr.check(1, result.getListModel().size()); + TestMgr.check("hello", result.getListModel().get(0).getName()); + + TestMgr.check("second", result.getModel().getName()); + TestMgr.check(true, result.getModel() instanceof SecondAbstractModel); + TestMgr.check("wrapped", result.getName()); + } + + private void testMapAbstractModel() { + Map data = new HashMap<>(); + AbstractModel model = new DefaultAbstractModel(); + model.setName("hello"); + data.put(100L, model); + Map result = pojo.mapAbstractModel(data); + TestMgr.check(1, result.size()); + TestMgr.check("hello", result.get(result.keySet().iterator().next()).getName()); + } + + private void testListAbstractModel() { + List data = new ArrayList<>(); + AbstractModel model = new DefaultAbstractModel(); + model.setName("hello"); + data.add(model); + List result = pojo.listAbstractModel(data); + TestMgr.check(1, result.size()); + TestMgr.check("hello", result.get(0).getName()); + } + + private void testAbstractModel() { + AbstractModel model = new DefaultAbstractModel(); + model.setName("hello"); + + AbstractModel result = pojo.abstractModel(model); + TestMgr.check("hello", result.getName()); + } + + private void testLongMap() { + Map data = new HashMap<>(); + data.put(100L, 200L); + Map result = pojo.longMap(data); + TestMgr.check(1, result.size()); + TestMgr.check(200L, result.get(100L)); + } +} diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestRpcReferenceMethodInjection.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestRpcReferenceMethodInjection.java new file mode 100644 index 00000000000..92308e6b6cd --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestRpcReferenceMethodInjection.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.demo.pojo.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 TestRpcReferenceMethodInjection implements CategorizedTestCase { + + private SchemeInterfacePojo pojo; + + private SchemeInterfacePojo pojoValue; + + @RpcReference(microserviceName = "pojo", schemaId = "SchemeInterfacePojoImpl") + public void setSchemeInterfacePojo(SchemeInterfacePojo pojo) { + this.pojo = pojo; + } + + @RpcReference(microserviceName = "${servicecomb.provider.name}", schemaId = "SchemeInterfacePojoImpl") + public void setSchemeInterfacePojoValue(SchemeInterfacePojo pojoValue) { + this.pojoValue = pojoValue; + } + + @Override + public void testAllTransport() throws Exception { + TestMgr.check(-1, pojo.reduce(1, 2)); + TestMgr.check(-1, pojoValue.reduce(1, 2)); + } +} 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 new file mode 100644 index 00000000000..0f735af07c4 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSameService.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.pojo.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; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.servers.Server; + +@Component +public class TestSameService implements CategorizedTestCase { + @OpenAPIDefinition(servers = {@Server(url = "/SameInterface1")}) + public interface SameInterface1 { + String sayHello(String name); + } + + @OpenAPIDefinition(servers = {@Server(url = "/SameInterface2")}) + public interface SameInterface2 { + String sayHello(String name); + } + + @OpenAPIDefinition(servers = {@Server(url = "/SameService1")}) + public interface SameService1 { + String sayHello(String name); + } + + @OpenAPIDefinition(servers = {@Server(url = "/SameService2")}) + public interface SameService2 { + String sayHello(String name); + } + + @RpcReference(microserviceName = "pojo", schemaId = "SameService1") + SameService1 sameService1; + + @RpcReference(microserviceName = "pojo", schemaId = "SameService2") + SameService2 sameService2; + + @RpcReference(microserviceName = "pojo", schemaId = "SameInterface1") + SameInterface1 sameInterface1; + + @RpcReference(microserviceName = "pojo", schemaId = "SameInterface2") + SameInterface2 sameInterface2; + + @Override + public void testAllTransport() throws Exception { + TestMgr.check("pk1-svc-1", sameService1.sayHello("1")); + TestMgr.check("pk2-svc-1", sameService2.sayHello("1")); + TestMgr.check("pk1-inf-1", sameInterface1.sayHello("1")); + TestMgr.check("pk2-inf-1", sameInterface2.sayHello("1")); + } +} diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSchemeInterface.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSchemeInterface.java new file mode 100644 index 00000000000..7866c3add94 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSchemeInterface.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.pojo.client; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.stereotype.Component; + +@Component +public class TestSchemeInterface implements CategorizedTestCase { + @RpcReference(microserviceName = "pojo", schemaId = "SchemaInterface") + private SchemaInterface pojo; + + @Override + public void testAllTransport() throws Exception { + TestMgr.check("hello", pojo.echo("hello")); + + try { + pojo.echoError("hello"); + TestMgr.failed("should throw exception", new Exception()); + } catch (InvocationException e) { + TestMgr.check( + "Consumer method org.apache.servicecomb.demo.pojo.client.SchemaInterface:" + + "echoError not exist in contract, microserviceName=pojo, schemaId=SchemaInterface.", + ((CommonExceptionData) e.getErrorData()).getMessage()); + } + } +} diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSchemeInterfacePojo.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSchemeInterfacePojo.java new file mode 100644 index 00000000000..18eb0db47db --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestSchemeInterfacePojo.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.demo.pojo.client; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.stereotype.Component; + +@Component +public class TestSchemeInterfacePojo implements CategorizedTestCase { + @RpcReference(microserviceName = "pojo", schemaId = "SchemeInterfacePojoImpl") + private SchemeInterfacePojo pojo; + + @Override + public void testAllTransport() throws Exception { + TestMgr.check(-1, pojo.reduce(1, 2)); + + try { + pojo.add(1, 2); + TestMgr.failed("should throw exception", new Exception()); + } catch (InvocationException e) { + TestMgr.check( + "Consumer method org.apache.servicecomb.demo.pojo.client.SchemeInterfacePojo:" + + "add not exist in contract, microserviceName=pojo, " + + "schemaId=SchemeInterfacePojoImpl.", + ((CommonExceptionData) e.getErrorData()).getMessage()); + } + } +} 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 new file mode 100644 index 00000000000..eefc0fd1137 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestWeakPojo.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.core.provider.consumer.InvokerUtils; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.server.GenericsModel; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.JsonProcessingException; + +/* +This is the provider definition: + + + @GetMapping(path = "/diffNames") + @ApiOperation(value = "differentName", nickname = "differentName") + public int diffNames(@RequestParam("x") int a, @RequestParam("y") int b) + + +and the swagger is: + + + paths: + /diffNames: + get: + summary: "differentName" + operationId: "differentName" + parameters: + - name: "x" + in: "query" + required: true + type: "integer" + format: "int32" + - name: "y" + in: "query" + required: true + type: "integer" + format: "int32" + responses: + "200": + description: "response of 200" + schema: + type: "integer" + format: "int32" + + +In consumer, you can define any prototype that matches generated swagger of provider. + */ + +interface DiffNames { + int differentName(int x, int y); +} + +interface DiffNames2 { + int differentName(int y, int x); +} + +interface Generics { + List> genericParams(int code, List> names); +} + +interface GenericsModelInf { + GenericsModel genericParamsModel(int code, GenericsModel model); +} + +interface ObjectInf { + GenericsModel obj(GenericsModel obj); +} + +@Component +public class TestWeakPojo implements CategorizedTestCase { + @RpcReference(microserviceName = "pojo", schemaId = "WeakPojo") + private DiffNames diffNames; + + @RpcReference(microserviceName = "pojo", schemaId = "WeakPojo") + private DiffNames2 diffNames2; + + @RpcReference(microserviceName = "pojo", schemaId = "WeakPojo") + private Generics generics; + + @RpcReference(microserviceName = "pojo", schemaId = "WeakPojo") + private GenericsModelInf genericsModelInf; + + @RpcReference(microserviceName = "pojo", schemaId = "WeakPojo") + private ObjectInf objectInf; + + @Override + public void testRestTransport() throws Exception { + + } + + @Override + public void testHighwayTransport() throws Exception { + + } + + @Override + public void testAllTransport() throws Exception { + testDiffName(); + + testGenerics(); + + testGenericsModel(); + + testObject(); + } + + private void testObject() throws JsonProcessingException { + GenericsModel model = new GenericsModel(); + model.setName("model"); + List> namesList = new ArrayList<>(); + List names = new ArrayList<>(); + names.add("hello"); + namesList.add(names); + model.setNameList(namesList); + List>> objectLists = new ArrayList<>(); + List> objectList = new ArrayList<>(); + List objects = new ArrayList<>(); + objects.add("object"); + objectList.add(objects); + objectLists.add(objectList); + model.setObjectLists(objectLists); + GenericsModel result = objectInf.obj(model); + TestMgr.check(JsonUtils.writeValueAsString(model), JsonUtils.writeValueAsString(result)); + } + + private void testGenericsModel() throws JsonProcessingException { + GenericsModel model = new GenericsModel(); + model.setName("model"); + List> namesList = new ArrayList<>(); + List names = new ArrayList<>(); + names.add("hello"); + namesList.add(names); + model.setNameList(namesList); + List>> objectLists = new ArrayList<>(); + List> objectList = new ArrayList<>(); + List objects = new ArrayList<>(); + objects.add("object"); + objectList.add(objects); + objectLists.add(objectList); + model.setObjectLists(objectLists); + GenericsModel result = genericsModelInf.genericParamsModel(100, model); + TestMgr.check(JsonUtils.writeValueAsString(model), JsonUtils.writeValueAsString(result)); + } + + private void testGenerics() { + List> namesList = new ArrayList<>(); + List names = new ArrayList<>(); + names.add("hello"); + namesList.add(names); + List> nameListResult = generics.genericParams(100, namesList); + TestMgr.check(1, nameListResult.size()); + TestMgr.check(1, nameListResult.get(0).size()); + TestMgr.check("hello", nameListResult.get(0).get(0)); + } + + private void testDiffName() { + TestMgr.check(7, diffNames.differentName(2, 3)); + TestMgr.check(8, diffNames2.differentName(2, 3)); + Map args = new HashMap<>(); + args.put("x", 2); + args.put("y", 3); + Map swaggerArgs = new HashMap<>(); + swaggerArgs.put("differentNameBody", args); + 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/ClientModel.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/invoker/ClientModel.java new file mode 100644 index 00000000000..fd699064d46 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/invoker/ClientModel.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.demo.pojo.client.invoker; + +public class ClientModel { + private String name; + private int code; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } +} 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 new file mode 100644 index 00000000000..025e67a0b49 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/invoker/TestInvokerEndpoint.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.invoker; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import org.apache.servicecomb.core.provider.consumer.InvokerUtils; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.stereotype.Component; + +@Component +@SuppressWarnings({"unchecked", "rawtypes"}) +public class TestInvokerEndpoint implements CategorizedTestCase { + @Override + public void testRestTransport() throws Exception { + testInvokerUtilsDiffModelRest(); + } + + @Override + public void testHighwayTransport() throws Exception { + testInvokerUtilsDiffModelHighway(); + } + + @Override + public void testAllTransport() throws Exception { + testInvokerUtilsDiffModel(); + testInvokerUtilsDiffModelMapArgs(); + } + + private void testInvokerUtilsDiffModelHighway() throws Exception { + Map args = new HashMap<>(); + ClientModel model = new ClientModel(); + model.setCode(200); + model.setName("hello"); + args.put("request", model); + + 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", "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", "highway", "InvokerEndpoint", "model", args, ClientModel.class, response -> { + ClientModel reactiveResult = response.getResult(); + TestMgr.check(model.getCode(), reactiveResult.getCode()); + TestMgr.check(model.getName(), reactiveResult.getName()); + System.out.println("done"); + countDownLatch.countDown(); + }); + countDownLatch.await(); + } + + private void testInvokerUtilsDiffModelRest() throws Exception { + Map args = new HashMap<>(); + ClientModel model = new ClientModel(); + model.setCode(200); + model.setName("hello"); + args.put("request", model); + + 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", "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", "rest", "InvokerEndpoint", "model", args, ClientModel.class, response -> { + ClientModel reactiveResult = response.getResult(); + TestMgr.check(model.getCode(), reactiveResult.getCode()); + TestMgr.check(model.getName(), reactiveResult.getName()); + System.out.println("done"); + countDownLatch.countDown(); + }); + countDownLatch.await(); + } + + private void testInvokerUtilsDiffModel() throws Exception { + Map args = new HashMap<>(); + ClientModel model = new ClientModel(); + model.setCode(200); + model.setName("hello"); + args.put("request", model); + + Map result = InvokerUtils.syncInvoke("pojo", "InvokerEndpoint", + "model", args, Map.class); + TestMgr.check(model.getCode(), result.get("code")); + TestMgr.check(model.getName(), result.get("name")); + + ClientModel modelResult = InvokerUtils.syncInvoke("pojo", "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", "InvokerEndpoint", "model", args, + ClientModel.class, response -> { + ClientModel reactiveResult = response.getResult(); + TestMgr.check(model.getCode(), reactiveResult.getCode()); + TestMgr.check(model.getName(), reactiveResult.getName()); + System.out.println("done"); + countDownLatch.countDown(); + }); + countDownLatch.await(); + } + + private void testInvokerUtilsDiffModelMapArgs() throws Exception { + Map args = new HashMap<>(); + Map model = new HashMap(); + model.put("code", 20); + model.put("name", "hello"); + args.put("request", model); + + 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")); + + ClientModel modelResult = InvokerUtils.syncInvoke("pojo", "InvokerEndpoint", "model", args, ClientModel.class); + TestMgr.check(model.get("code"), modelResult.getCode()); + TestMgr.check(model.get("name"), modelResult.getName()); + + CountDownLatch countDownLatch = new CountDownLatch(1); + InvokerUtils.reactiveInvoke("pojo", "InvokerEndpoint", "model", args, ClientModel.class, response -> { + ClientModel reactiveResult = response.getResult(); + TestMgr.check(model.get("code"), reactiveResult.getCode()); + TestMgr.check(model.get("name"), reactiveResult.getName()); + countDownLatch.countDown(); + }); + countDownLatch.await(); + } +} diff --git a/demo/demo-pojo/pojo-client/src/main/resources/META-INF/spring/pojo.client.bean.xml b/demo/demo-pojo/pojo-client/src/main/resources/META-INF/spring/pojo.client.bean.xml index a18fc693880..bba1822fd9b 100644 --- a/demo/demo-pojo/pojo-client/src/main/resources/META-INF/spring/pojo.client.bean.xml +++ b/demo/demo-pojo/pojo-client/src/main/resources/META-INF/spring/pojo.client.bean.xml @@ -1,12 +1,13 @@ - + + + + + + + - - - - - - - - - - + - - - \ No newline at end of file + + + diff --git a/demo/demo-pojo/pojo-client/src/main/resources/log4j2.xml b/demo/demo-pojo/pojo-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 c7e983b2193..45c62dffcf1 100644 --- a/demo/demo-pojo/pojo-client/src/main/resources/microservice.yaml +++ b/demo/demo-pojo/pojo-client/src/main/resources/microservice.yaml @@ -1,15 +1,49 @@ -APPLICATION_ID: pojotest -service_description: - name: pojoClient - version: 0.0.1 -cse: +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: - registry: - address: http://127.0.0.1:9980 - handler: - chain: - Consumer: - default: bizkeeper-consumer,loadbalance + application: pojotest + name: pojoClient + version: 0.0.1 + registry: + sc: + address: http://127.0.0.1:30100 + rest.client.enabled: false # using only http2 + swagger: + disableDataTypeCheck: true + isolation: Consumer: - enabled: false \ No newline at end of file + enabled: false + loadbalance: + strategy: + name: Random + metrics: + window_time: 12000 + publisher.defaultLog.enabled: false # when in testing , can turn on + flowcontrol: + enabled: true + Consumer: + qps: + limit: + pojo: + FlowControlClientSchema: + foo: 3 + bar: 3000 + provider: + name: pojo diff --git a/demo/demo-pojo/pojo-client/src/test/java/io/servicecomb/demo/pojo/PojoIT.java b/demo/demo-pojo/pojo-client/src/test/java/io/servicecomb/demo/pojo/PojoIT.java deleted file mode 100644 index a990d976e73..00000000000 --- a/demo/demo-pojo/pojo-client/src/test/java/io/servicecomb/demo/pojo/PojoIT.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.servicecomb.demo.pojo; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -import org.junit.Before; -import org.junit.Test; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.demo.pojo.client.PojoClient; - -public class PojoIT { - - @Before - public void setUp() throws Exception { - TestMgr.errors().clear(); - } - - @Test - public void clientGetsNoError() throws Exception { - PojoClient.main(new String[0]); - - assertThat(TestMgr.errors().isEmpty(), is(true)); - } -} diff --git a/demo/demo-pojo/pojo-client/src/test/java/org/apache/servicecomb/demo/pojo/PojoIT.java b/demo/demo-pojo/pojo-client/src/test/java/org/apache/servicecomb/demo/pojo/PojoIT.java new file mode 100644 index 00000000000..c0c4f7542c3 --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/test/java/org/apache/servicecomb/demo/pojo/PojoIT.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.pojo; + +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.pojo.client.PojoClient; +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 = PojoClient.class) +public class PojoIT { + + @BeforeEach + public void setUp() throws Exception { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + PojoClient.run(); + + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} diff --git a/demo/demo-pojo/pojo-server/pom.xml b/demo/demo-pojo/pojo-server/pom.xml index 293666b1c0f..b0b673ef47e 100644 --- a/demo/demo-pojo/pojo-server/pom.xml +++ b/demo/demo-pojo/pojo-server/pom.xml @@ -1,12 +1,13 @@ - - 4.0.0 - - io.servicecomb.demo - demo-pojo - 0.3.0-SNAPSHOT - - pojo-server + + 4.0.0 + + org.apache.servicecomb.demo + demo-pojo + 3.4.0-SNAPSHOT + + pojo-server + Java Chassis::Demo::POJO::Server + + + + org.apache.servicecomb.demo + demo-schema + + + + + org.apache.servicecomb.demo.pojo.server.PojoServer + true + - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-pojo - - + + + + 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.servicecomb.demo.pojo.server.PojoServer - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-build-config - 0.3.0-SNAPSHOT - - - - - - - - - docker - - - - io.fabric8 - docker-maven-plugin - - - org.commonjava.maven.plugins - directory-maven-plugin - - - - - + + + io.fabric8 + docker-maven-plugin + + + + + diff --git a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/CodeFirstPojo.java b/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/CodeFirstPojo.java deleted file mode 100644 index 7019b391e47..00000000000 --- a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/CodeFirstPojo.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.server; - -import java.util.Date; -import java.util.List; -import java.util.Map; - -import io.servicecomb.demo.CodeFirstPojoIntf; -import io.servicecomb.demo.compute.Person; -import io.servicecomb.demo.server.User; -import io.servicecomb.provider.pojo.RpcSchema; -import io.servicecomb.swagger.invocation.context.ContextUtils; -import io.swagger.annotations.SwaggerDefinition; - -@RpcSchema() -@SwaggerDefinition(basePath = "/pojo/rest") -public class CodeFirstPojo implements CodeFirstPojoIntf { - @Override - public Map testUserMap(Map userMap) { - return userMap; - } - - @Override - public List testUserArray(List users) { - return users; - } - - public String[] testStrings(String[] input) { - input[0] += input[0] + "0"; - return input; - } - - public byte[] testBytes(byte[] input) { - input[0] = (byte) (input[0] + 1); - return input; - } - - public int reduce(int a, int b) { - return a - b; - } - - public Date addDate(Date date, long second) { - return new Date(date.getTime() + second * 1000); - } - - public Person sayHello(Person user) { - user.setName("hello " + user.getName()); - return user; - } - - // @SuppressWarnings("unchecked") - // public String testRawJsonString(String jsonInput) { - // Map person; - // try { - // person = RestObjectMapper.INSTANCE.readValue(jsonInput.getBytes(), Map.class); - // } catch (Exception e) { - // e.printStackTrace(); - // return null; - // } - // return "hello " + person.get("name"); - // } - - public String saySomething(String prefix, Person user) { - return prefix + " " + user.getName(); - } - - public String sayHi(String name) { - ContextUtils.getInvocationContext().setStatus(202); - return name + " sayhi"; - } - - public String sayHi2(String name) { - return name + " sayhi 2"; - } - - public boolean isTrue() { - return true; - } - - public String addString(List s) { - String result = ""; - for (String x : s) { - result += x; - } - return result; - } -} diff --git a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/HelloImpl.java b/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/HelloImpl.java deleted file mode 100644 index 20f044a4455..00000000000 --- a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/HelloImpl.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.server; - -import io.servicecomb.demo.helloworld.greeter.Hello; - -public class HelloImpl implements Hello { - - @Override - public String SayHello(String name) { - return "Hello Message fast"; - } - - @Override - public String SayHelloAgain(String name) { - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return "Hello Message slow"; - } -} diff --git a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/PojoServer.java b/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/PojoServer.java deleted file mode 100644 index f9335e2eadb..00000000000 --- a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/PojoServer.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.server; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class PojoServer { - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/SmartCareImpl.java b/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/SmartCareImpl.java deleted file mode 100644 index 5e117ce5193..00000000000 --- a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/SmartCareImpl.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.server; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.demo.smartcare.Application; -import io.servicecomb.demo.smartcare.Response; -import io.servicecomb.demo.smartcare.SmartCare; - -public class SmartCareImpl implements SmartCare { - private static final Logger LOG = LoggerFactory.getLogger(SmartCareImpl.class); - - @Override - public Response addApplication(Application application) { - // TODO: add application - LOG.info(application.toString()); - - Response resp = new Response(); - resp.setResultCode(0); - resp.setResultMessage("add application " + application.getName() + " success"); - return resp; - } - - @Override - public Response delApplication(String appName) { - // TODO: delete application - LOG.info(appName); - - try { - System.out.println(5 / 0); - } catch (Exception e) { - Response resp = new Response(); - resp.setResultCode(1); - resp.setResultMessage("delete application " + appName + " failed"); - return resp; - } - - return null; - } -} diff --git a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/TestImpl.java b/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/TestImpl.java deleted file mode 100644 index 88d57ed6307..00000000000 --- a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/TestImpl.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.server; - -import java.util.Arrays; -import java.util.List; - -import io.servicecomb.demo.server.Test; -import io.servicecomb.demo.server.TestRequest; -import io.servicecomb.demo.server.User; -import io.servicecomb.provider.pojo.RpcSchema; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -@RpcSchema(schemaId = "server") -public class TestImpl implements Test { - @Override - public String testStringArray(String[] arr) { - return String.format("arr is '%s'", Arrays.toString(arr)); - } - - @Override - public String getTestString(String code) { - return String.format("code is '%s'", String.valueOf(code)); - } - - @Override - public String postTestStatic(int code) { - return null; - } - - private User doTest(int index, User user, List users, byte[] data) { - if (user == null) { - user = new User(); - } - - user.setIndex(index); - - int userCount = (users == null) ? 0 : users.size(); - user.setName(user.getName() + ", users count:" + userCount); - return user; - } - - @Override - public String testException(int code) { - String strCode = String.valueOf(code); - switch (code) { - case 200: - return strCode; - case 456: - throw new InvocationException(code, strCode, strCode + " error"); - case 556: - throw new InvocationException(code, strCode, Arrays.asList(strCode + " error")); - case 557: - throw new InvocationException(code, strCode, Arrays.asList(Arrays.asList(strCode + " error"))); - default: - break; - } - - return "not expected"; - } - - @Override - public User splitParam(int index, User user) { - return doTest(index, user, null, null); - } - - @Override - public User wrapParam(TestRequest request) { - if (request == null) { - return null; - } - return doTest(request.getIndex(), request.getUser(), request.getUsers(), request.getData()); - } - - @Override - public String addString(String[] strArr) { - String result = Arrays.toString(strArr); - System.out.println("addString: " + result); - return result; - } -} 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 new file mode 100644 index 00000000000..680d7981d05 --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/CodeFirstPojo.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.server; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.demo.CodeFirstPojoIntf; +import org.apache.servicecomb.demo.compute.Person; +import org.apache.servicecomb.demo.mapnull.ParseRequest; +import org.apache.servicecomb.demo.mapnull.ParseResponse; +import org.apache.servicecomb.demo.server.MapModel; +import org.apache.servicecomb.demo.server.User; +import org.apache.servicecomb.provider.pojo.RpcSchema; +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.servers.Server; + +@RpcSchema() +@OpenAPIDefinition(servers = {@Server(url = "/pojo/rest")}) +public class CodeFirstPojo implements CodeFirstPojoIntf { + @Override + public ParseResponse parse(ParseRequest request) { + ParseResponse r = new ParseResponse(); + r.setResultCode("CU1I0000"); + r.setResultInfo("报文处理成功!报文解析成功!"); + r.setMsgType("cncc.111.001.01"); + Map h = new HashMap<>(); + h.put("C11", "20200101"); + h.put("F41", "3"); + h.put("F40", "cncc.111.001.01"); + h.put("E24", "HVPA5C30177808463743"); + h.put("E35", "HVPA5C30177808463743"); + h.put("A29", "{N:, K20=}"); + h.put("F38", "HVPS"); + h.put("K13", "01"); + h.put("F39", "SAPS"); + h.put("K14", "CMT"); + h.put("K16", ""); + h.put("C92", "102633"); + r.setMsgHeader(h); + Map b = new HashMap<>(); + b.put("E50", "hvps.141.001.01"); + b.put("A00", "402451000010"); + b.put("A01", "105100000017"); + b.put("F32", "NORM"); + b.put("E57", "19218385"); + b.put("D14", "200000.00"); + b.put("C14", "20200101"); + b.put("F25", "02711"); + b.put("A70", "908100000002"); + b.put("C92", "20200101"); + b.put("F2H", "G105"); + r.setMsgBody(b); + return r; + } + + @Override + public MapModel testMapModel(MapModel model) { + return model; + } + + @Override + public Map testMap(Map map) { + return map; + } + + @Override + public Map testUserMap(Map userMap) { + return userMap; + } + + @Override + public List testUserArray(List users) { + return users; + } + + public String[] testStrings(String[] input) { + input[0] += input[0] + "0"; + return input; + } + + public byte[] testBytes(byte[] input) { + input[0] = (byte) (input[0] + 1); + return input; + } + + public int reduce(int a, int b) { + return a - b; + } + + public Date addDate(Date date, long second) { + return new Date(date.getTime() + second * 1000); + } + + public Person sayHello(Person user) { + user.setName("hello " + user.getName()); + return user; + } + + public String saySomething(String prefix, Person user) { + return prefix + " " + user.getName(); + } + + public String sayHi(String name) { + ContextUtils.getInvocationContext().setStatus(202); + return name + " sayhi, context k: " + + (ContextUtils.getInvocationContext() == null ? "" + : ContextUtils.getInvocationContext().getContext("k")); + } + + @Operation(operationId = "sayHi2", summary = "") + public CompletableFuture sayHi2Async(String name) { + CompletableFuture future = new CompletableFuture<>(); + future.complete(name + " sayhi 2"); + return future; + } + + public boolean isTrue() { + return true; + } + + public String addString(List s) { + StringBuilder result = new StringBuilder(); + for (String x : s) { + result.append(x); + } + return result.toString(); + } +} diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/FlowControlClientSchema.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/FlowControlClientSchema.java new file mode 100644 index 00000000000..d94bcba140b --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/FlowControlClientSchema.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.pojo.server; + +import org.apache.servicecomb.provider.pojo.RpcSchema; + +@RpcSchema(schemaId = "FlowControlClientSchema") +public class FlowControlClientSchema { + public int foo(int num) { + return num; + } + + public int bar(int num) { + return num; + } +} diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/FlowControlSchema.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/FlowControlSchema.java new file mode 100644 index 00000000000..ba2c29338c7 --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/FlowControlSchema.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.pojo.server; + +import org.apache.servicecomb.provider.pojo.RpcSchema; + +@RpcSchema(schemaId = "FlowControlSchema") +public class FlowControlSchema { + public int foo(int num) { + return num; + } + + public int bar(int num) { + return num; + } +} diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/HelloImpl.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/HelloImpl.java new file mode 100644 index 00000000000..07f3ceb3c8c --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/HelloImpl.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.demo.pojo.server; + +import org.apache.servicecomb.demo.helloworld.greeter.Hello; + +public class HelloImpl implements Hello { + + @Override + public String SayHello(String name) { + return "Hello Message fast"; + } + + @Override + public String SayHelloAgain(String name) { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return "Hello Message slow"; + } +} diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/NotRecommendedService.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/NotRecommendedService.java new file mode 100644 index 00000000000..cd2e3deede1 --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/NotRecommendedService.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.pojo.server; + +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.demo.server.AbstractModel; +import org.apache.servicecomb.demo.server.NotRecommendedServiceInf; +import org.apache.servicecomb.demo.server.WrappedAbstractModel; +import org.apache.servicecomb.provider.pojo.RpcSchema; + +@RpcSchema(schemaId = "NotRecommendedService", schemaInterface = NotRecommendedServiceInf.class) +public class NotRecommendedService implements NotRecommendedServiceInf { + + @Override + public Map longMap(Map map) { + return map; + } + + @Override + public List listAbstractModel(List listModel) { + return listModel; + } + + @Override + public AbstractModel abstractModel(AbstractModel model) { + return model; + } + + @Override + public Map mapAbstractModel(Map mapModel) { + return mapModel; + } + + @Override + public WrappedAbstractModel wrappedAbstractModel(WrappedAbstractModel wrappedModel) { + return wrappedModel; + } +} diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/PojoProducersCustomized.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/PojoProducersCustomized.java new file mode 100644 index 00000000000..2e8b05fb7e9 --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/PojoProducersCustomized.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.pojo.server; + +import org.apache.servicecomb.provider.pojo.schema.PojoProducers; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +@Primary +@Component +// This example shows user's how to customize PojoProducers by @Primary annotation +public class PojoProducersCustomized extends PojoProducers { +} 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 new file mode 100644 index 00000000000..1ae04e578ee --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/PojoServer.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.pojo.server; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.annotation.ImportResource; + +@SpringBootApplication +@ImportResource(value = "classpath*:META-INF/spring/*.bean.xml") +public class PojoServer { + public static void main(String[] args) throws Exception { + 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/SchemaInterface.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemaInterface.java new file mode 100644 index 00000000000..cee5bd44b66 --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemaInterface.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.pojo.server; + +public interface SchemaInterface { + String echo(String content); +} diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemaInterfaceImpl.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemaInterfaceImpl.java new file mode 100644 index 00000000000..6e8645d460e --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemaInterfaceImpl.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.demo.pojo.server; + +public class SchemaInterfaceImpl implements SchemaInterface { + @Override + public String echo(String content) { + return content; + } + + public String echoError(String content) { + return content; + } +} diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemeInterfacePojo.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemeInterfacePojo.java new file mode 100644 index 00000000000..7a503944798 --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemeInterfacePojo.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.pojo.server; + +public interface SchemeInterfacePojo { + int reduce(int a, int b); +} diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemeInterfacePojoImpl.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemeInterfacePojoImpl.java new file mode 100644 index 00000000000..0e544409a5f --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SchemeInterfacePojoImpl.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.demo.pojo.server; + +import org.apache.servicecomb.provider.pojo.RpcSchema; + +@RpcSchema(schemaId = "SchemeInterfacePojoImpl", schemaInterface = SchemeInterfacePojo.class) +public class SchemeInterfacePojoImpl implements SchemeInterfacePojo { + @Override + public int reduce(int a, int b) { + return a - b; + } + + public int add(int a, int b) { + return a + b; + } +} diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SmartCareImpl.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SmartCareImpl.java new file mode 100644 index 00000000000..7b34256a923 --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/SmartCareImpl.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.pojo.server; + +import org.apache.servicecomb.demo.smartcare.Application; +import org.apache.servicecomb.demo.smartcare.Response; +import org.apache.servicecomb.demo.smartcare.SmartCare; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SmartCareImpl implements SmartCare { + private static final Logger LOG = LoggerFactory.getLogger(SmartCareImpl.class); + + @Override + public Response addApplication(Application application) { + LOG.info(application.toString()); + + Response resp = new Response(); + resp.setResultCode(0); + resp.setResultMessage("add application " + application.getName() + " success"); + return resp; + } + + @SuppressWarnings("divzero") + @Override + public Response delApplication(String appName) { + LOG.info(appName); + + try { + System.out.println(5 / 0); + } catch (Exception e) { + Response resp = new Response(); + resp.setResultCode(1); + resp.setResultMessage("delete application " + appName + " failed"); + return resp; + } + + return null; + } +} 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 new file mode 100644 index 00000000000..230dfd804fe --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/TestImpl.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.demo.pojo.server; + +import java.util.Arrays; +import java.util.List; + +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; +import org.apache.servicecomb.provider.pojo.RpcSchema; +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +@RpcSchema(schemaId = "server") +public class TestImpl implements Test { + @Override + public String testStringArray(String[] arr) { + return String.format("arr is '%s'", Arrays.toString(arr)); + } + + @Override + public String getTestString(String code) { + return String.format("code is '%s'", code); + } + + @Override + public String postTestStatic(int code) { + return null; + } + + @Override + public String patchTestStatic(int code) { + return null; + } + + private User doTest(int index, User user, List users, byte[] data) { + if (user == null) { + user = new User(); + } + + user.setIndex(index); + + int userCount = (users == null) ? 0 : users.size(); + user.setName(user.getName() + ", users count:" + userCount); + return user; + } + + @Override + public String testException(int code) { + String strCode = String.valueOf(code); + switch (code) { + case 200: + return strCode; + case 456: + throw new InvocationException(code, strCode, strCode + " error"); + case 556: + throw new InvocationException(code, strCode, Arrays.asList(strCode + " error")); + case 557: + throw new InvocationException(code, strCode, Arrays.asList(Arrays.asList(strCode + " error"))); + default: + break; + } + + return "not expected"; + } + + @Override + public User splitParam(int index, User user) { + return doTest(index, user, null, null); + } + + @Override + public User wrapParam(TestRequest request) { + if (request == null || request.getUser() == null) { + return null; + } + return doTest(request.getIndex(), request.getUser(), request.getUsers(), request.getData()); + } + + @Override + public String addString(String[] strArr) { + String result = Arrays.toString(strArr); + System.out.println("addString: " + result); + return result; + } + + @Override + public String testTraceId() { + 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 new file mode 100644 index 00000000000..962952531d0 --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/WeakPojo.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.pojo.server; + +import java.util.List; + +import org.apache.servicecomb.demo.server.GenericsModel; +import org.apache.servicecomb.provider.pojo.RpcSchema; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; + +@RpcSchema(schemaId = "WeakPojo") +public class WeakPojo { + @Operation(summary = "differentName", operationId = "differentName") + public int diffNames(@Parameter(name = "x") int a, @Parameter(name = "y") int b) { + return a * 2 + b; + } + + public List> genericParams(int code, List> names) { + return names; + } + + public GenericsModel genericParamsModel(int code, GenericsModel model) { + return model; + } + + public Object obj(Object obj) { + return obj; + } +} diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/invoker/InvokerEndpoint.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/invoker/InvokerEndpoint.java new file mode 100644 index 00000000000..4333bf30bcb --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/invoker/InvokerEndpoint.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.server.invoker; + +import org.apache.servicecomb.provider.pojo.RpcSchema; + +@RpcSchema(schemaId = "InvokerEndpoint") +public class InvokerEndpoint { + public ServerModel model(ServerModel request) { + return request; + } +} diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/invoker/ServerModel.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/invoker/ServerModel.java new file mode 100644 index 00000000000..3320f273ce0 --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/invoker/ServerModel.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.demo.pojo.server.invoker; + +public class ServerModel { + private String name; + private int code; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } +} 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 new file mode 100644 index 00000000000..340ddf355eb --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameInterface.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.demo.pojo.server.same.pk1; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.servers.Server; + +@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/SameInterfaceImpl.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameInterfaceImpl.java new file mode 100644 index 00000000000..e0f79bb1e0c --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameInterfaceImpl.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.pojo.server.same.pk1; + +import org.apache.servicecomb.provider.pojo.RpcSchema; +import org.springframework.stereotype.Component; + +@RpcSchema(schemaId = "SameInterface1", schemaInterface = SameInterface.class) +@Component("SameInterface1") +public class SameInterfaceImpl implements SameInterface { + @Override + public String sayHello(String name) { + return "pk1-inf-" + 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 new file mode 100644 index 00000000000..9a59104e99c --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk1/SameService.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.pojo.server.same.pk1; + +import org.apache.servicecomb.provider.pojo.RpcSchema; +import org.springframework.stereotype.Component; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.servers.Server; + +@RpcSchema(schemaId = "SameService1") +@Component("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 new file mode 100644 index 00000000000..7410776ef65 --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameInterface.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.demo.pojo.server.same.pk2; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.servers.Server; + +@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/SameInterfaceImpl.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameInterfaceImpl.java new file mode 100644 index 00000000000..59080d80169 --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameInterfaceImpl.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.pojo.server.same.pk2; + +import org.apache.servicecomb.provider.pojo.RpcSchema; +import org.springframework.stereotype.Component; + +@RpcSchema(schemaId = "SameInterface2", schemaInterface = SameInterface.class) +@Component("SameInterface2") +public class SameInterfaceImpl implements SameInterface { + @Override + public String sayHello(String name) { + return "pk2-inf-" + 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 new file mode 100644 index 00000000000..38fc544681c --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/same/pk2/SameService.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.pojo.server.same.pk2; + +import org.apache.servicecomb.provider.pojo.RpcSchema; +import org.springframework.stereotype.Component; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.servers.Server; + +@RpcSchema(schemaId = "SameService2") +@Component("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/META-INF/spring/pojo.server.bean.xml b/demo/demo-pojo/pojo-server/src/main/resources/META-INF/spring/pojo.server.bean.xml index f1d20f13a13..f64fb18a918 100644 --- a/demo/demo-pojo/pojo-server/src/main/resources/META-INF/spring/pojo.server.bean.xml +++ b/demo/demo-pojo/pojo-server/src/main/resources/META-INF/spring/pojo.server.bean.xml @@ -1,12 +1,13 @@ - - + + + + - - \ No newline at end of file + + diff --git a/demo/demo-pojo/pojo-server/src/main/resources/log4j2.xml b/demo/demo-pojo/pojo-server/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-pojo/pojo-server/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 6711eae566c..0fe723a06ff 100644 --- a/demo/demo-pojo/pojo-server/src/main/resources/microservice.yaml +++ b/demo/demo-pojo/pojo-server/src/main/resources/microservice.yaml @@ -1,18 +1,44 @@ -APPLICATION_ID: pojotest -service_description: - name: pojo - version: 0.0.4 -cse: +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: - registry: - address: http://127.0.0.1:9980 + application: pojotest + name: pojo + version: 0.0.4 + registry: + sc: + address: http://127.0.0.1:30100 rest: - address: 0.0.0.0:8080 + address: 0.0.0.0:8080?protocol=http2 + server: + http2: + useAlpnEnabled: false highway: address: 0.0.0.0:7070 - #executors: - #default: test - #Provider: - #server: test - #server.wrapParam: test + swagger: + disableDataTypeCheck: true + flowcontrol: + enabled: true + Provider: + qps: + limit: + ANY: + FlowControlSchema: + foo: 3 + bar: 3000 diff --git a/demo/demo-pojo/pojo-tests/pom.xml b/demo/demo-pojo/pojo-tests/pom.xml deleted file mode 100644 index 814bfbecdbb..00000000000 --- a/demo/demo-pojo/pojo-tests/pom.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - demo-pojo - io.servicecomb.demo - 0.3.0-SNAPSHOT - - 4.0.0 - - pojo-tests - - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-pojo - - - junit - junit - - - - \ No newline at end of file diff --git a/demo/demo-pojo/pojo-tests/src/test/java/io/servicecomb/demo/integration/PojoReferenceIntegrationTest.java b/demo/demo-pojo/pojo-tests/src/test/java/io/servicecomb/demo/integration/PojoReferenceIntegrationTest.java deleted file mode 100644 index 136bff82b43..00000000000 --- a/demo/demo-pojo/pojo-tests/src/test/java/io/servicecomb/demo/integration/PojoReferenceIntegrationTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.integration; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -import org.junit.BeforeClass; -import org.junit.Test; - -import com.seanyinx.github.unit.scaffolding.Randomness; - -public class PojoReferenceIntegrationTest { - - private final int expected = Randomness.nextInt(); - - @BeforeClass - public static void setUp() throws Exception { - SomePojoTestMain.main(new String[0]); - } - - @Test - public void startsUpAsUsualWhenRpcReferenceIsNotReachable() { - assertThat(SomePojoTestMain.pojoService.localIdentity(expected), is(expected)); - } -} diff --git a/demo/demo-pojo/pojo-tests/src/test/java/io/servicecomb/demo/integration/SomePojoService.java b/demo/demo-pojo/pojo-tests/src/test/java/io/servicecomb/demo/integration/SomePojoService.java deleted file mode 100644 index c284c631538..00000000000 --- a/demo/demo-pojo/pojo-tests/src/test/java/io/servicecomb/demo/integration/SomePojoService.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.integration; - -import org.springframework.stereotype.Component; - -import io.servicecomb.demo.CodeFirstPojoIntf; -import io.servicecomb.provider.pojo.RpcReference; - -@Component -class SomePojoService { - @RpcReference(microserviceName = "pojo", schemaId = "io.servicecomb.demo.CodeFirstPojoIntf") - private CodeFirstPojoIntf codeFirstPojo; - - int localIdentity(int expected) { - return expected; - } - - String remoteSayHi(String username) { - return codeFirstPojo.sayHi(username); - } -} diff --git a/demo/demo-pojo/pojo-tests/src/test/java/io/servicecomb/demo/integration/SomePojoTestMain.java b/demo/demo-pojo/pojo-tests/src/test/java/io/servicecomb/demo/integration/SomePojoTestMain.java deleted file mode 100644 index 00e3f8e46c0..00000000000 --- a/demo/demo-pojo/pojo-tests/src/test/java/io/servicecomb/demo/integration/SomePojoTestMain.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.integration; - -import javax.inject.Inject; - -import org.springframework.stereotype.Component; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -@Component -class SomePojoTestMain { - - static SomePojoService pojoService; - - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } - - @Inject - public void setPojoService(SomePojoService pojoService) { - SomePojoTestMain.pojoService = pojoService; - } -} diff --git a/demo/demo-pojo/pojo-tests/src/test/resources/microservice.yaml b/demo/demo-pojo/pojo-tests/src/test/resources/microservice.yaml deleted file mode 100644 index fc10d3d3519..00000000000 --- a/demo/demo-pojo/pojo-tests/src/test/resources/microservice.yaml +++ /dev/null @@ -1,12 +0,0 @@ -APPLICATION_ID: pojotest -service_description: - name: pojo-test - version: 0.0.4 - rest: - address: 0.0.0.0:8080 - highway: - address: 0.0.0.0:7070 - -servicecomb: - rpcReference: - loadingMode: eager diff --git a/demo/demo-pojo/pom.xml b/demo/demo-pojo/pom.xml index 3a7e2a17701..96217173a03 100644 --- a/demo/demo-pojo/pom.xml +++ b/demo/demo-pojo/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.3.0-SNAPSHOT - - demo-pojo - pom - - pojo-server - pojo-client - pojo-tests + 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.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-pojo + Java Chassis::Demo::POJO + pom + + pojo-server + pojo-client - + + + org.apache.servicecomb + solution-basic + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + handler-publickey-auth + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + diff --git a/demo/demo-register-url-prefix/README.md b/demo/demo-register-url-prefix/README.md new file mode 100644 index 00000000000..e0b2df4db0b --- /dev/null +++ b/demo/demo-register-url-prefix/README.md @@ -0,0 +1,19 @@ +This demo is an integration test for testing + +```yaml +servicecomb: + service: + registry: + registerUrlPrefix: true +``` + +When this configuration is enabled, web context path is added to swagger, and consumer can +invoke with context path + +```yaml +template.getForObject( + "cse://demo-register-url-prefix-server/hellodemo/register/url/prefix/getName?name=2", + String.class) +``` + +This feature is not recommended for use by default, but for some backward capabilities. 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 new file mode 100644 index 00000000000..1b7fc782cd1 --- /dev/null +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-client/pom.xml @@ -0,0 +1,134 @@ + + + + + 4.0.0 + demo-register-url-prefix-client + Java Chassis::Demo::Register URL Prefix client + + org.apache.servicecomb.demo + demo-register-url-prefix + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.prefix.Application + + + + org.apache.servicecomb.demo + demo-schema + + + + + + docker + + 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 + + + + + + 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 new file mode 100644 index 00000000000..8b53c6c6bd4 --- /dev/null +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/java/org/apache/servicecomb/demo/prefix/Application.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.prefix; + +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.web.client.RestOperations; + +@SpringBootApplication +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() { + RestOperations template = RestTemplateBuilder.create(); + TestMgr.check("2", template + .getForObject("cse://demo-register-url-prefix-server/hellodemo/register/url/prefix/getName?name=2", + String.class)); + TestMgr.summary(); + } +} 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 new file mode 100644 index 00000000000..65b87064740 --- /dev/null +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- + +server: + port: 8082 + +servicecomb: + service: + 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 diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/log4j2.xml b/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/test/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixIT.java b/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/test/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixIT.java new file mode 100644 index 00000000000..e4ae70fad6c --- /dev/null +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/test/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixIT.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.prefix; + +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.springframework.boot.SpringApplication; + +public class RegisterUrlPrefixIT { + @BeforeEach + public void setUp() throws Exception { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + SpringApplication.run(Application.class); + + Application.runTest(); + + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} 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 new file mode 100644 index 00000000000..2d34ae62051 --- /dev/null +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-server/pom.xml @@ -0,0 +1,101 @@ + + + + + 4.0.0 + demo-register-url-prefix-server + Java Chassis::Demo::Register URL Prefix Server + + org.apache.servicecomb.demo + demo-register-url-prefix + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.prefix.PrefixApplication + + + + jakarta.ws.rs + jakarta.ws.rs-api + + + + + + 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-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 new file mode 100644 index 00000000000..2fc02f7adbd --- /dev/null +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/PrefixApplication.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.demo.prefix; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +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 new file mode 100644 index 00000000000..dae91a50f49 --- /dev/null +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixEndpoint.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.prefix; + +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 = "RegisterUrlPrefixEndpoint") +@RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) +public class RegisterUrlPrefixEndpoint { + @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-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 new file mode 100644 index 00000000000..8a4140eeced --- /dev/null +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/application.yml @@ -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. +## --------------------------------------------------------------------------- + +server: + servlet: + context-path: /hellodemo + port: 8080 + +servicecomb: + service: + 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 + rest: + address: 0.0.0.0:8080 diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/log4j2.xml b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/logback.xml b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/logback.xml new file mode 100644 index 00000000000..cc214e46e64 --- /dev/null +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/resources/logback.xml @@ -0,0 +1,29 @@ + + + + + + + %d [%level] [%thread] - %msg (%F:%L\)%n + + + + + + + diff --git a/demo/demo-register-url-prefix/pom.xml b/demo/demo-register-url-prefix/pom.xml new file mode 100644 index 00000000000..e51630b5397 --- /dev/null +++ b/demo/demo-register-url-prefix/pom.xml @@ -0,0 +1,66 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-register-url-prefix + Java Chassis::Demo::Register URL Prefix + pom + + demo-register-url-prefix-server + demo-register-url-prefix-client + + + + + org.apache.servicecomb + solution-basic + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + java-chassis-spring-boot-starter-servlet + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + org.apache.servicecomb + foundation-test-scaffolding + compile + + + diff --git a/demo/demo-schema/pom.xml b/demo/demo-schema/pom.xml index aeed9403263..b17f345111b 100644 --- a/demo/demo-schema/pom.xml +++ b/demo/demo-schema/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.3.0-SNAPSHOT - - demo-schema + 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.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-schema + Java Chassis::Demo::Schema + - - - io.servicecomb - handler-bizkeeper - - - io.servicecomb - handler-loadbalance - - - io.servicecomb - transport-highway - - - io.servicecomb - transport-rest-vertx - - - io.servicecomb - transport-rest-servlet - - - javax.servlet - javax.servlet-api - compile - - - io.servicecomb - handler-flowcontrol-qps - - - io.servicecomb - swagger-generator-springmvc - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - config/log4j.eclipse.properties - - - - - - \ No newline at end of file + + + org.apache.servicecomb + solution-basic + + + org.apache.servicecomb + foundation-test-scaffolding + compile + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + config/log4j.eclipse.properties + + + + + + diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstPojoIntf.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstPojoIntf.java deleted file mode 100644 index f3ee8698d16..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstPojoIntf.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo; - -import java.util.Date; -import java.util.List; -import java.util.Map; - -import io.servicecomb.demo.compute.Person; -import io.servicecomb.demo.server.User; - -public interface CodeFirstPojoIntf { - Map testUserMap(Map userMap); - - List testUserArray(List users); - - String[] testStrings(String[] input); - - byte[] testBytes(byte[] input); - - int reduce(int a, int b); - - Date addDate(Date date, long second); - - Person sayHello(Person user); - - String saySomething(String prefix, Person user); - - String sayHi(String name); - - String sayHi2(String name); - - boolean isTrue(); - - String addString(List s); -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstRestTemplate.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstRestTemplate.java deleted file mode 100644 index 305537558bb..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstRestTemplate.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo; - -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; - -import io.servicecomb.core.CseContext; -import io.servicecomb.demo.compute.Person; -import io.servicecomb.demo.server.User; -import io.servicecomb.serviceregistry.RegistryUtils; - -public class CodeFirstRestTemplate { - public void testCodeFirst(RestTemplate template, String microserviceName, String basePath) { - for (String transport : DemoConst.transports) { - CseContext.getInstance().getConsumerProviderManager().setTransport(microserviceName, transport); - TestMgr.setMsg(microserviceName, transport); - - String cseUrlPrefix = "cse://" + microserviceName + basePath; - - testExtend(template, cseUrlPrefix); - - testCodeFirstUserMap(template, cseUrlPrefix); - testCodeFirstTextPlain(template, cseUrlPrefix); - testCodeFirstBytes(template, cseUrlPrefix); - testCseResponse(microserviceName, template, cseUrlPrefix); - testCodeFirstAddDate(template, cseUrlPrefix); - - testCodeFirstAdd(template, cseUrlPrefix); - testCodeFirstAddString(template, cseUrlPrefix); - testCodeFirstIsTrue(template, cseUrlPrefix); - testCodeFirstSayHi2(template, cseUrlPrefix); - testCodeFirstSayHi(template, cseUrlPrefix); - testCodeFirstSaySomething(template, cseUrlPrefix); - // testCodeFirstRawJsonString(template, cseUrlPrefix); - testCodeFirstSayHello(template, cseUrlPrefix); - testCodeFirstReduce(template, cseUrlPrefix); - } - } - - private void testCodeFirstUserMap(RestTemplate template, String cseUrlPrefix) { - User user1 = new User(); - user1.setNames(new String[] {"u1", "u2"}); - - User user2 = new User(); - user2.setNames(new String[] {"u3", "u4"}); - - Map userMap = new HashMap<>(); - userMap.put("u1", user1); - userMap.put("u2", user2); - - @SuppressWarnings("unchecked") - Map result = template.postForObject(cseUrlPrefix + "testUserMap", - userMap, - Map.class); - - TestMgr.check("u1", result.get("u1").getNames()[0]); - TestMgr.check("u2", result.get("u1").getNames()[1]); - TestMgr.check("u3", result.get("u2").getNames()[0]); - TestMgr.check("u4", result.get("u2").getNames()[1]); - } - - private void testCodeFirstTextPlain(RestTemplate template, String cseUrlPrefix) { - String body = "a=1"; - String result = template.postForObject(cseUrlPrefix + "textPlain", - body, - String.class); - TestMgr.check(body, result); - } - - private void testCodeFirstBytes(RestTemplate template, String cseUrlPrefix) { - byte[] body = new byte[] {0, 1, 2}; - byte[] result = template.postForObject(cseUrlPrefix + "bytes", - body, - byte[].class); - TestMgr.check(3, result.length); - TestMgr.check(1, result[0]); - TestMgr.check(1, result[1]); - TestMgr.check(2, result[2]); - } - - protected void checkStatusCode(String microserviceName, int expectStatusCode, HttpStatus httpStatus) { - TestMgr.check(expectStatusCode, httpStatus.value()); - } - - private void testCseResponse(String targetMicroserviceName, RestTemplate template, - String cseUrlPrefix) { - String srcMicroserviceName = RegistryUtils.getMicroservice().getServiceName(); - String context = String.format("{x-cse-src-microservice=%s}", srcMicroserviceName); - - ResponseEntity responseEntity = - template.exchange(cseUrlPrefix + "cseResponse", HttpMethod.GET, null, User.class); - TestMgr.check("User [name=nameA, age=100, index=0]", responseEntity.getBody()); - TestMgr.check("h1v " + context, responseEntity.getHeaders().getFirst("h1")); - TestMgr.check("h2v " + context, responseEntity.getHeaders().getFirst("h2")); - checkStatusCode(targetMicroserviceName, 202, responseEntity.getStatusCode()); - } - - private void testCodeFirstAddDate(RestTemplate template, String cseUrlPrefix) { - Map body = new HashMap<>(); - Date date = new Date(); - body.put("date", date); - - int seconds = 1; - Date result = template.postForObject(cseUrlPrefix + "addDate?seconds={seconds}", - body, - Date.class, - seconds); - TestMgr.check(new Date(date.getTime() + seconds * 1000), result); - } - - protected void testExtend(RestTemplate template, String cseUrlPrefix) { - - } - - protected void testCodeFirstAddString(RestTemplate template, String cseUrlPrefix) { - ResponseEntity responseEntity = - template.exchange(cseUrlPrefix + "addstring?s=a&s=b", - HttpMethod.DELETE, - null, - String.class); - TestMgr.check("ab", responseEntity.getBody()); - } - - protected void testCodeFirstIsTrue(RestTemplate template, String cseUrlPrefix) { - boolean result = template.getForObject(cseUrlPrefix + "istrue", boolean.class); - TestMgr.check(true, result); - } - - protected void testCodeFirstSayHi2(RestTemplate template, String cseUrlPrefix) { - ResponseEntity responseEntity = - template.exchange(cseUrlPrefix + "sayhi/{name}/v2", HttpMethod.PUT, null, String.class, "world"); - TestMgr.check("world sayhi 2", responseEntity.getBody()); - } - - protected void testCodeFirstSayHi(RestTemplate template, String cseUrlPrefix) { - ResponseEntity responseEntity = - template.exchange(cseUrlPrefix + "sayhi/{name}", HttpMethod.PUT, null, String.class, "world"); - TestMgr.check(202, responseEntity.getStatusCode()); - TestMgr.check("world sayhi", responseEntity.getBody()); - } - - protected void testCodeFirstSaySomething(RestTemplate template, String cseUrlPrefix) { - Person person = new Person(); - person.setName("person name"); - - HttpHeaders headers = new HttpHeaders(); - headers.add("prefix", "prefix prefix"); - - HttpEntity requestEntity = new HttpEntity<>(person, headers); - String result = template.postForObject(cseUrlPrefix + "saysomething", requestEntity, String.class); - TestMgr.check("prefix prefix person name", result); - } - - protected void testCodeFirstSayHello(RestTemplate template, String cseUrlPrefix) { - Map persionFieldMap = new HashMap<>(); - persionFieldMap.put("name", "person name from map"); - Person result = template.postForObject(cseUrlPrefix + "sayhello", persionFieldMap, Person.class); - TestMgr.check("hello person name from map", result); - - Person input = new Person(); - input.setName("person name from Object"); - result = template.postForObject(cseUrlPrefix + "sayhello", input, Person.class); - TestMgr.check("hello person name from Object", result); - } - - protected void testCodeFirstAdd(RestTemplate template, String cseUrlPrefix) { - Map params = new HashMap<>(); - params.put("a", "5"); - params.put("b", "3"); - int result = - template.postForObject(cseUrlPrefix + "add", params, Integer.class); - TestMgr.check(8, result); - } - - protected void testCodeFirstReduce(RestTemplate template, String cseUrlPrefix) { - Map params = new HashMap<>(); - params.put("a", "5"); - - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.COOKIE, "b=3"); - - HttpEntity requestEntity = new HttpEntity<>(headers); - ResponseEntity result = - template.exchange(cseUrlPrefix + "reduce?a={a}", HttpMethod.GET, requestEntity, Integer.class, params); - TestMgr.check(2, result.getBody()); - } -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/DemoConst.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/DemoConst.java deleted file mode 100644 index 5f8543ae8f1..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/DemoConst.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo; - -import io.servicecomb.core.Const; - -public interface DemoConst { - String[] transports = new String[] {"highway", "rest", Const.ANY_TRANSPORT}; -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/DemoSSLCustom.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/DemoSSLCustom.java deleted file mode 100644 index 890c94a4697..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/DemoSSLCustom.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo; - -import java.net.URL; - -import io.servicecomb.foundation.ssl.SSLCustom; - -public class DemoSSLCustom extends SSLCustom { - @Override - public char[] decode(char[] encrypted) { - return encrypted; - } - - @Override - public String getFullPath(String filename) { - URL url = Thread.currentThread().getContextClassLoader().getResource("certificates/" + filename); - if (url == null) { - return filename; - } - - return url.getPath(); - } -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/TestMgr.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/TestMgr.java deleted file mode 100644 index 5f0748eb1d2..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/TestMgr.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo; - -import java.util.ArrayList; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class TestMgr { - private static final Logger LOGGER = LoggerFactory.getLogger(TestMgr.class); - - private static final List errorList = new ArrayList<>(); - - private static String msg = ""; - - public static void setMsg(String msg) { - TestMgr.msg = msg; - } - - public static void setMsg(String microserviceName, String transport) { - TestMgr.msg = String.format("microservice=%s, transport=%s", microserviceName, transport); - } - - public static void check(Object expect, Object real) { - String strExpect = String.valueOf(expect); - String strReal = String.valueOf(real); - - if (!strExpect.equals(strReal)) { - errorList.add(new Error(msg + " | Expect " + strExpect + ", but " + strReal)); - } - } - - public static void summary() { - if (errorList.isEmpty()) { - LOGGER.info("............. test finished ............"); - return; - } - - LOGGER.info("............. test not finished ............"); - for (Throwable e : errorList) { - LOGGER.info("", e); - } - } - - public static List errors() { - return errorList; - } -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/compute/Compute.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/compute/Compute.java deleted file mode 100644 index 7a9509307f7..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/compute/Compute.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.compute; - -public interface Compute { - - int add(int a, int b); - - int reduce(int a, int b); - - Person sayHello(Person user); - - String testRawJsonString(String jsonInput); - - String saySomething(String prefix, Person user); - - void sayHi(String name); - - void sayHi2(String name); - - void sayHei(String name); - - boolean isTrue(); - - String addString(String[] s); -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/compute/Person.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/compute/Person.java deleted file mode 100644 index fdeaaf98d5b..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/compute/Person.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.compute; - -public class Person { - 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/io/servicecomb/demo/controller/Controller.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/controller/Controller.java deleted file mode 100644 index 6a475243d40..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/controller/Controller.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.controller; - -public interface Controller { - int add(int a, int b); - - String sayHello(String name); - - String saySomething(String prefix, Person user); - - String sayHi(String name); - - String sayHei(String name); -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/controller/Person.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/controller/Person.java deleted file mode 100644 index ddb3aadb669..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/controller/Person.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.controller; - -public class Person { - 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/io/servicecomb/demo/helloworld/greeter/Hello.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/helloworld/greeter/Hello.java deleted file mode 100644 index 07aac5b7e52..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/helloworld/greeter/Hello.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.helloworld.greeter; - -public interface Hello { - public String SayHello(String name); - - public String SayHelloAgain(String name); -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/server/Test.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/server/Test.java deleted file mode 100644 index 4466a4ed53c..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/server/Test.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.server; - -public interface Test { - String testStringArray(String[] arr); - - String getTestString(String code); - - String postTestStatic(int code); - - String testException(int code); - - User wrapParam(TestRequest request); - - User splitParam(int index, User user); - - String addString(String[] strArr); -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/server/TestRequest.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/server/TestRequest.java deleted file mode 100644 index ef79c1977fe..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/server/TestRequest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.server; - -import java.util.ArrayList; -import java.util.List; - -public class TestRequest { - private int index; - - private User user; - - private List users = new ArrayList<>(); - - private byte[] data; - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - - public List getUsers() { - return users; - } - - public void setUsers(List users) { - this.users = users; - } - - public byte[] getData() { - return data; - } - - public void setData(byte[] data) { - this.data = data; - } -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/server/TestResponse.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/server/TestResponse.java deleted file mode 100644 index 82dec9dbd20..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/server/TestResponse.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.server; - -public class TestResponse { - private User user; - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - - @Override - public String toString() { - return "TestResponse [user=" + user + "]"; - } -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/server/User.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/server/User.java deleted file mode 100644 index be75ead0d80..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/server/User.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.server; - -public class User { - private String name = "nameA"; - - private int age = 100; - - private int index; - - private String[] names; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String[] getNames() { - return names; - } - - public void setNames(String[] names) { - this.names = names; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - @Override - public String toString() { - return "User [name=" + name + ", age=" + age + ", index=" + index + "]"; - } -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/smartcare/Application.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/smartcare/Application.java deleted file mode 100644 index c8db5f2f438..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/smartcare/Application.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.smartcare; - -import java.util.List; - -public class Application { - private String name; - - private String labelEN; - - private String labelCH; - - private String defaultGroup; - - private String version; - - private boolean dynamicFlag; - - private List groups; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getLabelEN() { - return labelEN; - } - - public void setLabelEN(String labelEN) { - this.labelEN = labelEN; - } - - public String getLabelCH() { - return labelCH; - } - - public void setLabelCH(String labelCH) { - this.labelCH = labelCH; - } - - public String getDefaultGroup() { - return defaultGroup; - } - - public void setDefaultGroup(String defaultGroup) { - this.defaultGroup = defaultGroup; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public boolean isDynamicFlag() { - return dynamicFlag; - } - - public void setDynamicFlag(boolean dynamicFlag) { - this.dynamicFlag = dynamicFlag; - } - - public List getGroups() { - return groups; - } - - public void setGroups(List groups) { - this.groups = groups; - } - - @Override - public String toString() { - return "name=" + name + "\n" - + "version=" + version; - } -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/smartcare/Group.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/smartcare/Group.java deleted file mode 100644 index 44a20635d73..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/smartcare/Group.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.smartcare; - -public class Group { - private String name; - - private String labelEN; - - private String labelCH; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getLabelEN() { - return labelEN; - } - - public void setLabelEN(String labelEN) { - this.labelEN = labelEN; - } - - public String getLabelCH() { - return labelCH; - } - - public void setLabelCH(String labelCH) { - this.labelCH = labelCH; - } -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/smartcare/Response.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/smartcare/Response.java deleted file mode 100644 index 4a34525e5c3..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/smartcare/Response.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.smartcare; - -public class Response { - private int resultCode; - - private String resultMessage; - - public int getResultCode() { - return resultCode; - } - - public void setResultCode(int resultCode) { - this.resultCode = resultCode; - } - - public String getResultMessage() { - return resultMessage; - } - - public void setResultMessage(String resultMessage) { - this.resultMessage = resultMessage; - } - - @Override - public String toString() { - return "resultCode: " + resultCode + "\n" + "resultMessage: " + resultMessage; - } -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/smartcare/SmartCare.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/smartcare/SmartCare.java deleted file mode 100644 index 9398ff42ad7..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/smartcare/SmartCare.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.smartcare; - -public interface SmartCare { - - Response addApplication(Application application); - - Response delApplication(String appName); -} diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/tccserver/TestTcc.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/tccserver/TestTcc.java deleted file mode 100644 index 1f254c7706b..00000000000 --- a/demo/demo-schema/src/main/java/io/servicecomb/demo/tccserver/TestTcc.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.tccserver; - -import io.servicecomb.demo.server.TestRequest; -import io.servicecomb.demo.server.User; - -public interface TestTcc { - User wrapParam(TestRequest request); - - User splitParam(int index, User user); -} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CategorizedTestCase.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CategorizedTestCase.java new file mode 100644 index 00000000000..c5267878c92 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CategorizedTestCase.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; + +public interface CategorizedTestCase { + /** + * test case which only successful in REST transport + */ + default void testRestTransport() throws Exception { + + } + + /** + * test case which only successful in HIGHWAY transport + */ + default void testHighwayTransport() throws Exception { + + } + + /** + * test case which successful in both REST and HIGHWAY transport + */ + default void testAllTransport() throws Exception { + + } + + default String getMicroserviceName() { + return null; + } +} 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 new file mode 100644 index 00000000000..acb82190374 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CategorizedTestCaseRunner.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.demo; + +import java.util.Map; + +import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CategorizedTestCaseRunner { + private static final Logger LOGGER = LoggerFactory.getLogger(CategorizedTestCaseRunner.class); + + public static void runCategorizedTestCase(String microserviceName) throws Exception { + Map tests = BeanUtils.getContext().getBeansOfType(CategorizedTestCase.class); + for (String transport : DemoConst.transports) { + for (CategorizedTestCase testCase : tests.values()) { + + try { + if (testCase.getMicroserviceName() != null) { + changeTransport(testCase.getMicroserviceName(), transport); + } else { + changeTransport(microserviceName, transport); + } + + testCase.testAllTransport(); + if ("rest".equals(transport)) { + testCase.testRestTransport(); + } else if ("highway".equals(transport)) { + testCase.testHighwayTransport(); + } + } catch (Exception e) { + LOGGER.error("run categorized test case " + + testCase.getClass().getName() + + " failed.", e); + throw e; + } + } + } + } + + public static void changeTransport(String microserviceName, String 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/CodeFirstPojoIntf.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CodeFirstPojoIntf.java new file mode 100644 index 00000000000..38779b3e141 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CodeFirstPojoIntf.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.demo; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.demo.compute.Person; +import org.apache.servicecomb.demo.mapnull.ParseRequest; +import org.apache.servicecomb.demo.mapnull.ParseResponse; +import org.apache.servicecomb.demo.server.MapModel; +import org.apache.servicecomb.demo.server.User; + +public interface CodeFirstPojoIntf { + ParseResponse parse(ParseRequest request); + + MapModel testMapModel(MapModel model); + + Map testMap(Map map); + + Map testUserMap(Map userMap); + + List testUserArray(List users); + + String[] testStrings(String[] input); + + byte[] testBytes(byte[] input); + + int reduce(int a, int b); + + Date addDate(Date date, long second); + + Person sayHello(Person user); + + String saySomething(String prefix, Person user); + + String sayHi(String name); + + boolean isTrue(); + + String addString(List s); +} 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 new file mode 100644 index 00000000000..ee2bf395847 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CodeFirstRestTemplate.java @@ -0,0 +1,303 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +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.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.HttpStatusCode; +import org.springframework.http.ResponseEntity; +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) { + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); + TestMgr.setMsg(microserviceName, transport); + } + + public void testCodeFirst(RestOperations template, String microserviceName, String basePath) { + String cseUrlPrefix = "cse://" + microserviceName + basePath; + changeTransport(microserviceName, "highway"); + testOnlyHighway(template, cseUrlPrefix); + + changeTransport(microserviceName, CoreConst.RESTFUL); + testOnlyRest(microserviceName, template, cseUrlPrefix); + + for (String transport : DemoConst.transports) { + changeTransport(microserviceName, transport); + testAllTransport(microserviceName, template, cseUrlPrefix); + } + } + + protected void testAllTransport(String microserviceName, RestOperations template, String cseUrlPrefix) { + testCodeFirstUserMap(template, cseUrlPrefix); + testCodeFirstTextPlain(template, cseUrlPrefix); + testCodeFirstBytes(template, cseUrlPrefix); + testCseResponse(microserviceName, template, cseUrlPrefix); + testCodeFirstAddDate(template, cseUrlPrefix); + + testCodeFirstAdd(template, cseUrlPrefix); + testCodeFirstAddString(template, cseUrlPrefix); + testCodeFirstIsTrue(template, cseUrlPrefix); + testCodeFirstSayHi2(template, cseUrlPrefix); + testCodeFirstSayHi(template, cseUrlPrefix); + testCodeFirstSaySomething(template, cseUrlPrefix); + testCodeFirstSayHello(template, cseUrlPrefix); + testCodeFirstReduce(template, cseUrlPrefix); + + testTraceIdOnContextContainsTraceId(template, cseUrlPrefix); + + testRawJson(template, cseUrlPrefix); + } + + protected void testOnlyHighway(RestOperations template, String cseUrlPrefix) { + + } + + protected void testOnlyRest(String microserviceName, RestOperations template, String cseUrlPrefix) { + testCodeFirstUserMap(template, cseUrlPrefix); + testCodeFirstTextPlain(template, cseUrlPrefix); + testCodeFirstBytes(template, cseUrlPrefix); + testCseResponse(microserviceName, template, cseUrlPrefix); + testCodeFirstAddDate(template, cseUrlPrefix); + testCodeFirstAdd(template, cseUrlPrefix); + testCodeFirstAddString(template, cseUrlPrefix); + + testModelFieldIgnore(template, cseUrlPrefix); + testCodeFirstReduce(template, cseUrlPrefix); + // only rest transport will set trace id + testTraceIdOnNotSetBefore(template, cseUrlPrefix); + } + + private void testCodeFirstUserMap(RestOperations template, String cseUrlPrefix) { + User user1 = new User(); + user1.setNames(new String[] {"u1", "u2"}); + + User user2 = new User(); + user2.setNames(new String[] {"u3", "u4"}); + + Map userMap = new HashMap<>(); + userMap.put("u1", user1); + userMap.put("u2", user2); + + // 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]); + TestMgr.check("u3", result.get("u2").getNames()[0]); + TestMgr.check("u4", result.get("u2").getNames()[1]); + } + + private void testCodeFirstTextPlain(RestOperations template, String cseUrlPrefix) { + String body = "a=1"; + String result = template.postForObject(cseUrlPrefix + "textPlain", + body, + String.class); + TestMgr.check(body, result); + } + + private void testCodeFirstBytes(RestOperations template, String cseUrlPrefix) { + byte[] body = new byte[] {0, 1, 2}; + byte[] result = template.postForObject(cseUrlPrefix + "bytes", + body, + byte[].class); + TestMgr.check(3, result.length); + TestMgr.check(1, result[0]); + TestMgr.check(1, result[1]); + TestMgr.check(2, result[2]); + } + + protected void checkStatusCode(String microserviceName, int expectStatusCode, HttpStatusCode httpStatus) { + TestMgr.check(expectStatusCode, httpStatus.value()); + } + + private void testCseResponse(String targetMicroserviceName, RestOperations template, + String cseUrlPrefix) { + String srcMicroserviceName = BootStrapProperties.readServiceName(environment); + + ResponseEntity responseEntity = + template.exchange(cseUrlPrefix + "cseResponse", HttpMethod.GET, null, User.class); + TestMgr.check("User [name=nameA, age=100, index=0]", responseEntity.getBody()); + TestMgr.check("h1v " + srcMicroserviceName, responseEntity.getHeaders().getFirst("h1")); + TestMgr.check("h2v " + srcMicroserviceName, responseEntity.getHeaders().getFirst("h2")); + checkStatusCode(targetMicroserviceName, 202, responseEntity.getStatusCode()); + } + + private void testCodeFirstAddDate(RestOperations template, String cseUrlPrefix) { + Map body = new HashMap<>(); + Date date = new Date(); + body.put("date", date); + + int seconds = 1; + Date result = template.postForObject(cseUrlPrefix + "addDate?seconds={seconds}", + body, + Date.class, + seconds); + TestMgr.check(new Date(date.getTime() + seconds * 1000), result); + } + + private void testCodeFirstAddString(RestOperations template, String cseUrlPrefix) { + ResponseEntity responseEntity = + template.exchange(cseUrlPrefix + "addstring?s=a&s=b", + HttpMethod.DELETE, + null, + String.class); + TestMgr.check("ab", responseEntity.getBody()); + } + + private void testCodeFirstIsTrue(RestOperations template, String cseUrlPrefix) { + boolean result = template.getForObject(cseUrlPrefix + "istrue", boolean.class); + TestMgr.check(true, result); + } + + 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(RestOperations template, String cseUrlPrefix) { + ResponseEntity responseEntity = + template.exchange(cseUrlPrefix + "sayhi/{name}", HttpMethod.PUT, null, String.class, "world"); + TestMgr.check(202, responseEntity.getStatusCode().value()); + TestMgr.check("world sayhi", responseEntity.getBody()); + } + + private void testCodeFirstSaySomething(RestOperations template, String cseUrlPrefix) { + Person person = new Person(); + person.setName("person name"); + + HttpHeaders headers = new HttpHeaders(); + headers.add("prefix", "prefix prefix"); + headers.add("userId", "serviceCombUser"); + + HttpEntity requestEntity = new HttpEntity<>(person, headers); + String result = template.postForObject(cseUrlPrefix + "saysomething", requestEntity, String.class); + TestMgr.check("prefix prefix person name", result); + } + + 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); + TestMgr.check("hello person name from map", result); + + Person input = new Person(); + input.setName("person name from Object"); + result = template.postForObject(cseUrlPrefix + "sayhello", input, Person.class); + TestMgr.check("hello person name from Object", result); + } + + private void testCodeFirstAdd(RestOperations template, String cseUrlPrefix) { + Map params = new HashMap<>(); + params.put("a", "5"); + params.put("b", "3"); + int result = + template.postForObject(cseUrlPrefix + "add", params, Integer.class); + TestMgr.check(8, result); + } + + private void testCodeFirstReduce(RestOperations template, String cseUrlPrefix) { + Map params = new HashMap<>(); + params.put("a", "5"); + + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.COOKIE, "b=3"); + + HttpEntity requestEntity = new HttpEntity<>(headers); + ResponseEntity result = + template.exchange(cseUrlPrefix + "reduce?a={a}", HttpMethod.GET, requestEntity, Integer.class, params); + TestMgr.check(2, result.getBody()); + } + + private void testModelFieldIgnore(RestOperations template, String cseUrlPrefix) { + InputModelForTestIgnore input = new InputModelForTestIgnore("input_id_rest", "input_id_content", + new Person("inputSomeone"), new JsonObject("{\"InputJsonKey\" : \"InputJsonValue\"}"), () -> { + }); + OutputModelForTestIgnore output = template + .postForObject(cseUrlPrefix + "ignore", input, OutputModelForTestIgnore.class); + + TestMgr.check(null, output.getInputId()); + TestMgr.check(input.getContent(), output.getContent()); + TestMgr.check(null, output.getOutputId()); + + TestMgr.check(null, output.getInputIgnoreInterface()); + TestMgr.check(null, output.getInputJsonObject()); + TestMgr.check(null, output.getInputObject()); + + TestMgr.check(null, output.getOutputIgnoreInterface()); + TestMgr.check(null, output.getOutputJsonObject()); + TestMgr.check(null, output.getOutputObject()); + } + + 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(RestOperations template, String cseUrlPrefix) { + String traceIdUrl = cseUrlPrefix + "traceId"; + String result = template.getForObject(traceIdUrl, String.class); + TestMgr.checkNotEmpty(result); + } + + private void testTraceIdOnContextContainsTraceId(RestOperations template, String cseUrlPrefix) { + String traceIdUrl = cseUrlPrefix + "traceId"; + InvocationContext invocationContext = new InvocationContext(); + 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); + ContextUtils.removeInvocationContext(); + } +} 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 new file mode 100644 index 00000000000..550f9e5adaf --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/CommonSchemaInterface.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.demo; + +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +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.Operation; + +@RequestMapping("/CommonSchemaInterface") +public interface CommonSchemaInterface { + @GetMapping(path = "testInvocationTimeout") + String testInvocationTimeout(@RequestParam("timeout") long timeout, @RequestParam("name") String name); + + @GetMapping(path = "testInvocationTimeoutWithInvocation") + @Operation(summary = "testInvocationTimeoutWithInvocation", operationId = "testInvocationTimeoutWithInvocation") + String testInvocationTimeout(InvocationContext context, @RequestParam("timeout") long timeout, + @RequestParam("name") String name); + + @GetMapping(path = "testInvocationTimeoutInClientWait") + String testInvocationTimeoutInClientWait(@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 new file mode 100644 index 00000000000..84018a5254b --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/DemoConst.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.demo; + +import org.apache.servicecomb.core.CoreConst; + +public interface DemoConst { + String[] transports = new String[] {"rest", "highway", CoreConst.ANY_TRANSPORT}; +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/DemoSSLCustom.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/DemoSSLCustom.java new file mode 100644 index 00000000000..ee8e5fd918b --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/DemoSSLCustom.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.demo; + +import java.io.File; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.ssl.SSLCustom; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DemoSSLCustom extends SSLCustom { + private static final Logger LOGGER = LoggerFactory.getLogger(DemoSSLCustom.class); + + @Override + public char[] decode(char[] encrypted) { + return encrypted; + } + + @Override + public String getFullPath(String filename) { + LOGGER.info("current working dir :" + System.getProperty("user.dir")); + + if (StringUtils.isEmpty(filename)) { + return null; + } + + // local for different IDEs + File localFile = new File( + System.getProperty("user.dir") + "/demo/demo-springmvc/springmvc-server/src/main/resources/certificates/" + + filename); + if (localFile.isFile()) { + return localFile.getAbsolutePath(); + } + + localFile = new File( + System.getProperty("user.dir") + "/src/main/resources/certificates/" + + filename); + if (localFile.isFile()) { + return localFile.getAbsolutePath(); + } + + localFile = new File(System.getProperty("user.dir") + "/certificates/" + filename); + if (localFile.isFile()) { + return localFile.getAbsolutePath(); + } + + // docker + localFile = new File("/maven/maven/certificates/" + filename); + if (localFile.isFile()) { + return localFile.getAbsolutePath(); + } + + // in jar, maybe + LOGGER.info("not found file {} in file system, maybe in jar.", filename); + return "certificates/" + filename; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/EmptyObject.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/EmptyObject.java new file mode 100644 index 00000000000..2c1baa47265 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/EmptyObject.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; + +public class EmptyObject { + +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/Generic.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/Generic.java new file mode 100644 index 00000000000..401bb52a286 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/Generic.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; + +public class Generic { + public T value; +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/RestObjectMapperWithStringMapper.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/RestObjectMapperWithStringMapper.java new file mode 100644 index 00000000000..a15d4cb3f52 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/RestObjectMapperWithStringMapper.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.RestObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; + +import com.fasterxml.jackson.databind.JavaType; + +/** + * Demonstrate how to using String as raw type when using RestTemplate to invoke a service that use POJO. e.g. + *

+ * Provider:

+ * + * public Response errorCodeWithHeader(MultiRequest request) + * + *

+ * Consumer:

+ * + * String stringRequest = "{\"key\":\"testValue\"}"; + * template.postForEntity(url, stringRequest, MultiResponse200.class); + * + *

+ * Caution: json will convert String to object based on String constructor, using this feature will make default + * conversion change. You must write convertValue to check possible types using. + */ +public class RestObjectMapperWithStringMapper extends RestObjectMapper { + private static final long serialVersionUID = 4279371572149490568L; + + private static Logger LOGGER = LoggerFactory.getLogger(RestObjectMapperWithStringMapper.class); + + public RestObjectMapperWithStringMapper() { + super(); + } + + @Override + public T convertValue(Object fromValue, JavaType toValueType) throws IllegalArgumentException { + if (String.class.isInstance(fromValue) + && !BeanUtils.isSimpleValueType(toValueType.getRawClass())) { + try { + return super.readValue((String) fromValue, toValueType); + } catch (IOException e) { + LOGGER.error("Failed to convert value for {}.", e.getMessage()); + } + } + return super.convertValue(fromValue, toValueType); + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/RestObjectMapperWithStringMapperNotWriteNull.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/RestObjectMapperWithStringMapperNotWriteNull.java new file mode 100644 index 00000000000..38329e1f6f4 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/RestObjectMapperWithStringMapperNotWriteNull.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; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +/** + * Demonstrate how to using String as raw type when using RestTemplate to invoke a service that use POJO. e.g. + *

+ * Provider:

+ * + * public Response errorCodeWithHeader(MultiRequest request) + * + *

+ * Consumer:

+ * + * String stringRequest = "{\"key\":\"testValue\"}"; + * template.postForEntity(url, stringRequest, MultiResponse200.class); + * + *

+ * Caution: json will convert String to object based on String constructor, using this feature will make default + * conversion change. You must write convertValue to check possible types using. + */ +public class RestObjectMapperWithStringMapperNotWriteNull extends RestObjectMapperWithStringMapper { + private static final long serialVersionUID = 4279371572149490560L; + + public RestObjectMapperWithStringMapperNotWriteNull() { + super(); + setSerializationInclusion(Include.NON_NULL); + } +} 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 new file mode 100644 index 00000000000..c90fe8bc1a5 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/TestMgr.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestMgr { + private static final Logger LOGGER = LoggerFactory.getLogger(TestMgr.class); + + private static final List errorList = new ArrayList<>(); + + private static String msg = ""; + + private static final AtomicLong checks = new AtomicLong(0); + + public static void setMsg(String msg) { + TestMgr.msg = msg; + } + + public static void setMsg(String microserviceName, String transport) { + TestMgr.msg = String.format("microservice=%s, transport=%s", microserviceName, transport); + } + + public static void check(Object expect, Object real) { + check(expect, real, null); + } + + public static void check(Object expect, Object real, Throwable error) { + checks.incrementAndGet(); + + if (expect == real) { + return; + } + + String strExpect = String.valueOf(expect); + String strReal = String.valueOf(real); + + if (!strExpect.equals(strReal)) { + Error newError = new Error(msg + " | Expect " + strExpect + ", but " + strReal); + if (error != null) { + newError.setStackTrace(error.getStackTrace()); + } + errorList.add(newError); + } + } + + public static void checkNotEmpty(String real) { + checks.incrementAndGet(); + + if (StringUtils.isEmpty(real)) { + errorList.add(new Error(msg + " | unexpected null result, method is " + getCaller())); + } + } + + public static void fail(String desc) { + failed(desc, new Exception(desc)); + } + + public static void failed(String desc, Throwable e) { + checks.incrementAndGet(); + + Error error = new Error(msg + " | " + desc + ", method is " + getCaller()); + if (e != null) { + error.setStackTrace(e.getStackTrace()); + } + errorList.add(error); + } + + public static boolean isSuccess() { + return errorList.isEmpty(); + } + + public static void summary() { + if (errorList.isEmpty()) { + LOGGER.info("............. test finished ............"); + LOGGER.info("............. total checks : " + checks.get()); + return; + } + + LOGGER.info("............. test not finished ............"); + LOGGER.info("............. total checks : " + checks.get()); + LOGGER.info("............. total errors : " + errorList.size()); + LOGGER.info("............. error details: "); + for (Throwable e : errorList) { + LOGGER.info("", e); + } + } + + public static List errors() { + return errorList; + } + + private static String getCaller() { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + if (stackTrace.length < 3) { + return null; + } + StackTraceElement stackTraceElement = stackTrace[3]; + return stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName(); + } +} 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/compute/Compute.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/Compute.java new file mode 100644 index 00000000000..8249a82b31c --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/Compute.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.demo.compute; + +public interface Compute { + + int add(int a, int b); + + int reduce(int a, int b); + + Person sayHello(Person user); + + String testRawJsonString(String jsonInput); + + String saySomething(String prefix, Person user); + + void sayHi(String name); + + void sayHi2(String name); + + void sayHei(String name); + + boolean isTrue(); + + String addString(String[] s); +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/GenericParam.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/GenericParam.java new file mode 100644 index 00000000000..206f652e6cf --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/GenericParam.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.demo.compute; + +public class GenericParam { + private String str; + + private long num; + + private T data; + + public String getStr() { + return str; + } + + public void setStr(String str) { + this.str = str; + } + + public GenericParam str(String str) { + this.str = str; + return this; + } + + public long getNum() { + return num; + } + + public void setNum(long num) { + this.num = num; + } + + public GenericParam num(long num) { + this.num = num; + return this; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public GenericParam data(T data) { + this.data = data; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("GenericParam{"); + sb.append("str='").append(str).append('\''); + sb.append(", num=").append(num); + sb.append(", data=").append(data); + sb.append('}'); + return sb.toString(); + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/GenericParamExtended.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/GenericParamExtended.java new file mode 100644 index 00000000000..084a8e95136 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/GenericParamExtended.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.compute; + +public class GenericParamExtended extends GenericParam { + private String strExtended; + + private int intExtended; + + public String getStrExtended() { + return strExtended; + } + + public void setStrExtended(String strExtended) { + this.strExtended = strExtended; + } + + public GenericParamExtended strExtended(String strExtended) { + this.strExtended = strExtended; + return this; + } + + public int getIntExtended() { + return intExtended; + } + + public void setIntExtended(int intExtended) { + this.intExtended = intExtended; + } + + public GenericParamExtended intExtended(int intExtended) { + this.intExtended = intExtended; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("GenericParamExtended{"); + sb.append("strExtended='").append(strExtended).append('\''); + sb.append(", intExtended=").append(intExtended); + sb.append(", super=").append(super.toString()); + sb.append('}'); + return sb.toString(); + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/GenericParamWithJsonIgnore.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/GenericParamWithJsonIgnore.java new file mode 100644 index 00000000000..448f64b9f4e --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/GenericParamWithJsonIgnore.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.compute; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +public class GenericParamWithJsonIgnore { + private String str; + + private long num; + + @JsonIgnore + private T data; + + public String getStr() { + return str; + } + + public void setStr(String str) { + this.str = str; + } + + public GenericParamWithJsonIgnore str(String str) { + this.str = str; + return this; + } + + public long getNum() { + return num; + } + + public void setNum(long num) { + this.num = num; + } + + public GenericParamWithJsonIgnore num(long num) { + this.num = num; + return this; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public GenericParamWithJsonIgnore data(T data) { + this.data = data; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("GenericParamWithJsonIgnore{"); + sb.append("str='").append(str).append('\''); + sb.append(", num=").append(num); + sb.append(", data=").append(data); + sb.append('}'); + return sb.toString(); + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/Person.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/Person.java new file mode 100644 index 00000000000..d3f0cfb8f3f --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/compute/Person.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.compute; + +public class Person { + private String name; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + public Person() { + } + + public Person(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/Controller.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/Controller.java new file mode 100644 index 00000000000..b82cd345a52 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/Controller.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.demo.controller; + +public interface Controller { + int add(int a, int b); + + String sayHello(String name); + + String saySomething(String prefix, Person user); + + String sayHi(String name); + + String sayHei(String name); + + String sayHello1(String name); +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/Person.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/Person.java new file mode 100644 index 00000000000..061598eaa1d --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/Person.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 Person { + 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/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/helloworld/greeter/Hello.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/helloworld/greeter/Hello.java new file mode 100644 index 00000000000..d9b35305ae6 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/helloworld/greeter/Hello.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.demo.helloworld.greeter; + +public interface Hello { + String SayHello(String name); + + String SayHelloAgain(String name); +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/ignore/IgnoreInterface.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/ignore/IgnoreInterface.java new file mode 100644 index 00000000000..253019af8ca --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/ignore/IgnoreInterface.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.ignore; + +public interface IgnoreInterface { + void ignoreMethod(); +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/ignore/InputModelForTestIgnore.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/ignore/InputModelForTestIgnore.java new file mode 100644 index 00000000000..b54b91d79ba --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/ignore/InputModelForTestIgnore.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.ignore; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import io.vertx.core.json.JsonObject; + +public class InputModelForTestIgnore { + @JsonIgnore + private String inputId = null; + + private String content = null; + + @JsonIgnore + private Object inputObject = null; + + @JsonIgnore + private JsonObject inputJsonObject = null; + + @JsonIgnore + private IgnoreInterface inputIgnoreInterface = null; + + public String getInputId() { + return this.inputId; + } + + public void setInputId(String inputId) { + this.inputId = inputId; + } + + public String getContent() { + return this.content; + } + + public void setContent(String content) { + this.content = content; + } + + public Object getInputObject() { + return inputObject; + } + + public void setInputObject(Object inputObject) { + this.inputObject = inputObject; + } + + public JsonObject getInputJsonObject() { + return inputJsonObject; + } + + public void setInputJsonObject(JsonObject inputJsonObject) { + this.inputJsonObject = inputJsonObject; + } + + public IgnoreInterface getInputIgnoreInterface() { + return inputIgnoreInterface; + } + + public void setInputIgnoreInterface(IgnoreInterface inputIgnoreInterface) { + this.inputIgnoreInterface = inputIgnoreInterface; + } + + public InputModelForTestIgnore() { + } + + public InputModelForTestIgnore(String inputId, String content, Object inputObject, + JsonObject inputJsonObject, IgnoreInterface inputIgnoreInterface) { + this.inputId = inputId; + this.content = content; + this.inputObject = inputObject; + this.inputJsonObject = inputJsonObject; + this.inputIgnoreInterface = inputIgnoreInterface; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/ignore/OutputModelForTestIgnore.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/ignore/OutputModelForTestIgnore.java new file mode 100644 index 00000000000..ec35eeca517 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/ignore/OutputModelForTestIgnore.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ignore; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import io.vertx.core.json.JsonObject; + +public class OutputModelForTestIgnore { + @JsonIgnore + private String outputId = null; + + private String inputId = null; + + private String content = null; + + @JsonIgnore + private Object inputObject = null; + + @JsonIgnore + private JsonObject inputJsonObject = null; + + @JsonIgnore + private IgnoreInterface inputIgnoreInterface = null; + + @JsonIgnore + private Object outputObject = null; + + @JsonIgnore + private JsonObject outputJsonObject = null; + + @JsonIgnore + private IgnoreInterface outputIgnoreInterface = null; + + public String getOutputId() { + return this.outputId; + } + + public void setOutputId(String outputId) { + this.outputId = outputId; + } + + public String getInputId() { + return this.inputId; + } + + public void setInputId(String inputId) { + this.inputId = inputId; + } + + public String getContent() { + return this.content; + } + + public void setContent(String content) { + this.content = content; + } + + public Object getInputObject() { + return inputObject; + } + + public void setInputObject(Object inputObject) { + this.inputObject = inputObject; + } + + public JsonObject getInputJsonObject() { + return inputJsonObject; + } + + public void setInputJsonObject(JsonObject inputJsonObject) { + this.inputJsonObject = inputJsonObject; + } + + public IgnoreInterface getInputIgnoreInterface() { + return inputIgnoreInterface; + } + + public void setInputIgnoreInterface(IgnoreInterface inputIgnoreInterface) { + this.inputIgnoreInterface = inputIgnoreInterface; + } + + public Object getOutputObject() { + return outputObject; + } + + public void setOutputObject(Object outputObject) { + this.outputObject = outputObject; + } + + public JsonObject getOutputJsonObject() { + return outputJsonObject; + } + + public void setOutputJsonObject(JsonObject outputJsonObject) { + this.outputJsonObject = outputJsonObject; + } + + public IgnoreInterface getOutputIgnoreInterface() { + return outputIgnoreInterface; + } + + public void setOutputIgnoreInterface(IgnoreInterface outputIgnoreInterface) { + this.outputIgnoreInterface = outputIgnoreInterface; + } + + public OutputModelForTestIgnore() { + } + + public OutputModelForTestIgnore(String outputId, String inputId, String content, Object inputObject, + JsonObject inputJsonObject, IgnoreInterface inputIgnoreInterface, Object outputObject, + JsonObject outputJsonObject, IgnoreInterface outputIgnoreInterface) { + this.outputId = outputId; + this.inputId = inputId; + this.content = content; + this.inputObject = inputObject; + this.inputJsonObject = inputJsonObject; + this.inputIgnoreInterface = inputIgnoreInterface; + this.outputObject = outputObject; + this.outputJsonObject = outputJsonObject; + this.outputIgnoreInterface = outputIgnoreInterface; + } +} 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 new file mode 100644 index 00000000000..797833437a7 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBJob.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.demo.jaxbbean; + + +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) +@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL) +public class JAXBJob { + private String name; + + private String content; + + public JAXBJob() { + } + + public JAXBJob(String name, String content) { + this.name = name; + this.content = content; + } + + @Override + public String toString() { + return "Job{" + "name'" + name + '\'' + ", content='" + content + '\'' + '}'; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } +} 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 new file mode 100644 index 00000000000..450ebacabe8 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBPerson.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.jaxbbean; + +import java.io.Serializable; + +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") +@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL) +public class JAXBPerson implements Serializable { + private static final long serialVersionUID = -7127275268696924681L; + + private String name; + + private int age; + + private String role; + + private String weight; + + private JAXBJob job; + + public JAXBPerson() { + } + + public JAXBPerson(String name, int age, String role, String weight) { + this.name = name; + this.age = age; + this.role = role; + this.weight = weight; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @XmlAttribute + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @XmlElement(nillable = true) + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + @XmlTransient + public String getWeight() { + return weight; + } + + public void setWeight(String weight) { + this.weight = weight; + } + + @XmlElement + public JAXBJob getJob() { + return job; + } + + public void setJob(JAXBJob job) { + this.job = job; + } + + @Override + public String toString() { + return "Person{" + + "name='" + name + '\'' + + ", age=" + age + + ", role='" + role + '\'' + + ", job=" + job + + '}'; + } +} 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 new file mode 100644 index 00000000000..7a99260c393 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxrs/server/validation/ValidationModel.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.jaxrs.server.validation; + +import java.util.List; + +import jakarta.validation.constraints.NotNull; + +public class ValidationModel { + @NotNull + private Integer age; + + @NotNull + private List members; + + @NotNull + private String name; + + public Integer getAge() { + return age; + } + + public void setAge(Integer age) { + this.age = age; + } + + public List getMembers() { + return members; + } + + public void setMembers(List members) { + this.members = members; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/mapnull/ParseRequest.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/mapnull/ParseRequest.java new file mode 100644 index 00000000000..1544e99b81d --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/mapnull/ParseRequest.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.demo.mapnull; + +import java.util.HashMap; +import java.util.Map; + +public class ParseRequest { + public String msgType = ""; + + public String strMsg; + + public String strID = ""; + + public Map flags = new HashMap<>(); + + public String getMsgType() { + return msgType; + } + + public void setMsgType(String msgType) { + this.msgType = msgType; + } + + public String getStrMsg() { + return strMsg; + } + + public void setStrMsg(String strMsg) { + this.strMsg = strMsg; + } + + public String getStrID() { + return strID; + } + + public void setStrID(String strID) { + this.strID = strID; + } + + public Map getFlags() { + return flags; + } + + public void setFlags(Map flags) { + this.flags = flags; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/mapnull/ParseResponse.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/mapnull/ParseResponse.java new file mode 100644 index 00000000000..8330fe8c9cc --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/mapnull/ParseResponse.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.demo.mapnull; + +import java.util.Map; + +public class ParseResponse { + public String resultCode = "99999999"; + + public String resultInfo = "unknown result"; + + public String msgType = ""; + + public Map msgHeader; + + public Map msgBody; + + public String getResultCode() { + return resultCode; + } + + public void setResultCode(String resultCode) { + this.resultCode = resultCode; + } + + public String getResultInfo() { + return resultInfo; + } + + public void setResultInfo(String resultInfo) { + this.resultInfo = resultInfo; + } + + public String getMsgType() { + return msgType; + } + + public void setMsgType(String msgType) { + this.msgType = msgType; + } + + public Map getMsgHeader() { + return msgHeader; + } + + public void setMsgHeader(Map msgHeader) { + this.msgHeader = msgHeader; + } + + public Map getMsgBody() { + return msgBody; + } + + public void setMsgBody(Map msgBody) { + this.msgBody = msgBody; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/model/SpecialNameModel.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/model/SpecialNameModel.java new file mode 100644 index 00000000000..dfacc030852 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/model/SpecialNameModel.java @@ -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. + */ + +package org.apache.servicecomb.demo.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class SpecialNameModel { + // names starts with only one lower case , although getter/setter generated by IDE is correct, + // will cause jackson generate incorrect swagger names. + // @JsonProperty must be used to make json work in a predictable way. + @JsonProperty("aIntName") + private int aIntName; + + public int getaIntName() { + return aIntName; + } + + public void setaIntName(int aIntName) { + this.aIntName = aIntName; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiRequest.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiRequest.java new file mode 100644 index 00000000000..738355a12cd --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiRequest.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.multiErrorCode; + +public class MultiRequest{ + private String message; + + private int code; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiResponse200.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiResponse200.java new file mode 100644 index 00000000000..34f60c7e5f2 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiResponse200.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.multiErrorCode; + +public class MultiResponse200 { + private String message; + + private int code; + + private long t200; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public long getT200() { + return t200; + } + + public void setT200(long t200) { + this.t200 = t200; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiResponse400.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiResponse400.java new file mode 100644 index 00000000000..af27dc1e445 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiResponse400.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.multiErrorCode; + +public class MultiResponse400 { + private String message; + + private int code; + + private long t400; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public long getT400() { + return t400; + } + + public void setT400(long t400) { + this.t400 = t400; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiResponse500.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiResponse500.java new file mode 100644 index 00000000000..379d1ba2a34 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/multiErrorCode/MultiResponse500.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.multiErrorCode; + +public class MultiResponse500 { + private String message; + + private int code; + + private long t500; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public long getT500() { + return t500; + } + + public void setT500(long t500) { + this.t500 = t500; + } +} 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 new file mode 100644 index 00000000000..17659cfed21 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/ProduceAppXmlProcessor.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.demo.produceprocessor; + +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +import jakarta.ws.rs.core.MediaType; + +import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; +import org.apache.servicecomb.demo.utils.JAXBUtils; + +import com.fasterxml.jackson.databind.JavaType; + +public class ProduceAppXmlProcessor implements ProduceProcessor { + + @Override + public String getName() { + return MediaType.APPLICATION_XML; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public void doEncodeResponse(OutputStream output, Object result) throws Exception { + output.write(JAXBUtils.convertToXml(result).getBytes(StandardCharsets.UTF_8)); + } + + @Override + public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { + return JAXBUtils.convertToJavaBean(input, type); + } + +} 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 new file mode 100644 index 00000000000..aebe13eb116 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/produceprocessor/override/ProduceAppXmlProcessor.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.demo.produceprocessor.override; + +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +import jakarta.ws.rs.core.MediaType; + +import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; +import org.apache.servicecomb.demo.utils.JAXBUtils; + +import com.fasterxml.jackson.databind.JavaType; + +public class ProduceAppXmlProcessor implements ProduceProcessor { + + @Override + public String getName() { + return MediaType.APPLICATION_XML; + } + + @Override + public int getOrder() { + return -1; + } + + @Override + public void doEncodeResponse(OutputStream output, Object result) throws Exception { + output.write(JAXBUtils.convertToXml(result).getBytes(StandardCharsets.UTF_8)); + } + + @Override + public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { + return JAXBUtils.convertToJavaBean(input, type); + } + +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/AbstractModel.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/AbstractModel.java new file mode 100644 index 00000000000..9cf13285e26 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/AbstractModel.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.demo.server; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + +// 当使用 List 等 Collection 类型的时候, jackson 序列化不会携带类以外的其他属性 +// 即会忽略掉类型信息。 当抽象类型用于 Collection 类型参数的时候,必须使用已有的属性(例子中的 property = "type")来确定类型, +// 并在子类中显示指定属性的值,而不能使用和依赖jackson根据类型信息自己生成的类型。 + +// 还有一种方法,是序列化的时候指定类型, see: https://www.studytrails.com/2016/09/12/java-jackson-serialization-list/ +// 但是多数序列化接口,包括 RestTemplate 等,都无法得知参数的类型。 这种方式难于应用于开发框架实现,因此对于Collection 类型参数场景,使用已有属性 +// 是最简洁的方法。 + +@JsonTypeInfo( + use = Id.NAME, property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = DefaultAbstractModel.class, name = "default"), + @JsonSubTypes.Type(value = SecondAbstractModel.class, name = "second"), +}) +public abstract class AbstractModel { + protected String type; + + protected String name; + + public abstract String getType(); + + public abstract String getName(); + + public abstract void setName(String name); +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/DefaultAbstractModel.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/DefaultAbstractModel.java new file mode 100644 index 00000000000..c9f06eeacf7 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/DefaultAbstractModel.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.server; + +public class DefaultAbstractModel extends AbstractModel { + @Override + public String getType() { + return "default"; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public void setName(String name) { + this.name = name; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/GenericsModel.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/GenericsModel.java new file mode 100644 index 00000000000..45853dbb1aa --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/GenericsModel.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.demo.server; + +import java.util.List; + +public class GenericsModel { + private String name; + + private List> nameList; + + private List>> objectLists; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List> getNameList() { + return nameList; + } + + public void setNameList(List> nameList) { + this.nameList = nameList; + } + + public List>> getObjectLists() { + return objectLists; + } + + public void setObjectLists(List>> objectLists) { + this.objectLists = objectLists; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/MapModel.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/MapModel.java new file mode 100644 index 00000000000..695310c1255 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/MapModel.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.server; + +import java.util.Map; + +public class MapModel { + private String name; + + private Map names; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map getNames() { + return names; + } + + public void setNames(Map names) { + this.names = names; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/NotRecommendedServiceInf.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/NotRecommendedServiceInf.java new file mode 100644 index 00000000000..f6be5ae58c1 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/NotRecommendedServiceInf.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.server; + +import java.util.List; +import java.util.Map; + +public interface NotRecommendedServiceInf { + Map longMap(Map map); + + List listAbstractModel(List listModel); + + AbstractModel abstractModel(AbstractModel model); + + Map mapAbstractModel(Map mapModel); + + WrappedAbstractModel wrappedAbstractModel(WrappedAbstractModel wrappedModel); +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/SecondAbstractModel.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/SecondAbstractModel.java new file mode 100644 index 00000000000..46a0b857fd4 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/SecondAbstractModel.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.server; + +public class SecondAbstractModel extends AbstractModel { + @Override + public String getType() { + return "second"; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public void setName(String name) { + this.name = name; + } +} 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 new file mode 100644 index 00000000000..0369d576073 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/Test.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.server; + +public interface Test { + String testStringArray(String[] arr); + + String getTestString(String code); + + String postTestStatic(int code); + + String patchTestStatic(int code); + + String testException(int code); + + User wrapParam(TestRequest request); + + User splitParam(int index, User user); + + String addString(String[] strArr); + + String testTraceId(); + + int[] testIntArray(int[] request); +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/TestRequest.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/TestRequest.java new file mode 100644 index 00000000000..6f622686271 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/TestRequest.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.demo.server; + +import java.util.ArrayList; +import java.util.List; + +public class TestRequest { + private int index; + + private User user; + + private List users = new ArrayList<>(); + + private byte[] data; + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/TestResponse.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/TestResponse.java new file mode 100644 index 00000000000..c6baa8e0078 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/TestResponse.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.server; + +public class TestResponse { + private User user; + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + @Override + public String toString() { + return "TestResponse [user=" + user + "]"; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/User.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/User.java new file mode 100644 index 00000000000..c55ed1e88c6 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/User.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.server; + +import org.apache.servicecomb.foundation.common.utils.JsonUtils; + +import com.fasterxml.jackson.core.JsonProcessingException; + +public class User { + private String name = "nameA"; + + private int age = 100; + + private int index; + + private String[] names; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String[] getNames() { + return names; + } + + public void setNames(String[] names) { + this.names = names; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + @Override + public String toString() { + return "User [name=" + name + ", age=" + age + ", index=" + index + "]"; + } + + public String jsonString() { + try { + return JsonUtils.writeValueAsString(this); + } catch (JsonProcessingException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/WrappedAbstractModel.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/WrappedAbstractModel.java new file mode 100644 index 00000000000..8015ccf54f6 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/WrappedAbstractModel.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.demo.server; + +import java.util.List; +import java.util.Map; + +public class WrappedAbstractModel { + private String name; + + private Map mapModel; + + private List listModel; + + private AbstractModel model; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map getMapModel() { + return mapModel; + } + + public void setMapModel(Map mapModel) { + this.mapModel = mapModel; + } + + public List getListModel() { + return listModel; + } + + public void setListModel(List listModel) { + this.listModel = listModel; + } + + public AbstractModel getModel() { + return model; + } + + public void setModel(AbstractModel model) { + this.model = model; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/Application.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/Application.java new file mode 100644 index 00000000000..88f7db4861b --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/Application.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.smartcare; + +import java.util.List; + +public class Application { + private String name; + + private String labelEN; + + private String labelCH; + + private String defaultGroup; + + private String version; + + private boolean dynamicFlag; + + private List groups; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLabelEN() { + return labelEN; + } + + public void setLabelEN(String labelEN) { + this.labelEN = labelEN; + } + + public String getLabelCH() { + return labelCH; + } + + public void setLabelCH(String labelCH) { + this.labelCH = labelCH; + } + + public String getDefaultGroup() { + return defaultGroup; + } + + public void setDefaultGroup(String defaultGroup) { + this.defaultGroup = defaultGroup; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public boolean isDynamicFlag() { + return dynamicFlag; + } + + public void setDynamicFlag(boolean dynamicFlag) { + this.dynamicFlag = dynamicFlag; + } + + public List getGroups() { + return groups; + } + + public void setGroups(List groups) { + this.groups = groups; + } + + @Override + public String toString() { + return "name=" + name + "\n" + + "version=" + version; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/Group.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/Group.java new file mode 100644 index 00000000000..8010d38a6e6 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/Group.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.smartcare; + +public class Group { + private String name; + + private String labelEN; + + private String labelCH; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLabelEN() { + return labelEN; + } + + public void setLabelEN(String labelEN) { + this.labelEN = labelEN; + } + + public String getLabelCH() { + return labelCH; + } + + public void setLabelCH(String labelCH) { + this.labelCH = labelCH; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/Response.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/Response.java new file mode 100644 index 00000000000..3e0a2867a10 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/Response.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.smartcare; + +public class Response { + private int resultCode; + + private String resultMessage; + + public int getResultCode() { + return resultCode; + } + + public void setResultCode(int resultCode) { + this.resultCode = resultCode; + } + + public String getResultMessage() { + return resultMessage; + } + + public void setResultMessage(String resultMessage) { + this.resultMessage = resultMessage; + } + + @Override + public String toString() { + return "resultCode: " + resultCode + "\n" + "resultMessage: " + resultMessage; + } +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/SmartCare.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/SmartCare.java new file mode 100644 index 00000000000..85008f9d75a --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/smartcare/SmartCare.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.smartcare; + +public interface SmartCare { + + Response addApplication(Application application); + + Response delApplication(String appName); +} 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 new file mode 100644 index 00000000000..7ff2518e8a3 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/utils/JAXBUtils.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.utils; + +import java.io.InputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; + +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.Source; +import javax.xml.transform.sax.SAXSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.InputSource; + +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); + + private JAXBUtils() { + } + + public static String convertToXml(Object obj) { + return convertToXml(obj, StandardCharsets.UTF_8.toString()); + } + + public static String convertToXml(Object obj, String encoding) { + String result = null; + try { + JAXBContext context = JAXBContext.newInstance(obj.getClass()); + Marshaller marshaller = context.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding); + + StringWriter writer = new StringWriter(); + marshaller.marshal(obj, writer); + result = writer.toString(); + } catch (Exception e) { + LOGGER.error("Bean convert to xml failed, error message: {}", e.getMessage(), e); + } + return result; + } + + @SuppressWarnings("unchecked") + public static T convertToJavaBean(InputStream xml, JavaType type) { + return (T) convertToJavaBean(xml, type.getRawClass()); + } + + @SuppressWarnings("unchecked") + public static T convertToJavaBean(InputStream xml, Class c) { + T t = null; + try { + JAXBContext context = JAXBContext.newInstance(c); + Unmarshaller unmarshaller = context.createUnmarshaller(); + t = (T) unmarshaller.unmarshal(XXEPrevention(xml)); + } catch (Exception e) { + LOGGER.error("Xml convert to Bean failed, error message: {}", e.getMessage()); + } + return t; + } + + private static Source XXEPrevention(InputStream xml) { + Source xmlSource = null; + SAXParserFactory spf = SAXParserFactory.newInstance(); + try { + spf.setFeature("http://xml.org/sax/features/external-general-entities", false); + spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(), new InputSource(xml)); + } catch (Exception e) { + LOGGER.error("Xml External Entity (XXE) Processing report error, error message: {}", e.getMessage()); + } + return xmlSource; + } +} 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 new file mode 100644 index 00000000000..3117c5e1f82 --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/validator/Student.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.demo.validator; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.NotNull; + +public class Student { + @NotNull + private String name; + + @Max(20) + private int age; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + public Student() { + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Student(String name, int age) { + this.name = name; + this.age = age; + } + + @Override + public String toString() { + return name + " " + age; + } +} 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 new file mode 100644 index 00000000000..b7015e2920e --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/validator/Teacher.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.demo.validator; + +import jakarta.validation.constraints.NotBlank; + +public class Teacher { + + @NotBlank(message = "must not be blank") + private String name; + + private String age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAge() { + return age; + } + + public void setAge(String age) { + this.age = age; + } + + @Override + public String toString() { + return name + " " + age; + } +} 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 new file mode 100644 index 00000000000..acd03890620 --- /dev/null +++ b/demo/demo-schema/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor @@ -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.demo.produceprocessor.ProduceAppXmlProcessor +org.apache.servicecomb.demo.produceprocessor.override.ProduceAppXmlProcessor diff --git a/demo/demo-schema/src/main/resources/config/log4j.eclipse.properties b/demo/demo-schema/src/main/resources/config/log4j.eclipse.properties deleted file mode 100644 index e8f7ef1ccac..00000000000 --- a/demo/demo-schema/src/main/resources/config/log4j.eclipse.properties +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=../../../../cse-demo-bin/logs/ \ No newline at end of file diff --git a/demo/demo-schema/src/main/resources/config/log4j.properties b/demo/demo-schema/src/main/resources/config/log4j.properties deleted file mode 100644 index 14aec88f691..00000000000 --- a/demo/demo-schema/src/main/resources/config/log4j.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=../logs/ -paas.logs.file=cse.log - -log4j.rootLogger=INFO,paas,stdout \ No newline at end of file diff --git a/demo/demo-schema/src/main/resources/microservice.yaml b/demo/demo-schema/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..28df63a4323 --- /dev/null +++ b/demo/demo-schema/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- +servicecomb-config-order: -1 + +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 eeca9e3a7c5..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/jaxrs/compute.yaml +++ /dev/null @@ -1,182 +0,0 @@ -# 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: io.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 - 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 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 2bd42957881..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/no-use/cse.basic.Basic/main.idl +++ /dev/null @@ -1,46 +0,0 @@ -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); -} \ No newline at end of file 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 1ef02b8c45c..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/no-use/hello/main.idl +++ /dev/null @@ -1,11 +0,0 @@ -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 ec995e14ec7..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/no-use/helloworld.Greeter/main.idl +++ /dev/null @@ -1,14 +0,0 @@ -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 b0a65f98775..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/no-use/helloworld.Standard/main.idl +++ /dev/null @@ -1,19 +0,0 @@ -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); -} \ No newline at end of file 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 92951fb15ae..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/no-use/server/main.idl +++ /dev/null @@ -1,82 +0,0 @@ -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; -} \ No newline at end of file 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 56deebc254e..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/pojo/helloworld.Greeter.yaml +++ /dev/null @@ -1,57 +0,0 @@ -# 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: io.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 \ No newline at end of file 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 9dc588e1cbb..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/pojo/server.yaml +++ /dev/null @@ -1,179 +0,0 @@ -# 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: io.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: true - 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: TestRequest - in: body - required: true - 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: io.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 \ No newline at end of file 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 e375ebd078f..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/pojo/smartcare.yaml +++ /dev/null @@ -1,118 +0,0 @@ -# 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: io.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: io.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 b6fdc0055cc..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/pojo/tcc-server.yaml +++ /dev/null @@ -1,91 +0,0 @@ -# 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: io.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: io.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 ea5afaca1cf..00000000000 --- a/demo/demo-schema/src/main/resources/microservices/springmvc/controller.yaml +++ /dev/null @@ -1,106 +0,0 @@ -# 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: io.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: /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 - -definitions: - Person: - type: object - properties: - name: - type: string diff --git a/demo/demo-server-servlet/pom.xml b/demo/demo-server-servlet/pom.xml deleted file mode 100644 index 0e31932cdc8..00000000000 --- a/demo/demo-server-servlet/pom.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.3.0-SNAPSHOT - - demo-server-servlet - war - - - - io.servicecomb.demo - pojo-server - - - - - pojo - - diff --git a/demo/demo-server-servlet/src/main/resources/microservice.yaml b/demo/demo-server-servlet/src/main/resources/microservice.yaml deleted file mode 100644 index b2ea15bfe61..00000000000 --- a/demo/demo-server-servlet/src/main/resources/microservice.yaml +++ /dev/null @@ -1 +0,0 @@ -servicecomb.rest.servlet.urlPattern: /rest/* \ No newline at end of file diff --git a/demo/demo-server-servlet/src/main/webapp/WEB-INF/web.xml b/demo/demo-server-servlet/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index e41b5ad2cbd..00000000000 --- a/demo/demo-server-servlet/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - contextConfigLocation - - classpath*:META-INF/spring/*.bean.xml - classpath*:app-config.xml - - - - - - - - io.servicecomb.transport.rest.servlet.RestServletContextListener - - - - - - - - - - - - - - - - - - diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-client/pom.xml b/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-client/pom.xml deleted file mode 100644 index 5cc5669927c..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-client/pom.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - 4.0.0 - - io.servicecomb.demo - demo-spring-boot-discovery - 0.3.0-SNAPSHOT - - demo-spring-boot-discovery-client - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - spring-boot-starter-discovery - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-run-config - 0.3.0-SNAPSHOT - - - - - - - - - - docker - - demo-spring-boot-discovery-server - -Dcse.rest.address=0.0.0.0:8080 - - - - - io.fabric8 - docker-maven-plugin - - - - - - diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-client/src/test/java/io/servicecomb/demo/discovery/client/DiscoveryClientIT.java b/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-client/src/test/java/io/servicecomb/demo/discovery/client/DiscoveryClientIT.java deleted file mode 100644 index e7439c5404a..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-client/src/test/java/io/servicecomb/demo/discovery/client/DiscoveryClientIT.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.servicecomb.demo.discovery.client; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; - -import java.net.URI; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.cloud.client.discovery.EnableDiscoveryClient; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.web.client.RestTemplate; - -import io.servicecomb.springboot.starter.provider.EnableServiceComb; - -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = RANDOM_PORT, classes = DiscoveryClientIT.DiscoveryTestApplication.class) -public class DiscoveryClientIT { - - @Autowired - private DiscoveryClient discoveryClient; - - // @Autowired - // private LoadBalancerClient client; - - private final RestTemplate restTemplate = new RestTemplate(); - - @Test - public void getsRemoteServiceFromDiscoveryClient() throws Exception { - URI remoteUri = discoveryClient.getInstances("discoveryServer").get(0).getUri(); - - assertThat(remoteUri).isNotNull(); - - String response = restTemplate.getForObject( - remoteUri.toString() + "/greeting/sayhello/{name}", - String.class, - "Mike"); - - assertThat(response).isEqualTo("hello Mike"); - } - - @SpringBootApplication - @EnableServiceComb - @EnableDiscoveryClient - static class DiscoveryTestApplication { - - public static void main(String[] args) throws Exception { - SpringApplication.run(DiscoveryTestApplication.class, args); - } - } -} diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-client/src/test/resources/microservice.yaml b/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-client/src/test/resources/microservice.yaml deleted file mode 100644 index 6ead75bf62e..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-client/src/test/resources/microservice.yaml +++ /dev/null @@ -1,11 +0,0 @@ -APPLICATION_ID: discoverytest -service_description: - name: discoveryClient - version: 0.0.2 -cse: - service: - registry: - address: http://127.0.0.1:9980 - references: - discoveryServer: - version-rule: 0.0.2 diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/pom.xml b/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/pom.xml deleted file mode 100644 index 363f62d2f56..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/pom.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - 4.0.0 - - io.servicecomb.demo - demo-spring-boot-discovery - 0.3.0-SNAPSHOT - - demo-spring-boot-discovery-server - demo-spring-boot-discovery-server - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-springmvc - - - - - io.servicecomb.demo.discovery.server.DiscoveryServer - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-build-config - 0.3.0-SNAPSHOT - - - - - - - - - - docker - - - - io.fabric8 - docker-maven-plugin - - - org.commonjava.maven.plugins - directory-maven-plugin - - - - - - diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/java/io/servicecomb/demo/discovery/server/DiscoveryServer.java b/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/java/io/servicecomb/demo/discovery/server/DiscoveryServer.java deleted file mode 100644 index eeae8d45b5f..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/java/io/servicecomb/demo/discovery/server/DiscoveryServer.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.discovery.server; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import io.servicecomb.springboot.starter.provider.EnableServiceComb; - -@SpringBootApplication -@EnableServiceComb -public class DiscoveryServer { - public static void main(String[] args) throws Exception { - SpringApplication.run(DiscoveryServer.class, args); - } -} diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/java/io/servicecomb/demo/discovery/server/GreetingController.java b/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/java/io/servicecomb/demo/discovery/server/GreetingController.java deleted file mode 100644 index 7084af55e99..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/java/io/servicecomb/demo/discovery/server/GreetingController.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.discovery.server; - -import javax.ws.rs.core.MediaType; - -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; - -import io.servicecomb.provider.rest.common.RestSchema; - -@RestSchema(schemaId = "greeting") -@RequestMapping(path = "/greeting", produces = MediaType.TEXT_PLAIN) -public class GreetingController { - - @RequestMapping(path = "/sayhello/{name}", method = RequestMethod.GET) - @ResponseBody - public String sayHello(@PathVariable("name") String name) { - return "hello " + name; - } -} diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/application.properties b/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/application.properties deleted file mode 100644 index ceff262d08f..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/application.properties +++ /dev/null @@ -1,2 +0,0 @@ -# temp file -spring.main.web-environment=true \ No newline at end of file diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/application.yaml b/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/application.yaml deleted file mode 100644 index a7afc92b751..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/application.yaml +++ /dev/null @@ -1,2 +0,0 @@ -server: - port: 8080 diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/log4j.properties b/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/log4j.properties deleted file mode 100644 index f182dab3b42..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/log4j.properties +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -##log4j DEBUG < INFO < WARN < ERROR < FATAL -log4j.rootLogger=INFO,paas,stdout -paas.logs.dir=./ -paas.logs.file=cse.log - -#if not set runLogger level ,log4Utils.init will meet a null point error -log4j.logger.runLogger=INFO - -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%p] %m %l%n - -log4j.appender.paas=io.servicecomb.foundation.common.utils.RollingFileAppenderExt -log4j.appender.paas.file=${paas.logs.dir}${paas.logs.file} -log4j.appender.paas.MaxFileSize=20MB -log4j.appender.paas.MaxBackupIndex=10 -log4j.appender.paas.layout=org.apache.log4j.PatternLayout -log4j.appender.paas.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss,SSS/zzz}][%t][%p]%m %l%n -log4j.appender.paas.logPermission=rw------- diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/microservice.yaml b/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/microservice.yaml deleted file mode 100644 index b25536309fc..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-discovery-server/src/main/resources/microservice.yaml +++ /dev/null @@ -1,15 +0,0 @@ -APPLICATION_ID: discoverytest -service_description: - name: discoveryServer - version: 0.0.2 -cse: - service: - registry: - address: http://127.0.0.1:9980 - rest: - address: 0.0.0.0:8080 - handler: - chain: - Provider: - default: bizkeeper-provider -servicecomb.rest.servlet.urlPattern: /* \ No newline at end of file diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/pom.xml b/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/pom.xml deleted file mode 100644 index 44d640a32a9..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/pom.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - 4.0.0 - - io.servicecomb.demo - demo-spring-boot-discovery - 0.3.0-SNAPSHOT - - demo-spring-boot-zuul-proxy - - io.servicecomb.demo.discovery.zuul.DiscoveryZuulProxyIT.DiscoveryZuulProxy - - - - org.springframework.cloud - spring-cloud-starter-zuul - - - org.springframework.cloud - spring-cloud-starter-ribbon - - - org.springframework.cloud - spring-cloud-starter-hystrix - - - io.servicecomb - spring-boot-starter-discovery - - - io.servicecomb.demo - demo-schema - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-run-config - 0.3.0-SNAPSHOT - - - - - - - - - - docker - - demo-spring-boot-discovery-server - -Dcse.rest.address=0.0.0.0:8080?sslEnabled=false - - - - - io.fabric8 - docker-maven-plugin - - - - - - diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/src/test/java/io/servicecomb/demo/discovery/zuul/DiscoveryZuulProxyIT.java b/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/src/test/java/io/servicecomb/demo/discovery/zuul/DiscoveryZuulProxyIT.java deleted file mode 100644 index 5625eb5cd71..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/src/test/java/io/servicecomb/demo/discovery/zuul/DiscoveryZuulProxyIT.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.discovery.zuul; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.cloud.client.discovery.EnableDiscoveryClient; -import org.springframework.cloud.netflix.zuul.EnableZuulProxy; -import org.springframework.test.context.junit4.SpringRunner; - -import io.servicecomb.springboot.starter.provider.EnableServiceComb; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = DiscoveryZuulProxyIT.DiscoveryZuulProxy.class, webEnvironment = RANDOM_PORT) -public class DiscoveryZuulProxyIT { - - @Autowired - private TestRestTemplate restTemplate; - - @Test - public void getsRemoteServiceThroughGateway() throws Exception { - String response = restTemplate.getForObject( - "/gateway/greeting/sayhello/{name}", - String.class, - "Mike"); - - assertThat(response).isEqualTo("hello Mike"); - } - - @SpringBootApplication - @EnableZuulProxy - @EnableDiscoveryClient - @EnableServiceComb - static class DiscoveryZuulProxy { - - public static void main(String[] args) throws Exception { - SpringApplication.run(DiscoveryZuulProxy.class, args); - } - } -} diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/src/test/resources/application.properties b/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/src/test/resources/application.properties deleted file mode 100644 index 3bfc5385f88..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/src/test/resources/application.properties +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -spring.main.web-environment=true \ No newline at end of file diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/src/test/resources/application.yml b/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/src/test/resources/application.yml deleted file mode 100644 index 244c753c8c4..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/src/test/resources/application.yml +++ /dev/null @@ -1,19 +0,0 @@ -server: - port: 0 -spring: - cloud: - cse: - host: 127.0.0.1 - port: 9980 -zuul: - routes: - gateway: - serviceId: discoveryServer - -hystrix: - command: - default: - execution: - isolation: - thread: - timeoutInMilliseconds: 5000 diff --git a/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/src/test/resources/microservice.yaml b/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/src/test/resources/microservice.yaml deleted file mode 100644 index a34e0e57109..00000000000 --- a/demo/demo-spring-boot-discovery/demo-spring-boot-zuul-proxy/src/test/resources/microservice.yaml +++ /dev/null @@ -1,10 +0,0 @@ -APPLICATION_ID: discoverytest -service_description: - name: discoveryGateway - version: 0.0.2 -cse: - service: - registry: - address: http://127.0.0.1:9980 - rest: - address: 0.0.0.0:8082 diff --git a/demo/demo-spring-boot-discovery/pom.xml b/demo/demo-spring-boot-discovery/pom.xml deleted file mode 100644 index 5cd041840d3..00000000000 --- a/demo/demo-spring-boot-discovery/pom.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.3.0-SNAPSHOT - - demo-spring-boot-discovery - pom - - demo-spring-boot-discovery-server - demo-spring-boot-discovery-client - demo-spring-boot-zuul-proxy - - - - - io.servicecomb - spring-boot-starter-provider - - - io.servicecomb - spring-boot-starter-transport - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-test - test - - - - - ch.qos.logback - logback-classic - - - org.slf4j - log4j-over-slf4j - - - diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/pom.xml b/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/pom.xml deleted file mode 100644 index 356dad75680..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/pom.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - 4.0.0 - demo-spring-boot-jaxrs-client - - io.servicecomb.demo - demo-spring-boot - 0.3.0-SNAPSHOT - - - - io.servicecomb.demo - jaxrs-client - - - org.springframework.cloud - spring-cloud-commons - - - org.springframework.cloud - spring-cloud-context - - - org.springframework.cloud - spring-cloud-netflix-core - - - - - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-run-config - 0.3.0-SNAPSHOT - - - - - - - - - docker - - demo-spring-boot-jaxrs-server - - -Dcse.highway.address=0.0.0.0:7070 - -Dcse.rest.address=0.0.0.0:8080 - - - - - - io.fabric8 - docker-maven-plugin - - - - - - diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/src/main/java/io/servicecomb/springboot/jaxrs/client/JaxrsClient.java b/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/src/main/java/io/servicecomb/springboot/jaxrs/client/JaxrsClient.java deleted file mode 100644 index d0ef768ab19..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/src/main/java/io/servicecomb/springboot/jaxrs/client/JaxrsClient.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.springboot.jaxrs.client; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.springboot.starter.provider.EnableServiceComb; - -@SpringBootApplication -@EnableServiceComb -public class JaxrsClient { - - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - SpringApplication.run(JaxrsClient.class, args); - - io.servicecomb.demo.jaxrs.client.JaxrsClient.run(); - TestMgr.summary(); - } -} diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/src/main/resources/application.yml b/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/src/main/resources/application.yml deleted file mode 100644 index 7a73057f7e5..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/src/main/resources/application.yml +++ /dev/null @@ -1,2 +0,0 @@ -server: - port: 8999 \ No newline at end of file diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/src/main/resources/microservice.yaml b/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/src/main/resources/microservice.yaml deleted file mode 100644 index 1e0e9bf6502..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/src/main/resources/microservice.yaml +++ /dev/null @@ -1,13 +0,0 @@ -cse-config-order: 1 -APPLICATION_ID: spring-boot-jaxrstest -service_description: - name: jaxrsClient - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:9980 - handler: - chain: - Consumer: - default: bizkeeper-consumer,loadbalance diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/src/test/java/io/servicecomb/springboot/jaxrs/SpringBootJaxrsIT.java b/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/src/test/java/io/servicecomb/springboot/jaxrs/SpringBootJaxrsIT.java deleted file mode 100644 index 16e95bef190..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-client/src/test/java/io/servicecomb/springboot/jaxrs/SpringBootJaxrsIT.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.servicecomb.springboot.jaxrs; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -import org.junit.Before; -import org.junit.Test; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.springboot.jaxrs.client.JaxrsClient; - -public class SpringBootJaxrsIT { - - @Before - public void setUp() throws Exception { - TestMgr.errors().clear(); - } - - @Test - public void clientGetsNoError() throws Exception { - JaxrsClient.main(new String[0]); - - assertThat(TestMgr.errors().isEmpty(), is(true)); - } -} diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-server/pom.xml b/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-server/pom.xml deleted file mode 100644 index 313330d194c..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-server/pom.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - 4.0.0 - demo-spring-boot-jaxrs-server - - io.servicecomb.demo - demo-spring-boot - 0.3.0-SNAPSHOT - - - io.servicecomb.springboot.jaxrs.server.JaxrsServer - - - - io.servicecomb.demo - jaxrs-server - - - org.springframework.cloud - spring-cloud-commons - - - org.springframework.cloud - spring-cloud-context - - - org.springframework.cloud - spring-cloud-netflix-core - - - - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-build-config - 0.3.0-SNAPSHOT - - - - - - - - - - docker - - - - io.fabric8 - docker-maven-plugin - - - org.commonjava.maven.plugins - directory-maven-plugin - - - - - - diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-server/src/main/java/io/servicecomb/springboot/jaxrs/server/JaxrsServer.java b/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-server/src/main/java/io/servicecomb/springboot/jaxrs/server/JaxrsServer.java deleted file mode 100644 index 04bae13dfed..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-server/src/main/java/io/servicecomb/springboot/jaxrs/server/JaxrsServer.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.springboot.jaxrs.server; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.springboot.starter.provider.EnableServiceComb; - -@SpringBootApplication -@EnableServiceComb -public class JaxrsServer { - public static void main(final String[] args) throws Exception { - Log4jUtils.init(); - SpringApplication.run(JaxrsServer.class, args); - } -} diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-server/src/main/resources/application.yml b/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-server/src/main/resources/application.yml deleted file mode 100644 index 46eac2f3cb3..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-server/src/main/resources/application.yml +++ /dev/null @@ -1,2 +0,0 @@ -server: - port: 0 \ No newline at end of file diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-server/src/main/resources/microservice.yaml b/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-server/src/main/resources/microservice.yaml deleted file mode 100644 index 5ba991df420..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-jaxrs-server/src/main/resources/microservice.yaml +++ /dev/null @@ -1,17 +0,0 @@ -cse-config-order: 1 -APPLICATION_ID: spring-boot-jaxrstest -service_description: - name: jaxrs - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:9980 - rest: - address: 0.0.0.0:8080 - highway: - address: 0.0.0.0:7070 - handler: - chain: - Provider: - default: bizkeeper-provider diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-client/pom.xml b/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-client/pom.xml deleted file mode 100644 index 1b2f50b141b..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-client/pom.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - 4.0.0 - io.servicecomb.springboot.springmvc.client - demo-spring-boot-springmvc-client - - io.servicecomb.demo - demo-spring-boot - 0.3.0-SNAPSHOT - - - - io.servicecomb.demo - springmvc-client - - - diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-client/src/main/java/io/servicecomb/springboot/springmvc/client/SpringmvcClient.java b/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-client/src/main/java/io/servicecomb/springboot/springmvc/client/SpringmvcClient.java deleted file mode 100644 index 09e8f54ebdb..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-client/src/main/java/io/servicecomb/springboot/springmvc/client/SpringmvcClient.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.springboot.springmvc.client; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.springboot.starter.provider.EnableServiceComb; - -/** - * SpringmvcClient - * - * - */ -@SpringBootApplication -@EnableServiceComb -public class SpringmvcClient { - - public static void main(final String[] args) throws Exception { - Log4jUtils.init(); - SpringApplication.run(SpringmvcClient.class, args); - - io.servicecomb.demo.springmvc.client.SpringmvcClient.run(); - - TestMgr.summary(); - } -} diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-client/src/main/resources/application.yml b/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-client/src/main/resources/application.yml deleted file mode 100644 index 7a73057f7e5..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-client/src/main/resources/application.yml +++ /dev/null @@ -1,2 +0,0 @@ -server: - port: 8999 \ No newline at end of file diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-server/pom.xml b/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-server/pom.xml deleted file mode 100644 index 6480af9b162..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-server/pom.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - 4.0.0 - io.servicecomb.springboot.springmvc.server - demo-spring-boot-springmvc-server - - io.servicecomb.demo - demo-spring-boot - 0.3.0-SNAPSHOT - - - - io.servicecomb.demo - springmvc-server - - - diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-server/src/main/java/io/servicecomb/springboot/springmvc/server/SpringmvcServer.java b/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-server/src/main/java/io/servicecomb/springboot/springmvc/server/SpringmvcServer.java deleted file mode 100644 index 4d0ce2f3d5c..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-server/src/main/java/io/servicecomb/springboot/springmvc/server/SpringmvcServer.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.springboot.springmvc.server; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.springboot.starter.provider.EnableServiceComb; - -@SpringBootApplication -@EnableServiceComb -public class SpringmvcServer { - - public static void main(final String[] args) throws Exception { - Log4jUtils.init(); - SpringApplication.run(SpringmvcServer.class, args); - } -} diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-server/src/main/resources/application.yml b/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-server/src/main/resources/application.yml deleted file mode 100644 index 24ec07e6102..00000000000 --- a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-server/src/main/resources/application.yml +++ /dev/null @@ -1,2 +0,0 @@ -server: - port: 7999 \ No newline at end of file diff --git a/demo/demo-spring-boot-provider/pom.xml b/demo/demo-spring-boot-provider/pom.xml deleted file mode 100644 index d547a1789c1..00000000000 --- a/demo/demo-spring-boot-provider/pom.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.3.0-SNAPSHOT - - demo-spring-boot - pom - - demo-spring-boot-jaxrs-server - demo-spring-boot-jaxrs-client - demo-spring-boot-springmvc-server - demo-spring-boot-springmvc-client - - - - - io.servicecomb - spring-boot-starter-provider - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-actuator - - - \ No newline at end of file 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 184905569db..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 @@ -1,12 +1,13 @@ - 4.0.0 - demo-spring-boot-pojo-client - - io.servicecomb.demo - demo-spring-boot-transport - 0.3.0-SNAPSHOT - - - - io.servicecomb.springboot.pojo.client.PojoClient - - - - org.springframework.cloud - spring-cloud-commons - - - org.springframework.cloud - spring-cloud-context - - - org.springframework.cloud - spring-cloud-netflix-core - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-run-config - 0.3.0-SNAPSHOT - - - - - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + demo-spring-boot-pojo-client + Java Chassis::Demo::Spring Boot::Transport::POJO Client + + org.apache.servicecomb.demo + demo-spring-boot-transport + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.springboot.pojo.client.PojoClient + - - - docker - - demo-spring-boot-pojo-server - -Dcse.rest.address=0.0.0.0:8080 - - - - - io.fabric8 - docker-maven-plugin - - - - - + + + docker + + 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 + + + + + diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/DemoConst.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/DemoConst.java deleted file mode 100644 index 2f864b87841..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/DemoConst.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo; - -import io.servicecomb.core.Const; - -public interface DemoConst { - String[] transports = new String[] {"rest", Const.ANY_TRANSPORT}; -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/TestMgr.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/TestMgr.java deleted file mode 100644 index 5f0748eb1d2..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/TestMgr.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo; - -import java.util.ArrayList; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class TestMgr { - private static final Logger LOGGER = LoggerFactory.getLogger(TestMgr.class); - - private static final List errorList = new ArrayList<>(); - - private static String msg = ""; - - public static void setMsg(String msg) { - TestMgr.msg = msg; - } - - public static void setMsg(String microserviceName, String transport) { - TestMgr.msg = String.format("microservice=%s, transport=%s", microserviceName, transport); - } - - public static void check(Object expect, Object real) { - String strExpect = String.valueOf(expect); - String strReal = String.valueOf(real); - - if (!strExpect.equals(strReal)) { - errorList.add(new Error(msg + " | Expect " + strExpect + ", but " + strReal)); - } - } - - public static void summary() { - if (errorList.isEmpty()) { - LOGGER.info("............. test finished ............"); - return; - } - - LOGGER.info("............. test not finished ............"); - for (Throwable e : errorList) { - LOGGER.info("", e); - } - } - - public static List errors() { - return errorList; - } -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/pojo/client/PojoClientTest.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/pojo/client/PojoClientTest.java deleted file mode 100644 index 55df15ae811..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/pojo/client/PojoClientTest.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.client; - -import java.util.Arrays; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import io.servicecomb.core.CseContext; -import io.servicecomb.core.provider.consumer.InvokerUtils; -import io.servicecomb.demo.DemoConst; -import io.servicecomb.demo.TestMgr; -import io.servicecomb.demo.server.Test; -import io.servicecomb.demo.server.TestRequest; -import io.servicecomb.demo.server.User; -import io.servicecomb.provider.pojo.RpcReference; - -@Component -public class PojoClientTest { - private static Logger LOGGER = LoggerFactory.getLogger(PojoClientTest.class); - - @RpcReference(microserviceName = "pojo") - public static Test test; - - public static Test testFromXml; - - public static final byte buffer[] = new byte[1024]; - - static { - Arrays.fill(buffer, (byte) 1); - } - - public static void setTestFromXml(Test testFromXml) { - PojoClientTest.testFromXml = testFromXml; - } - - public static void runTest() throws Exception { - String microserviceName = "pojo"; - - for (String transport : DemoConst.transports) { - CseContext.getInstance().getConsumerProviderManager().setTransport(microserviceName, transport); - TestMgr.setMsg(microserviceName, transport); - LOGGER.info("test {}, transport {}", microserviceName, transport); - - testNull(testFromXml); - testNull(test); - testEmpty(test); - testStringArray(test); - testChinese(test); - testStringHaveSpace(test); - testWrapParam(test); - testSplitParam(test); - testInputArray(test); - - testCommonInvoke(transport); - } - TestMgr.summary(); - } - - 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); - } - - private static void testCommonInvoke(String transport) { - Object result = InvokerUtils.syncInvoke("pojo", "server", "splitParam", new Object[] {2, new User()}); - TestMgr.check("User [name=nameA, users count:0, age=100, index=2]", result); - - result = - InvokerUtils.syncInvoke("pojo", - "0.0.1", - transport, - "server", - "splitParam", - new Object[] {3, new User()}); - TestMgr.check("User [name=nameA, users count:0, age=100, index=3]", result); - } - - private static void testEmpty(Test test) { - TestMgr.check("code is ''", test.getTestString("")); - } - - private static void testNull(Test test) { - TestMgr.check("code is 'null'", test.getTestString(null)); - TestMgr.check(null, test.wrapParam(null)); - } - - 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-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/server/Test.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/server/Test.java deleted file mode 100644 index 4466a4ed53c..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/server/Test.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.server; - -public interface Test { - String testStringArray(String[] arr); - - String getTestString(String code); - - String postTestStatic(int code); - - String testException(int code); - - User wrapParam(TestRequest request); - - User splitParam(int index, User user); - - String addString(String[] strArr); -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/server/TestRequest.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/server/TestRequest.java deleted file mode 100644 index ef79c1977fe..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/server/TestRequest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.server; - -import java.util.ArrayList; -import java.util.List; - -public class TestRequest { - private int index; - - private User user; - - private List users = new ArrayList<>(); - - private byte[] data; - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - - public List getUsers() { - return users; - } - - public void setUsers(List users) { - this.users = users; - } - - public byte[] getData() { - return data; - } - - public void setData(byte[] data) { - this.data = data; - } -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/server/TestResponse.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/server/TestResponse.java deleted file mode 100644 index 82dec9dbd20..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/server/TestResponse.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.server; - -public class TestResponse { - private User user; - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - - @Override - public String toString() { - return "TestResponse [user=" + user + "]"; - } -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/server/User.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/server/User.java deleted file mode 100644 index be75ead0d80..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/demo/server/User.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.server; - -public class User { - private String name = "nameA"; - - private int age = 100; - - private int index; - - private String[] names; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String[] getNames() { - return names; - } - - public void setNames(String[] names) { - this.names = names; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - @Override - public String toString() { - return "User [name=" + name + ", age=" + age + ", index=" + index + "]"; - } -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/springboot/pojo/client/PojoClient.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/springboot/pojo/client/PojoClient.java deleted file mode 100644 index d4904af2b64..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/io/servicecomb/springboot/pojo/client/PojoClient.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.springboot.pojo.client; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.springboot.starter.provider.EnableServiceComb; - -/** - * SpringmvcClient - * - * - */ -@SpringBootApplication -@EnableServiceComb -public class PojoClient { - - public static void main(final String[] args) throws Exception { - Log4jUtils.init(); - SpringApplication.run(PojoClient.class, args); - - io.servicecomb.demo.pojo.client.PojoClientTest.runTest(); - } -} 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 new file mode 100644 index 00000000000..eb60d1e9d83 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/DemoConst.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.demo.springboot.pojo.client; + +import org.apache.servicecomb.core.CoreConst; + +public interface DemoConst { + 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 new file mode 100644 index 00000000000..b5a590b65e7 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClient.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.demo.springboot.pojo.client; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.annotation.ImportResource; + +/** + * SpringmvcClient + * + * + */ +@SpringBootApplication +@ImportResource(value = "classpath*:META-INF/spring/*.bean.xml") +public class PojoClient { + + public static void main(final String[] args) throws Exception { + new SpringApplicationBuilder().sources(PojoClient.class).web(WebApplicationType.SERVLET).build().run(args); + + PojoClientTest.runTest(); + } +} 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 new file mode 100644 index 00000000000..4e0525a1230 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientTest.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.demo.springboot.pojo.client; + +import java.util.Arrays; +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.provider.pojo.RpcReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class PojoClientTest { + private static Logger LOGGER = LoggerFactory.getLogger(PojoClientTest.class); + + @RpcReference(microserviceName = "spring-boot-pojo-server", schemaId = "server") + public static Test test; + + private static Test testFromXml; + + public static final byte[] buffer = new byte[1024]; + + public static final String SPLITPARAM_RESPONSE_USER_SUFFIX = "(modified by MyHandler)"; + + static { + Arrays.fill(buffer, (byte) 1); + } + + public static void setTestFromXml(Test testFromXml) { + PojoClientTest.testFromXml = testFromXml; + } + + public static void runTest() throws Exception { + String microserviceName = "spring-boot-pojo-server"; + + for (String transport : DemoConst.transports) { + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); + TestMgr.setMsg(microserviceName, transport); + LOGGER.info("test {}, transport {}", microserviceName, transport); + + testNull(testFromXml); + testNull(test); + testEmpty(test); + testStringArray(test); + testChinese(test); + testStringHaveSpace(test); + testWrapParam(test); + testSplitParam(test); + testInputArray(test); + + testCommonInvoke(transport); + } + TestMgr.summary(); + } + + 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" + SPLITPARAM_RESPONSE_USER_SUFFIX + + ", age=100, index=1]", + result); + } + + @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("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("spring-boot-pojo-server", + transport, + "server", + "splitParam", + wrap, User.class); + TestMgr.check("User [name=nameA, users count:0" + SPLITPARAM_RESPONSE_USER_SUFFIX + + ", age=100, index=3]", result); + } + + private static void testEmpty(Test test) { + TestMgr.check("code is ''", test.getTestString("")); + } + + private static void testNull(Test test) { + TestMgr.check("code is 'null'", test.getTestString(null)); + TestMgr.check(null, test.wrapParam(null)); + } + + 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" + SPLITPARAM_RESPONSE_USER_SUFFIX, 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-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/TestMgr.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/TestMgr.java new file mode 100644 index 00000000000..f0505643417 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/TestMgr.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.demo.springboot.pojo.client; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestMgr { + private static final Logger LOGGER = LoggerFactory.getLogger(TestMgr.class); + + private static final List errorList = new ArrayList<>(); + + private static String msg = ""; + + public static void setMsg(String msg) { + TestMgr.msg = msg; + } + + public static void setMsg(String microserviceName, String transport) { + TestMgr.msg = String.format("microservice=%s, transport=%s", microserviceName, transport); + } + + public static void check(Object expect, Object real) { + String strExpect = String.valueOf(expect); + String strReal = String.valueOf(real); + + if (!strExpect.equals(strReal)) { + errorList.add(new Error(msg + " | Expect " + strExpect + ", but " + strReal)); + } + } + + public static void summary() { + if (errorList.isEmpty()) { + LOGGER.info("............. test finished ............"); + return; + } + + LOGGER.info("............. test not finished ............"); + for (Throwable e : errorList) { + LOGGER.info("", e); + } + } + + public static List errors() { + return errorList; + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/Test.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/Test.java new file mode 100644 index 00000000000..6542be49ffc --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/Test.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.springboot.pojo.server.schema.server; + +public interface Test { + String testStringArray(String[] arr); + + String getTestString(String code); + + String postTestStatic(int code); + + String testException(int code); + + User wrapParam(TestRequest request); + + User splitParam(int index, User user); + + String addString(String[] strArr); +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestRequest.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestRequest.java new file mode 100644 index 00000000000..2b0aeeac41e --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestRequest.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.demo.springboot.pojo.server.schema.server; + +import java.util.ArrayList; +import java.util.List; + +public class TestRequest { + private int index; + + private User user; + + private List users = new ArrayList<>(); + + private byte[] data; + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestResponse.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestResponse.java new file mode 100644 index 00000000000..290ab4e9d58 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestResponse.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.springboot.pojo.server.schema.server; + +public class TestResponse { + private User user; + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + @Override + public String toString() { + return "TestResponse [user=" + user + "]"; + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/User.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/User.java new file mode 100644 index 00000000000..a46291ed9ff --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/User.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.demo.springboot.pojo.server.schema.server; + +public class User { + private String name = "nameA"; + + private int age = 100; + + private int index; + + private String[] names; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String[] getNames() { + return names; + } + + public void setNames(String[] names) { + this.names = names; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + @Override + public String toString() { + return "User [name=" + name + ", age=" + age + ", index=" + index + "]"; + } +} 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 98a273b5137..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 @@ -1,12 +1,13 @@ - - - - - - \ No newline at end of file + + + + + + 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 new file mode 100644 index 00000000000..1d901a66c99 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/application.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. +## --------------------------------------------------------------------------- + +server: + port: 8082 + +servicecomb: + service: + application: spring-boot-pojotest + name: spring-boot-pojo-client + version: 0.0.1 + registry: + sc: + address: http://127.0.0.1:30100 + handler: + chain: + Consumer: + default: loadbalance + isolation: + Consumer: + enabled: false + references: + pojo: + version-rule: 0.0.1 diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/microservice.yaml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/microservice.yaml deleted file mode 100644 index b35b1eb35a4..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/microservice.yaml +++ /dev/null @@ -1,19 +0,0 @@ -APPLICATION_ID: spring-boot-pojotest -service_description: - name: pojoClient - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:9980 - handler: - chain: - Consumer: - #default: loadbalance - default: bizkeeper-consumer,loadbalance - isolation: - Consumer: - enabled: false - references: - pojo: - version-rule: 0.0.1 \ No newline at end of file diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/test/java/io/servicecomb/springboot/pojo/client/PojoClientIT.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/test/java/io/servicecomb/springboot/pojo/client/PojoClientIT.java deleted file mode 100644 index f81239d755d..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/test/java/io/servicecomb/springboot/pojo/client/PojoClientIT.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.servicecomb.springboot.pojo.client; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.boot.SpringApplication; - -import io.servicecomb.demo.TestMgr; - -public class PojoClientIT { - - @Before - public void setUp() throws Exception { - TestMgr.errors().clear(); - } - - @Test - public void clientGetsNoError() throws Exception { - SpringApplication.run(PojoClient.class); - - assertThat(TestMgr.errors().isEmpty(), is(true)); - } -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/test/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientIT.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/test/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientIT.java new file mode 100644 index 00000000000..1312a72739b --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/test/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientIT.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.demo.springboot.pojo.client; + +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 = PojoClient.class) +public class PojoClientIT { + + @BeforeEach + public void setUp() throws Exception { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + PojoClientTest.runTest(); + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} 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 e1b68787967..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 @@ -1,12 +1,13 @@ - 4.0.0 - demo-spring-boot-pojo-server - - io.servicecomb.demo - demo-spring-boot-transport - 0.3.0-SNAPSHOT - - - - io.servicecomb.springboot.pojo.server.PojoServer - - - - org.springframework.cloud - spring-cloud-commons - - - org.springframework.cloud - spring-cloud-context - - - org.springframework.cloud - spring-cloud-netflix-core - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-build-config - 0.3.0-SNAPSHOT - - - - - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + demo-spring-boot-pojo-server + Java Chassis::Demo::Spring Boot::Transport::POJO Server + + org.apache.servicecomb.demo + demo-spring-boot-transport + 3.4.0-SNAPSHOT + + + + org.apache.servicecomb.demo.springboot.pojo.server.PojoServer + + + + + jakarta.ws.rs + jakarta.ws.rs-api + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + - - - docker - - - - io.fabric8 - docker-maven-plugin - - - org.commonjava.maven.plugins - directory-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-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/helloworld/greeter/Hello.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/helloworld/greeter/Hello.java deleted file mode 100644 index 07aac5b7e52..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/helloworld/greeter/Hello.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.helloworld.greeter; - -public interface Hello { - public String SayHello(String name); - - public String SayHelloAgain(String name); -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/pojo/server/HelloImpl.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/pojo/server/HelloImpl.java deleted file mode 100644 index 20f044a4455..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/pojo/server/HelloImpl.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.server; - -import io.servicecomb.demo.helloworld.greeter.Hello; - -public class HelloImpl implements Hello { - - @Override - public String SayHello(String name) { - return "Hello Message fast"; - } - - @Override - public String SayHelloAgain(String name) { - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return "Hello Message slow"; - } -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/pojo/server/TestImpl.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/pojo/server/TestImpl.java deleted file mode 100644 index 88d57ed6307..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/pojo/server/TestImpl.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.server; - -import java.util.Arrays; -import java.util.List; - -import io.servicecomb.demo.server.Test; -import io.servicecomb.demo.server.TestRequest; -import io.servicecomb.demo.server.User; -import io.servicecomb.provider.pojo.RpcSchema; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -@RpcSchema(schemaId = "server") -public class TestImpl implements Test { - @Override - public String testStringArray(String[] arr) { - return String.format("arr is '%s'", Arrays.toString(arr)); - } - - @Override - public String getTestString(String code) { - return String.format("code is '%s'", String.valueOf(code)); - } - - @Override - public String postTestStatic(int code) { - return null; - } - - private User doTest(int index, User user, List users, byte[] data) { - if (user == null) { - user = new User(); - } - - user.setIndex(index); - - int userCount = (users == null) ? 0 : users.size(); - user.setName(user.getName() + ", users count:" + userCount); - return user; - } - - @Override - public String testException(int code) { - String strCode = String.valueOf(code); - switch (code) { - case 200: - return strCode; - case 456: - throw new InvocationException(code, strCode, strCode + " error"); - case 556: - throw new InvocationException(code, strCode, Arrays.asList(strCode + " error")); - case 557: - throw new InvocationException(code, strCode, Arrays.asList(Arrays.asList(strCode + " error"))); - default: - break; - } - - return "not expected"; - } - - @Override - public User splitParam(int index, User user) { - return doTest(index, user, null, null); - } - - @Override - public User wrapParam(TestRequest request) { - if (request == null) { - return null; - } - return doTest(request.getIndex(), request.getUser(), request.getUsers(), request.getData()); - } - - @Override - public String addString(String[] strArr) { - String result = Arrays.toString(strArr); - System.out.println("addString: " + result); - return result; - } -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/server/Test.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/server/Test.java deleted file mode 100644 index 4466a4ed53c..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/server/Test.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.server; - -public interface Test { - String testStringArray(String[] arr); - - String getTestString(String code); - - String postTestStatic(int code); - - String testException(int code); - - User wrapParam(TestRequest request); - - User splitParam(int index, User user); - - String addString(String[] strArr); -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/server/TestRequest.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/server/TestRequest.java deleted file mode 100644 index ef79c1977fe..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/server/TestRequest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.server; - -import java.util.ArrayList; -import java.util.List; - -public class TestRequest { - private int index; - - private User user; - - private List users = new ArrayList<>(); - - private byte[] data; - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - - public List getUsers() { - return users; - } - - public void setUsers(List users) { - this.users = users; - } - - public byte[] getData() { - return data; - } - - public void setData(byte[] data) { - this.data = data; - } -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/server/TestResponse.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/server/TestResponse.java deleted file mode 100644 index 82dec9dbd20..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/server/TestResponse.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.server; - -public class TestResponse { - private User user; - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - - @Override - public String toString() { - return "TestResponse [user=" + user + "]"; - } -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/server/User.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/server/User.java deleted file mode 100644 index be75ead0d80..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/demo/server/User.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.server; - -public class User { - private String name = "nameA"; - - private int age = 100; - - private int index; - - private String[] names; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String[] getNames() { - return names; - } - - public void setNames(String[] names) { - this.names = names; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - @Override - public String toString() { - return "User [name=" + name + ", age=" + age + ", index=" + index + "]"; - } -} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/springboot/pojo/server/PojoServer.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/springboot/pojo/server/PojoServer.java deleted file mode 100644 index 08a344484f4..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/io/servicecomb/springboot/pojo/server/PojoServer.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.springboot.pojo.server; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.springboot.starter.provider.EnableServiceComb; - -@SpringBootApplication -@EnableServiceComb -public class PojoServer { - - public static void main(final String[] args) throws Exception { - Log4jUtils.init(); - SpringApplication.run(PojoServer.class, args); - } -} 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 new file mode 100644 index 00000000000..30af50bf5f5 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/PojoServer.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.springboot.pojo.server; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class PojoServer { + + public static void main(final String[] args) throws Exception { + new SpringApplicationBuilder().sources(PojoServer.class).web(WebApplicationType.SERVLET).build().run(args); + } +} 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 new file mode 100644 index 00000000000..09d2e2eb06d --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/handler/MyHandler.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springboot.pojo.server.handler; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.demo.springboot.pojo.server.schema.server.User; +import org.apache.servicecomb.swagger.invocation.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +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() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 100; + } + + @Override + public String getName() { + return "test-my-filter"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + LOGGER.info("If you see this log, that means this demo project has been converted to ServiceComb framework."); + + return nextNode.onFilter(invocation).whenComplete((response, throwable) -> { + if (invocation.getOperationMeta().getSchemaQualifiedName().equals("server.splitParam")) { + User user = response.getResult(); + user.setName(user.getName() + SPLITPARAM_RESPONSE_USER_SUFFIX); + } + }); + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/greeter/Hello.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/greeter/Hello.java new file mode 100644 index 00000000000..7279c9fede1 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/greeter/Hello.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.demo.springboot.pojo.server.schema.greeter; + +public interface Hello { + String SayHello(String name); + + String SayHelloAgain(String name); +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/HelloImpl.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/HelloImpl.java new file mode 100644 index 00000000000..4bf9381a738 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/HelloImpl.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.springboot.pojo.server.schema.server; + +import org.apache.servicecomb.demo.springboot.pojo.server.schema.greeter.Hello; +import org.apache.servicecomb.provider.pojo.RpcSchema; + +@RpcSchema(schemaId = "helloworld.Greeter") +public class HelloImpl implements Hello { + + @Override + public String SayHello(String name) { + return "Hello Message fast"; + } + + @Override + public String SayHelloAgain(String name) { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return "Hello Message slow"; + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/Test.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/Test.java new file mode 100644 index 00000000000..6542be49ffc --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/Test.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.springboot.pojo.server.schema.server; + +public interface Test { + String testStringArray(String[] arr); + + String getTestString(String code); + + String postTestStatic(int code); + + String testException(int code); + + User wrapParam(TestRequest request); + + User splitParam(int index, User user); + + String addString(String[] strArr); +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestImpl.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestImpl.java new file mode 100644 index 00000000000..6b9622a0804 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestImpl.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.demo.springboot.pojo.server.schema.server; + +import java.util.Arrays; +import java.util.List; + +import org.apache.servicecomb.provider.pojo.RpcSchema; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +@RpcSchema(schemaId = "server") +public class TestImpl implements Test { + @Override + public String testStringArray(String[] arr) { + return String.format("arr is '%s'", Arrays.toString(arr)); + } + + @Override + public String getTestString(String code) { + return String.format("code is '%s'", code); + } + + @Override + public String postTestStatic(int code) { + return null; + } + + private User doTest(int index, User user, List users, byte[] data) { + if (user == null) { + user = new User(); + } + + user.setIndex(index); + + int userCount = (users == null) ? 0 : users.size(); + user.setName(user.getName() + ", users count:" + userCount); + return user; + } + + @Override + public String testException(int code) { + String strCode = String.valueOf(code); + switch (code) { + case 200: + return strCode; + case 456: + throw new InvocationException(code, strCode, strCode + " error"); + case 556: + throw new InvocationException(code, strCode, Arrays.asList(strCode + " error")); + case 557: + throw new InvocationException(code, strCode, Arrays.asList(Arrays.asList(strCode + " error"))); + default: + break; + } + + return "not expected"; + } + + @Override + public User splitParam(int index, User user) { + return doTest(index, user, null, null); + } + + @Override + public User wrapParam(TestRequest request) { + if (request == null) { + return null; + } + return doTest(request.getIndex(), request.getUser(), request.getUsers(), request.getData()); + } + + @Override + public String addString(String[] strArr) { + String result = Arrays.toString(strArr); + System.out.println("addString: " + result); + return result; + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestRequest.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestRequest.java new file mode 100644 index 00000000000..2b0aeeac41e --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestRequest.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.demo.springboot.pojo.server.schema.server; + +import java.util.ArrayList; +import java.util.List; + +public class TestRequest { + private int index; + + private User user; + + private List users = new ArrayList<>(); + + private byte[] data; + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestResponse.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestResponse.java new file mode 100644 index 00000000000..290ab4e9d58 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/TestResponse.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.springboot.pojo.server.schema.server; + +public class TestResponse { + private User user; + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + @Override + public String toString() { + return "TestResponse [user=" + user + "]"; + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/User.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/User.java new file mode 100644 index 00000000000..a46291ed9ff --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/schema/server/User.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.demo.springboot.pojo.server.schema.server; + +public class User { + private String name = "nameA"; + + private int age = 100; + + private int index; + + private String[] names; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String[] getNames() { + return names; + } + + public void setNames(String[] names) { + this.names = names; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + @Override + public String toString() { + return "User [name=" + name + ", age=" + age + ", index=" + index + "]"; + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/META-INF/spring/pojo.server.bean.xml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/META-INF/spring/pojo.server.bean.xml deleted file mode 100644 index b152677cc9d..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/META-INF/spring/pojo.server.bean.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - \ No newline at end of file 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 new file mode 100644 index 00000000000..e7c0ccf1da0 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-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: 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/microservice.yaml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservice.yaml deleted file mode 100644 index 1f9ddd00b08..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservice.yaml +++ /dev/null @@ -1,10 +0,0 @@ -APPLICATION_ID: spring-boot-pojotest -service_description: - name: pojo - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:9980 - 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 56deebc254e..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/helloworld.Greeter.yaml +++ /dev/null @@ -1,57 +0,0 @@ -# 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: io.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 \ No newline at end of file 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 9dc588e1cbb..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/server.yaml +++ /dev/null @@ -1,179 +0,0 @@ -# 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: io.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: true - 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: TestRequest - in: body - required: true - 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: io.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 \ 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 e375ebd078f..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/smartcare.yaml +++ /dev/null @@ -1,118 +0,0 @@ -# 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: io.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: io.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 b6fdc0055cc..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/tcc-server.yaml +++ /dev/null @@ -1,91 +0,0 @@ -# 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: io.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: io.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 new file mode 100644 index 00000000000..edd73c1428b --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/pom.xml @@ -0,0 +1,131 @@ + + + + + 4.0.0 + + demo-spring-boot-springmvc-client + Java Chassis::Demo::Spring Boot::Spring MVC Client + + org.apache.servicecomb.demo + demo-spring-boot-transport + 3.4.0-SNAPSHOT + + + + + org.apache.servicecomb.demo + demo-schema + + + + + docker + + 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 + + + + + + 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 new file mode 100644 index 00000000000..f7e066497d1 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/PlaceHolderSchemaTest.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.springboot.springmvc.client; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; + +@Component +public class PlaceHolderSchemaTest implements CategorizedTestCase { + @Override + public void testRestTransport() throws Exception { + 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 new file mode 100644 index 00000000000..8ce77d9818e --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/SpringMvcClient.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.springboot.springmvc.client; + +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; + +/** + * SpringMvcClient + * + * + */ +@SpringBootApplication +public class SpringMvcClient { + + public static void main(final String[] args) throws Exception { + + new SpringApplicationBuilder().sources(SpringMvcClient.class).web(WebApplicationType.SERVLET).build().run(args); + + runTest(); + } + + public static void runTest() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("springmvc"); + + TestMgr.summary(); + } +} 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 new file mode 100644 index 00000000000..68ab69a48cb --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/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: 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-provider/demo-spring-boot-springmvc-client/src/main/resources/certificates/server.p12 b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/resources/certificates/server.p12 similarity index 100% rename from demo/demo-spring-boot-provider/demo-spring-boot-springmvc-client/src/main/resources/certificates/server.p12 rename to demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/resources/certificates/server.p12 diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-client/src/main/resources/certificates/trust.jks b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/resources/certificates/trust.jks similarity index 100% rename from demo/demo-spring-boot-provider/demo-spring-boot-springmvc-client/src/main/resources/certificates/trust.jks rename to demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/resources/certificates/trust.jks diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/resources/microservice.yaml b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..587c6bbf55a --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- +servicecomb-config-order: 100 + +3rd-svc: + urls: + - rest://localhost:8080?sslEnabled=false diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/test/java/org/apache/servicecomb/springboot/springmvc/client/SpringMvcClientIT.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/test/java/org/apache/servicecomb/springboot/springmvc/client/SpringMvcClientIT.java new file mode 100644 index 00000000000..ae3ef5b0044 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/test/java/org/apache/servicecomb/springboot/springmvc/client/SpringMvcClientIT.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.client; + +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 = SpringMvcClient.class) +public class SpringMvcClientIT { + @BeforeEach + public void setUp() throws Exception { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + try { + SpringMvcClient.runTest(); + + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } catch (Throwable e) { + e.printStackTrace(); + Assertions.fail("test case failed, message=" + e.getMessage()); + } + } +} 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 new file mode 100644 index 00000000000..efa0723b59a --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/pom.xml @@ -0,0 +1,98 @@ + + + + + 4.0.0 + + demo-spring-boot-springmvc-server + Java Chassis::Demo::Spring Boot::Spring MVC Server + + org.apache.servicecomb.demo + demo-spring-boot-transport + 3.4.0-SNAPSHOT + + + + 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-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/PlaceHolderSchema.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/PlaceHolderSchema.java new file mode 100644 index 00000000000..bb75e8e8a9a --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/PlaceHolderSchema.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.springboot.springmvc.server; + +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 = "PlaceHolderSchema") +@RequestMapping(path = "${test.placeholder.schema}") +public class PlaceHolderSchema { + @GetMapping(path = "/") + public String root(@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 new file mode 100644 index 00000000000..acbd39e0cc4 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/SpringmvcServer.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.springboot.springmvc.server; + +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication(exclude = {WebMvcAutoConfiguration.class}) +public class SpringmvcServer { + private static final Logger LOGGER = LoggerFactory.getLogger(SpringmvcServer.class); + + public static void main(final String[] args) throws Exception { + new SpringApplicationBuilder().sources(SpringmvcServer.class).web(WebApplicationType.SERVLET).build().run(args); + + assertPropertyCorrect(); + } + + private static void assertPropertyCorrect() { + // 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 new file mode 100644 index 00000000000..7cd484f51bb --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/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. +## --------------------------------------------------------------------------- + +server: + port: 8080 + +test.unresolved.placeholder: jdbc:postgresql://${ip}:${port}/pt +test: + placeholder: + 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-provider/demo-spring-boot-springmvc-server/src/main/resources/certificates/server.p12 b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/resources/certificates/server.p12 similarity index 100% rename from demo/demo-spring-boot-provider/demo-spring-boot-springmvc-server/src/main/resources/certificates/server.p12 rename to demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/resources/certificates/server.p12 diff --git a/demo/demo-spring-boot-provider/demo-spring-boot-springmvc-server/src/main/resources/certificates/trust.jks b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/resources/certificates/trust.jks similarity index 100% rename from demo/demo-spring-boot-provider/demo-spring-boot-springmvc-server/src/main/resources/certificates/trust.jks rename to demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/resources/certificates/trust.jks diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/resources/microservice.yaml b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..c3738a1dd60 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/resources/microservice.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. +## --------------------------------------------------------------------------- + +servicecomb: + config: + ignoreResolveFailure: true + + service: + registry: + registerPath: true + 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 + uploads: + directory: target + + rest: + address: 0.0.0.0:8080?sslEnabled=false diff --git a/demo/demo-spring-boot-transport/pom.xml b/demo/demo-spring-boot-transport/pom.xml index 254615576b4..bd7650a3f26 100644 --- a/demo/demo-spring-boot-transport/pom.xml +++ b/demo/demo-spring-boot-transport/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.3.0-SNAPSHOT - - demo-spring-boot-transport - pom - - demo-spring-boot-pojo-server - demo-spring-boot-pojo-client - + 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.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-spring-boot-transport + Java Chassis::Demo::Spring Boot::Transport + pom + + demo-spring-boot-springmvc-server + demo-spring-boot-springmvc-client + demo-spring-boot-pojo-server + demo-spring-boot-pojo-client + + + + + org.apache.servicecomb + solution-basic + + + org.apache.servicecomb + java-chassis-spring-boot-starter-servlet + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + - - - io.servicecomb - spring-boot-starter-provider - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-actuator - - + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + foundation-test-scaffolding + compile + + diff --git a/demo/demo-springmvc/pom.xml b/demo/demo-springmvc/pom.xml index 73201021a35..c307aa7741b 100644 --- a/demo/demo-springmvc/pom.xml +++ b/demo/demo-springmvc/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-parent - 0.3.0-SNAPSHOT - - demo-springmvc - pom - - springmvc-server - springmvc-client + 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.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-springmvc + Java Chassis::Demo::Spring MVC + pom + + springmvc-server + 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 + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + diff --git a/demo/demo-springmvc/springmvc-client/pom.xml b/demo/demo-springmvc/springmvc-client/pom.xml index cf8fd27b49f..17b7bbcd709 100644 --- a/demo/demo-springmvc/springmvc-client/pom.xml +++ b/demo/demo-springmvc/springmvc-client/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-springmvc - 0.3.0-SNAPSHOT - - springmvc-client + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + + org.apache.servicecomb.demo + demo-springmvc + 3.4.0-SNAPSHOT + + springmvc-client + Java Chassis::Demo::Spring MVC::Client + + + - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-springmvc - - - io.servicecomb - provider-pojo - - + + org.apache.servicecomb.demo.springmvc.SpringmvcClient + - - io.servicecomb.demo.springmvc.client.SpringmvcClient - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-run-config - 0.3.0-SNAPSHOT - - - - - - - - - docker - - springmvc-server - - -Dcse.highway.address=0.0.0.0:7070 - -Dcse.rest.address=0.0.0.0:8080?sslEnabled=false - - - - - - io.fabric8 - docker-maven-plugin - - - - - + + + docker + + 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 + + + + + diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java deleted file mode 100644 index 31022066cb8..00000000000 --- a/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.springmvc.client; - -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; - -import io.servicecomb.demo.CodeFirstRestTemplate; -import io.servicecomb.demo.TestMgr; -import io.servicecomb.provider.pojo.RpcReference; -import io.servicecomb.provider.springmvc.reference.CseHttpEntity; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.swagger.invocation.Response; - -@Component -public class CodeFirstRestTemplateSpringmvc extends CodeFirstRestTemplate { - - @RpcReference(microserviceName = "springmvc", schemaId = "codeFirst") - private CodeFirstSprigmvcIntf intf; - - @Override - protected void testExtend(RestTemplate template, String cseUrlPrefix) { - super.testExtend(template, cseUrlPrefix); - - testResponseEntity("springmvc", template, cseUrlPrefix); - testIntf(); - } - - private void testIntf() { - Date date = new Date(); - - String srcName = RegistryUtils.getMicroservice().getServiceName(); - - ResponseEntity responseEntity = intf.responseEntity(date); - TestMgr.check(date, responseEntity.getBody()); - TestMgr.check("h1v {x-cse-src-microservice=" + srcName + "}", responseEntity.getHeaders().getFirst("h1")); - TestMgr.check("h2v {x-cse-src-microservice=" + srcName + "}", responseEntity.getHeaders().getFirst("h2")); - - checkStatusCode("springmvc", 202, responseEntity.getStatusCode()); - - Response cseResponse = intf.cseResponse(); - TestMgr.check("User [name=nameA, age=100, index=0]", cseResponse.getResult()); - TestMgr.check("h1v {x-cse-src-microservice=" + srcName + "}", cseResponse.getHeaders().getFirst("h1")); - TestMgr.check("h2v {x-cse-src-microservice=" + srcName + "}", cseResponse.getHeaders().getFirst("h2")); - } - - private void testResponseEntity(String microserviceName, RestTemplate template, String cseUrlPrefix) { - Map body = new HashMap<>(); - Date date = new Date(); - body.put("date", date); - - CseHttpEntity> httpEntity = new CseHttpEntity<>(body); - httpEntity.addContext("contextKey", "contextValue"); - - String srcName = RegistryUtils.getMicroservice().getServiceName(); - - ResponseEntity responseEntity = - template.exchange(cseUrlPrefix + "responseEntity", HttpMethod.POST, httpEntity, Date.class); - TestMgr.check(date, responseEntity.getBody()); - TestMgr.check("h1v {contextKey=contextValue, x-cse-src-microservice=" + srcName + "}", - responseEntity.getHeaders().getFirst("h1")); - TestMgr.check("h2v {contextKey=contextValue, x-cse-src-microservice=" + srcName + "}", - responseEntity.getHeaders().getFirst("h2")); - checkStatusCode(microserviceName, 202, responseEntity.getStatusCode()); - } -} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/CodeFirstSprigmvcIntf.java b/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/CodeFirstSprigmvcIntf.java deleted file mode 100644 index 1f3ab36a158..00000000000 --- a/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/CodeFirstSprigmvcIntf.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.demo.springmvc.client; - -import java.util.Date; - -import org.springframework.http.ResponseEntity; - -import io.servicecomb.swagger.invocation.Response; - -public interface CodeFirstSprigmvcIntf { - ResponseEntity responseEntity(Date date); - - Response cseResponse(); -} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/MicroserviceArray.java b/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/MicroserviceArray.java deleted file mode 100644 index b60cc2f53bf..00000000000 --- a/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/MicroserviceArray.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.demo.springmvc.client; - -import java.util.List; - -import io.servicecomb.serviceregistry.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/io/servicecomb/demo/springmvc/client/ServiceCenterExample.java b/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/ServiceCenterExample.java deleted file mode 100644 index bac325c845b..00000000000 --- a/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/ServiceCenterExample.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.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/io/servicecomb/demo/springmvc/client/SpringmvcClient.java b/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/SpringmvcClient.java deleted file mode 100644 index f6b0ff40c18..00000000000 --- a/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/SpringmvcClient.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.springmvc.client; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; - -import io.servicecomb.core.CseContext; -import io.servicecomb.demo.DemoConst; -import io.servicecomb.demo.TestMgr; -import io.servicecomb.demo.controller.Controller; -import io.servicecomb.demo.controller.Person; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.provider.springmvc.reference.RestTemplateBuilder; - -public class SpringmvcClient { - private static RestTemplate restTemplate; - - private static Controller controller; - - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - - run(); - - TestMgr.summary(); - } - - public static void run() throws Exception { - restTemplate = RestTemplateBuilder.create(); - controller = BeanUtils.getBean("controller"); - - String prefix = "cse://springmvc"; - - try { - // this test class is intended for rery hang issue JAV-27 - restTemplate.getForObject(prefix + "/controller/sayhi?name=throwexception", String.class); - TestMgr.check("true", "false"); - } catch (Exception e) { - TestMgr.check("true", "true"); - } - - CodeFirstRestTemplateSpringmvc codeFirstClient = - BeanUtils.getContext().getBean(CodeFirstRestTemplateSpringmvc.class); - codeFirstClient.testCodeFirst(restTemplate, "springmvc", "/codeFirstSpringmvc/"); - - String microserviceName = "springmvc"; - for (String transport : DemoConst.transports) { - CseContext.getInstance().getConsumerProviderManager().setTransport(microserviceName, transport); - TestMgr.setMsg(microserviceName, transport); - - testController(restTemplate, microserviceName); - - testController(); - } - } - - private static void testController(RestTemplate template, String microserviceName) { - String prefix = "cse://" + microserviceName; - - 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, - String.class, - "world")); - TestMgr.check("hello hello 中国", - template.postForObject(prefix + "/controller/sayhello/{name}", - null, - String.class, - "hello 中国")); - - HttpHeaders headers = new HttpHeaders(); - headers.add("name", "world"); - @SuppressWarnings("rawtypes") - HttpEntity entity = new HttpEntity<>(null, headers); - ResponseEntity response = template.exchange(prefix + "/controller/sayhei", - HttpMethod.GET, - entity, - String.class); - TestMgr.check("hei world", response.getBody()); - - Person user = new Person(); - user.setName("world"); - TestMgr.check("ha world", - template.postForObject(prefix + "/controller/saysomething?prefix={prefix}", - user, - String.class, - "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)); - } -} 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 new file mode 100644 index 00000000000..b827884d323 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java @@ -0,0 +1,519 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +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.Person; +import org.apache.servicecomb.demo.springmvc.client.CodeFirstRestTemplateSpringmvc; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +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.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; +import org.springframework.http.MediaType; +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; + + +@SpringBootApplication +@ImportResource(value = "classpath*:META-INF/spring/*.bean.xml") +public class SpringmvcClient { + private static final Logger LOGGER = LoggerFactory.getLogger(SpringmvcClient.class); + + private static RestTemplate templateUrlWithServiceName = new CseRestTemplate(); + + private static RestTemplate templateUrlWithProviderPrefix = new CseRestTemplate(); + + private static RestOperations restTemplate; + + public static void main(String[] args) { + 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); + } + + public static void run() { + try { + runImpl(); + } 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): 1344 -------------"); + } + + private static void runImpl() throws Exception { + testHttpClientsIsOk(); + + templateUrlWithServiceName.setRequestFactory(new UrlWithServiceNameClientHttpRequestFactory()); + restTemplate = RestTemplateBuilder.create(); + templateUrlWithProviderPrefix.setRequestFactory(new UrlWithProviderPrefixClientHttpRequestFactory("/pojo/rest")); + + String prefix = "cse://springmvc"; + String microserviceName = "springmvc"; + + 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"); + } + 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); + codeFirstClient.testCodeFirst(restTemplate, "springmvc", "/codeFirstSpringmvc/"); + codeFirstClient.testCodeFirst(templateUrlWithProviderPrefix, "springmvc", "/pojo/rest/codeFirstSpringmvc/"); + testAllTransport(microserviceName); + testRestTransport(microserviceName, prefix); + CategorizedTestCaseRunner.runCategorizedTestCase(microserviceName); + } + + private static void testHandler(String microserviceName) { + changeTransport(microserviceName, "rest"); + String prefix = "cse://springmvc"; + String result = templateUrlWithServiceName.getForObject(prefix + "/controller/sayHello1?name=tom", String.class); + TestMgr.check("Hello tom,v", result); + } + + private static void testHttpClientsIsOk() { + TestMgr.check(HttpClients.getClient("http-transport-client") != null, true); + TestMgr.check(HttpClients.getClient("http2-transport-client") != null, true); + + TestMgr.check(HttpClients.getClient("http-transport-client", false) != null, true); + TestMgr.check(HttpClients.getClient("http2-transport-client", false) != null, true); + } + + private static void testRestTransport(String microserviceName, String prefix) { + changeTransport(microserviceName, "rest"); + + testControllerRest(templateUrlWithServiceName, microserviceName); + testSpringMvcDefaultValuesRest(templateUrlWithServiceName, microserviceName); + testSpringMvcDefaultValuesJavaPrimitiveRest(templateUrlWithServiceName, microserviceName); + + HttpHeaders headers = new HttpHeaders(); + headers.set("Accept-Encoding", "gzip"); + HttpEntity entity = new HttpEntity<>(headers); + ResponseEntity entityCompress = + restTemplate.exchange(prefix + + "/codeFirstSpringmvc/sayhi/compressed/{name}/v2", HttpMethod.GET, entity, String.class, "Test"); + TestMgr.check( + "Test sayhi compressed:This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text!", + entityCompress.getBody()); + // if server response is compressed, the content-length header will be removed , so can't check this. + // the transfer-encoding header will be missing when the server is set to not compressed + if (entityCompress.getHeaders().get("transfer-encoding") != null) { + TestMgr.check("chunked", entityCompress.getHeaders().get("transfer-encoding").get(0)); + } + + //0.5.0 later version metrics integration test + @SuppressWarnings("unchecked") + 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); + + //prometheus integration test + try { + String content = restTemplate + .getForObject("cse://springmvc/codeFirstSpringmvc/prometheusForTest", String.class); + + String application = LegacyPropertyFactory.getStringProperty("servicecomb.service.application", ""); + + TestMgr.check(true, + content.contains( + "servicecomb_invocation{appId=\"" + application + "\",operation=\"springmvc.codeFirst.addDate")); + TestMgr.check(true, + content.contains( + "servicecomb_invocation{appId=\"" + application + "\",operation=\"springmvc.codeFirst.sayHello")); + TestMgr.check(true, + content + .contains("servicecomb_invocation{appId=\"" + application + "\",operation=\"springmvc.codeFirst.isTrue")); + TestMgr.check(true, + content.contains("servicecomb_invocation{appId=\"" + application + "\",operation=\"springmvc.codeFirst.add")); + TestMgr.check(true, + content + .contains("servicecomb_invocation{appId=\"" + application + "\",operation=\"springmvc.codeFirst.sayHi2")); + TestMgr.check(true, content + .contains( + "servicecomb_invocation{appId=\"" + application + "\",operation=\"springmvc.codeFirst.saySomething")); + + String[] metricLines = content.split("\n"); + if (metricLines.length > 0) { + for (String metricLine : metricLines) { + if (!metricLine.startsWith("#")) { + String[] metricKeyAndValue = metricLine.split(" "); + if (!metricKeyAndValue[0].startsWith("jvm") && !metricKeyAndValue[0].startsWith("os")) { + if (Double.parseDouble(metricKeyAndValue[1]) < 0) { + TestMgr.check("true", "false"); + break; + } + } + } + } + } else { + TestMgr.check("true", "false"); + } + } catch (Exception e) { + LOGGER.error("", e); + TestMgr.check("true", "false"); + } + } + + private static void testAllTransport(String microserviceName) { + for (String transport : DemoConst.transports) { + changeTransport(microserviceName, transport); + + TestMgr.setMsg(microserviceName, transport); + + testControllerAllTransport(templateUrlWithServiceName, microserviceName); + + testSpringMvcDefaultValuesAllTransport(templateUrlWithServiceName, microserviceName); + testSpringMvcDefaultValuesJavaPrimitiveAllTransport(templateUrlWithServiceName, microserviceName); + } + } + + private static void testControllerRest(RestTemplate template, String microserviceName) { + String prefix = "cse://" + microserviceName; + + TestMgr.check("hi world [world]", + 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, + "world1")); + + TestMgr.check("hi world1+world2 [world1+world2]", + template.getForObject(prefix + "/controller/sayhi?name={name}", + String.class, + "world1+world2")); + + 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)); + + try { + template.postForObject(prefix + "/controller/sayhello/{name}", + null, + String.class, + "exception"); + TestMgr.check(true, false); + } catch (InvocationException e) { + TestMgr.check(e.getStatusCode(), 503); + } + } + + private static void testControllerAllTransport(RestTemplate template, String microserviceName) { + String prefix = "cse://" + microserviceName; + + TestMgr.check(7, + template.getForObject(prefix + "/controller/add?a=3&b=4", + Integer.class)); + + try { + template.getForObject(prefix + "/controller/add", + Integer.class); + TestMgr.check("failed", "success"); + } catch (InvocationException e) { + TestMgr.check(e.getStatusCode(), 400); + } + + TestMgr.check("hello world", + template.postForObject(prefix + "/controller/sayhello/{name}", + null, + String.class, + "world")); + TestMgr.check("hello hello 中国", + template.postForObject(prefix + "/controller/sayhello/{name}", + null, + String.class, + "hello 中国")); + + try { + template.postForObject(prefix + "/controller/sayhello/{name}", + null, + String.class, + "exception"); + TestMgr.check(true, false); + } catch (InvocationException e) { + TestMgr.check(e.getStatusCode(), 503); + } + + HttpHeaders headers = new HttpHeaders(); + headers.add("name", "world"); + @SuppressWarnings("rawtypes") + HttpEntity entity = new HttpEntity<>(null, headers); + ResponseEntity response = template.exchange(prefix + "/controller/sayhei", + HttpMethod.GET, + entity, + String.class); + TestMgr.check("hei world", response.getBody()); + + Person user = new Person(); + user.setName("world"); + TestMgr.check("ha world", + template.postForObject(prefix + "/controller/saysomething?prefix={prefix}", + user, + String.class, + "ha")); + } + + private static void testSpringMvcDefaultValuesRest(RestTemplate template, String microserviceName) { + String cseUrlPrefix = "cse://" + microserviceName + "/SpringMvcDefaultValues/"; + String result = template.getForObject(cseUrlPrefix + "/query?d=10", String.class); + TestMgr.check("Hello 20bobo4010", result); + boolean failed = false; + result = null; + try { + result = template.getForObject(cseUrlPrefix + "/query2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(true, failed); + TestMgr.check(null, result); + + failed = false; + result = null; + try { + result = template.getForObject(cseUrlPrefix + "/query2?d=2&e=2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(true, failed); + TestMgr.check(null, result); + + failed = false; + result = null; + try { + result = template.getForObject(cseUrlPrefix + "/query2?a=&d=2&e=2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(true, failed); + TestMgr.check(null, result); + + result = template.getForObject(cseUrlPrefix + "/query2?d=30&e=2", String.class); + TestMgr.check("Hello 20bobo40302", result); + + failed = false; + result = null; + try { + result = template.getForObject(cseUrlPrefix + "/query3?a=2&b=2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(true, failed); + TestMgr.check(null, result); + } + + private static void testSpringMvcDefaultValuesAllTransport(RestTemplate template, String microserviceName) { + String cseUrlPrefix = "cse://" + microserviceName + "/SpringMvcDefaultValues/"; + //default values + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap map = new LinkedMultiValueMap<>(); + HttpEntity> request = new HttpEntity<>(map, headers); + String result = template.postForObject(cseUrlPrefix + "/form", request, String.class); + TestMgr.check("Hello 20bobo", result); + + headers = new HttpHeaders(); + HttpEntity entity = new HttpEntity<>(null, headers); + result = template.postForObject(cseUrlPrefix + "/header", entity, String.class); + TestMgr.check("Hello 20bobo30", result); + + result = template.getForObject(cseUrlPrefix + "/query?d=10", String.class); + TestMgr.check("Hello 20bobo4010", result); + boolean failed = false; + result = null; + try { + result = template.getForObject(cseUrlPrefix + "/query2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(true, failed); + TestMgr.check(null, result); + + failed = false; + result = null; + try { + result = template.getForObject(cseUrlPrefix + "/query2?d=2&e=2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(true, failed); + TestMgr.check(null, result); + + failed = false; + result = null; + try { + result = template.getForObject(cseUrlPrefix + "/query2?a=&d=2&e=2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(true, failed); + TestMgr.check(null, result); + + result = template.getForObject(cseUrlPrefix + "/query2?d=30&e=2", String.class); + TestMgr.check("Hello 20bobo40302", result); + + failed = false; + result = null; + try { + result = template.getForObject(cseUrlPrefix + "/query3?a=2&b=2", String.class); + } catch (InvocationException e) { + failed = true; + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + TestMgr.check(true, failed); + TestMgr.check(null, result); + + result = template.getForObject(cseUrlPrefix + "/query3?a=30&b=2", String.class); + TestMgr.check("Hello 302", result); + + result = template.getForObject(cseUrlPrefix + "/query3?a=30", String.class); + TestMgr.check("Hello 30null", result); + + //input values + headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + HttpEntity> requestPara = new HttpEntity<>(null, headers); + result = template.postForObject(cseUrlPrefix + "/form?a=30&b=sam", requestPara, String.class); + TestMgr.check("Hello 30sam", result); + + headers = new HttpHeaders(); + headers.add("a", "30"); + headers.add("b", "sam"); + headers.add("c", "40"); + entity = new HttpEntity<>(null, headers); + result = template.postForObject(cseUrlPrefix + "/header", entity, String.class); + TestMgr.check("Hello 30sam40", result); + + result = template.getForObject(cseUrlPrefix + "/query?a=3&b=sam&c=5&d=30", String.class); + TestMgr.check("Hello 3sam530", result); + + result = template.getForObject(cseUrlPrefix + "/query2?a=3&b=4&c=5&d=30&e=2", String.class); + TestMgr.check("Hello 345302", result); + } + + private static void testSpringMvcDefaultValuesJavaPrimitiveAllTransport(RestTemplate template, + String microserviceName) { + String cseUrlPrefix = "cse://" + microserviceName + "/SpringMvcDefaultValues/"; + //default values with primitive + String result = template.postForObject(cseUrlPrefix + "/javaprimitiveint", null, String.class); + TestMgr.check("Hello 0bobo", result); + + result = template.postForObject(cseUrlPrefix + "/javaprimitivenumber", null, String.class); + TestMgr.check("Hello 0.0false", result); + + result = template.postForObject(cseUrlPrefix + "/javaprimitivestr", null, String.class); + TestMgr.check("Hello", result); + + result = template.postForObject(cseUrlPrefix + "/javaprimitivecomb", null, String.class); + TestMgr.check("Hello nullnull", result); + + result = template.postForObject(cseUrlPrefix + "/allprimitivetypes", null, String.class); + TestMgr.check("Hello false,\0,0,0,0,0,0.0,0.0,null", result); + + result = template.postForObject(cseUrlPrefix + + "/allprimitivetypes?pBoolean=true&pChar=c&pByte=20&pShort=30&pInt=40&pLong=50&pFloat=60&pDouble=70&pDoubleWrap=80", + null, String.class); + TestMgr.check("Hello true,c,20,30,40,50,60.0,70.0,80.0", result); + } + + private static void testSpringMvcDefaultValuesJavaPrimitiveRest(RestTemplate template, String microserviceName) { + String cseUrlPrefix = "cse://" + microserviceName + "/SpringMvcDefaultValues/"; + //default values with primitive + String result = template.postForObject(cseUrlPrefix + "/javaprimitiveint", null, String.class); + TestMgr.check("Hello 0bobo", result); + + result = template.postForObject(cseUrlPrefix + "/javaprimitivenumber", null, String.class); + TestMgr.check("Hello 0.0false", result); + + result = template.postForObject(cseUrlPrefix + "/javaprimitivestr", null, String.class); + TestMgr.check("Hello", result); + + result = template.postForObject(cseUrlPrefix + "/javaprimitivecomb", null, String.class); + TestMgr.check("Hello nullnull", result); + + result = template.postForObject(cseUrlPrefix + "/allprimitivetypes", null, String.class); + TestMgr.check("Hello false,\0,0,0,0,0,0.0,0.0,null", result); + } +} 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 new file mode 100644 index 00000000000..d30edc6b495 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java @@ -0,0 +1,254 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +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.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; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; + +import jakarta.servlet.http.Part; + +@Component +public class CodeFirstRestTemplateSpringmvc extends CodeFirstRestTemplate { + interface UploadPartAndFile { + String fileUpload(Part file1, File someFile); + } + + interface UploadStreamAndResource { + String fileUpload(InputStream file1, Resource someFile); + } + + private UploadPartAndFile uploadPartAndFile = Invoker.createProxy("springmvc", "codeFirst", UploadPartAndFile.class); + + private UploadStreamAndResource uploadStreamAndResource = + Invoker.createProxy("springmvc", "codeFirst", UploadStreamAndResource.class); + + private TestResponse testResponse = new TestResponse(); + + private TestObject testObject = new TestObject(); + + private TestGeneric testGeneric = new TestGeneric(); + + private TestRestTemplate testRestTemplate = new TestRestTemplate(); + + private TestContentType testContentType = new TestContentType(); + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + testResponse.setEnvironment(environment); + } + + @Override + protected void testOnlyRest(String microservcieName, RestOperations template, String cseUrlPrefix) { + try { + testUpload(template, cseUrlPrefix); + } catch (IOException e) { + throw new IllegalStateException(e); + } + testResponseEntity("springmvc", template, cseUrlPrefix); + testCodeFirstTestFormRest(template, cseUrlPrefix); + + testResponse.runRest(); + testObject.runRest(); + testGeneric.runRest(); + testRestTemplate.runRest(); + testContentType.runAllTest(); + + super.testOnlyRest(microservcieName, template, cseUrlPrefix); + } + + @Override + protected void testOnlyHighway(RestOperations template, String cseUrlPrefix) { + testResponse.runHighway(); + testObject.runHighway(); + testGeneric.runHighway(); + testCodeFirstTestFormHighway(template, cseUrlPrefix); + super.testOnlyHighway(template, cseUrlPrefix); + } + + @Override + protected void testAllTransport(String microserviceName, RestOperations template, String cseUrlPrefix) { + testResponse.runAllTransport(); + testObject.runAllTransport(); + testGeneric.runAllTransport(); + testRestTemplate.runAllTest(); + testResponseEntity("springmvc", template, cseUrlPrefix); + testCodeFirstTestForm(template, cseUrlPrefix); + + super.testAllTransport(microserviceName, template, cseUrlPrefix); + } + + 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); + + String file2Content = " bonjour"; + File someFile = File.createTempFile("upload2", ".txt"); + FileUtils.writeStringToFile(someFile, file2Content, StandardCharsets.UTF_8, false); + + String expect = String.format("%s:%s:%s\n" + + "%s:%s:%s", + file1.getName(), + MediaType.TEXT_PLAIN_VALUE, + file1Content, + someFile.getName(), + MediaType.TEXT_PLAIN_VALUE, + file2Content); + + String result = testRestTemplateUpload(template, cseUrlPrefix, file1, someFile); + TestMgr.check(expect, result); + + result = uploadPartAndFile.fileUpload(new FilePart(null, file1), someFile); + TestMgr.check(expect, result); + + expect = "hello world"; + MultiValueMap map = new LinkedMultiValueMap<>(); + map.add("file1", new FileSystemResource(file1)); + result = template.postForObject( + cseUrlPrefix + "/upload1", + new HttpEntity<>(map), + String.class); + TestMgr.check(expect, result); + + expect = String.format("null:%s:%s\n" + + "%s:%s:%s", + MediaType.APPLICATION_OCTET_STREAM_VALUE, + file1Content, + someFile.getName(), + MediaType.TEXT_PLAIN_VALUE, + file2Content); + result = uploadStreamAndResource + .fileUpload(new ByteArrayInputStream(file1Content.getBytes(StandardCharsets.UTF_8)), + new org.springframework.core.io.PathResource(someFile.getAbsolutePath())); + TestMgr.check(expect, result); + } + + 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)); + + return template.postForObject( + cseUrlPrefix + "/upload", + new HttpEntity<>(map), + String.class); + } + + private void testResponseEntity(String microserviceName, RestOperations template, String cseUrlPrefix) { + Map body = new HashMap<>(); + Date date = new Date(); + body.put("date", date); + + CseHttpEntity> httpEntity = new CseHttpEntity<>(body); + httpEntity.addContext("contextKey", "contextValue"); + + String srcName = BootStrapProperties.readServiceName(environment); + + ResponseEntity responseEntity = + template.exchange(cseUrlPrefix + "responseEntity", HttpMethod.POST, httpEntity, Date.class); + TestMgr.check(date, responseEntity.getBody()); + TestMgr.check("h1v " + srcName, responseEntity.getHeaders().getFirst("h1")); + TestMgr.check("h2v " + srcName, responseEntity.getHeaders().getFirst("h2")); + checkStatusCode(microserviceName, 202, responseEntity.getStatusCode()); + + responseEntity = + template.exchange(cseUrlPrefix + "responseEntity", HttpMethod.PATCH, httpEntity, Date.class); + TestMgr.check(date, responseEntity.getBody()); + TestMgr.check("h1v " + srcName, responseEntity.getHeaders().getFirst("h1")); + TestMgr.check("h2v " + srcName, responseEntity.getHeaders().getFirst("h2")); + checkStatusCode(microserviceName, 202, responseEntity.getStatusCode()); + + int retryResult = template.getForObject(cseUrlPrefix + "retrySuccess?a=2&b=3", Integer.class); + TestMgr.check(retryResult, 5); + retryResult = template.getForObject(cseUrlPrefix + "retrySuccess?a=2&b=3", Integer.class); + TestMgr.check(retryResult, 5); + } + + 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> formEntry = new HttpEntity<>(map, formHeaders); + TestMgr.check(code + "null", + template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); + map.put("form2", "hello"); + TestMgr + .check(code + "hello", template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); + } + + 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> 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); + formEntry = new HttpEntity<>(map, formHeaders); + TestMgr.check(code + "null", + template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); + } + + 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> 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", formEntry, String.class).getBody()); + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstSpringmvcIntf.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstSpringmvcIntf.java new file mode 100644 index 00000000000..f7401c30017 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstSpringmvcIntf.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.demo.springmvc.client; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.demo.EmptyObject; +import org.apache.servicecomb.demo.Generic; +import org.apache.servicecomb.demo.compute.GenericParam; +import org.apache.servicecomb.demo.compute.Person; +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.swagger.invocation.Response; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +public interface CodeFirstSpringmvcIntf { + ResponseEntity responseEntity(Date date); + + Response cseResponse(); + + Response cseResponseCorrect(); + + Object testObject(Object input); + + EmptyObject testEmpty(EmptyObject input); + + Map testMapObject(Map input); + + List testListObject(List input); + + Holder testHolderObject(Holder input); + + Holder testHolderUser(Holder input); + + Generic testGenericUser(Generic input); + + Generic testGenericLong(Generic input); + + Generic testGenericDate(Generic input); + + Generic testGenericEnum(Generic input); + + Generic> testGenericGenericUser(Generic> input); + + void testvoidInRPC(); + + Void testVoidInRPC(); + + String checkQueryObject(String name, String otherName, Person requestBody); + + String checkQueryGenericObject(GenericParam requestBody, String str, long num); + + String checkQueryGenericString(String str, GenericParam requestBody, long num, String data, + String strExtended, int intExtended); + + String testDelay(); + + String testAbort(); + + DecodeTestResponse testDecodeResponseError(); +} 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 new file mode 100644 index 00000000000..6bf78279d42 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CustomEndpointDiscoveryFilter.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.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 + protected String findTransportName(DiscoveryContext context, DiscoveryTreeNode parent) { + //only need rest endpoints + return "rest"; + } + + @Override + protected Object createEndpoint(DiscoveryContext context, String transportName, String endpoint, + StatefulDiscoveryInstance instance) { + return endpoint; + } + + @Override + public int getOrder() { + return 0; + } +} 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/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 new file mode 100644 index 00000000000..9bce39f0af7 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SchemeInterfaceSpringmvc.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.client; + +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/SpringMVCSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SpringMVCSchema.java new file mode 100644 index 00000000000..42c912188af --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/SpringMVCSchema.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.client; + +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.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(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) { + return null; + } + + @ApiResponses({ + @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) { + return null; + } +} 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 new file mode 100644 index 00000000000..aa0d9d0f63d --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestContentType.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.RestOperations; + +import jakarta.ws.rs.core.MediaType; + +public class TestContentType { + + private RestOperations restTemplate = RestTemplateBuilder.create(); + + public void runAllTest() { + testGlobalSetting(); + testApiOperation(); + testRequestMapping(); + testProtoBuffer(); + testResponseTypeOverwrite(); + } + + private void testGlobalSetting() { + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN); + CseHttpEntity requestEntity = new CseHttpEntity<>("from testGlobalSetting", requestHeaders); + ResponseEntity responseEntity = restTemplate + .exchange("cse://springmvc/contentTypeSpringmvc/testGlobalSetting", HttpMethod.POST, + requestEntity, String.class); + TestMgr.check( + "testGlobalSetting: name=[from testGlobalSetting], request content-type=[" + MediaType.TEXT_PLAIN + "]", + responseEntity.getBody()); + TestMgr.check(MediaType.TEXT_PLAIN, extractContentType(responseEntity.getHeaders().getContentType())); + } + + private void testApiOperation() { + HttpHeaders requestHeaders = new HttpHeaders(); + 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.TEXT_PLAIN + "]", + responseEntity.getBody()); + TestMgr.check(MediaType.TEXT_PLAIN, extractContentType(responseEntity.getHeaders().getContentType())); + } + + private void testRequestMapping() { + HttpHeaders requestHeaders = new HttpHeaders(); + requestHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); + CseHttpEntity requestEntity = new CseHttpEntity<>("from testRequestMapping", requestHeaders); + ResponseEntity responseEntity = restTemplate + .exchange("cse://springmvc/contentTypeSpringmvc/testRequestMapping", HttpMethod.POST, + requestEntity, String.class); + TestMgr.check( + "testRequestMapping: name=[from testRequestMapping], request content-type=[" + MediaType.APPLICATION_JSON + "]", + responseEntity.getBody()); + 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); + TestMgr.check("testResponseTypeOverwrite: OK", responseEntity.getBody()); + TestMgr.check(MediaType.TEXT_PLAIN, extractContentType(responseEntity.getHeaders().getContentType())); + } + + private String extractContentType(org.springframework.http.MediaType mediaType) { + return mediaType.getType() + "/" + mediaType.getSubtype(); + } +} 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 new file mode 100644 index 00000000000..3264caa9fa9 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestControllerImpl.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.demo.springmvc.client; + +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.RestOperations; + +@Component +public class TestControllerImpl implements CategorizedTestCase { + 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 (LegacyPropertyFactory.getBooleanProperty("servicecomb.test.vert.transport", true)) { + + TestMgr.check(restTemplate.getForObject( + SERVER + "/springmvc/controller/sayHello1?name=you;me", String.class), "Hello you,v"); + } else { + TestMgr.check(restTemplate.getForObject( + SERVER + "/springmvc/controller/sayHello1?name=you;me", String.class), "Hello you;me,v"); + } + TestMgr.check(restTemplate.getForObject( + SERVER + "/springmvc/controller/sayHello1?name={1}", + String.class, "you;me"), "Hello you;me,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 new file mode 100644 index 00000000000..580192c71a1 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDateTimeSchema.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.demo.springmvc.client; + +import java.net.URI; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.List; + +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.cache.VersionedCache; +import org.apache.servicecomb.loadbalance.LoadBalanceFilter; +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.RestOperations; + +interface DateTimeSchemaInf { + Date getDate(Date date); + + Date getDatePath(Date date); + + Date postDate(Date date); + + LocalDate getLocalDate(LocalDate date); + + LocalDate getLocalDatePath(LocalDate date); + + LocalDate postLocalDate(LocalDate date); + + LocalDateTime getLocalDateTime(LocalDateTime date); + + LocalDateTime getLocalDateTimePath(LocalDateTime date); + + LocalDateTime postLocalDateTime(LocalDateTime date); +} + +interface DateTimeSchemaWithContextInf { + Date getDate(InvocationContext context, Date date); +} + +@Component +public class TestDateTimeSchema implements CategorizedTestCase { + @RpcReference(microserviceName = "springmvc", schemaId = "DateTimeSchema") + private DateTimeSchemaInf dateTimeSchemaInf; + + @RpcReference(microserviceName = "springmvc", schemaId = "DateTimeSchema") + private DateTimeSchemaWithContextInf dateTimeSchemaWithContextInf; + + private DiscoveryTree discoveryTree; + + private SCBEngine scbEngine; + + @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())); + } + + public TestDateTimeSchema() { + + } + + @Override + public void testAllTransport() throws Exception { + testDateTimeSchema(); + testDateTimeSchemaMulticast(); + testDateTimeSchemaMulticastRestTemplate(); + } + + private void testDateTimeSchema() { + Date date = new Date(); + TestMgr.check(date.getTime(), dateTimeSchemaInf.getDate(date).getTime()); + TestMgr.check(date.getTime(), dateTimeSchemaInf.getDatePath(date).getTime()); + TestMgr.check(date.getTime(), dateTimeSchemaInf.postDate(date).getTime()); + + LocalDate localDate = LocalDate.of(2020, 2, 1); + TestMgr.check(localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), + dateTimeSchemaInf.getLocalDate(localDate).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + TestMgr.check(localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), + dateTimeSchemaInf.getLocalDatePath(localDate).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + TestMgr.check(localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), + dateTimeSchemaInf.postLocalDate(localDate).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + + LocalDateTime localDateTime = LocalDateTime.of(2020, 2, 1, 23, 23, 30, 333); + TestMgr.check(localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")), + dateTimeSchemaInf.getLocalDateTime(localDateTime) + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"))); + TestMgr.check(localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")), + dateTimeSchemaInf.getLocalDateTimePath(localDateTime) + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"))); + TestMgr.check(localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")), + dateTimeSchemaInf.postLocalDateTime(localDateTime) + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"))); + } + + private void testDateTimeSchemaMulticast() throws Exception { + DiscoveryContext context = new DiscoveryContext(); + VersionedCache serversVersionedCache = discoveryTree.discovery(context, "springmvctest", "springmvc"); + List endpoints = serversVersionedCache.data(); + + for (String endpoint : endpoints) { + InvocationContext invocationContext = new InvocationContext(); + invocationContext.addLocalContext(LoadBalanceFilter.SERVICECOMB_SERVER_ENDPOINT, endpoint); + Date date = new Date(); + TestMgr.check(date.getTime(), dateTimeSchemaWithContextInf.getDate(invocationContext, date).getTime()); + + invocationContext = new InvocationContext(); + invocationContext.addLocalContext(LoadBalanceFilter.SERVICECOMB_SERVER_ENDPOINT, parseEndpoint(endpoint)); + date = new Date(); + TestMgr.check(date.getTime(), dateTimeSchemaWithContextInf.getDate(invocationContext, date).getTime()); + } + } + + private Endpoint parseEndpoint(String endpointUri) throws Exception { + URI formatUri = new URI(endpointUri); + 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"); + List endpoints = serversVersionedCache.data(); + + RestOperations restTemplate = RestTemplateBuilder.create(); + + for (String endpoint : endpoints) { + CseHttpEntity entity = new CseHttpEntity<>(null); + InvocationContext invocationContext = new InvocationContext(); + invocationContext.addLocalContext(LoadBalanceFilter.SERVICECOMB_SERVER_ENDPOINT, endpoint); + entity.setContext(invocationContext); + + Date date = new Date(); + String dateValue = RestObjectMapperFactory.getRestObjectMapper().convertToString(date); + TestMgr.check(date.getTime(), + restTemplate + .exchange("cse://springmvc/dateTime/getDate?date={1}", HttpMethod.GET, + entity, Date.class, dateValue).getBody().getTime()); + + entity = new CseHttpEntity<>(null); + invocationContext = new InvocationContext(); + invocationContext.addLocalContext(LoadBalanceFilter.SERVICECOMB_SERVER_ENDPOINT, parseEndpoint(endpoint)); + entity.setContext(invocationContext); + + date = new Date(); + dateValue = RestObjectMapperFactory.getRestObjectMapper().convertToString(date); + TestMgr.check(date.getTime(), + restTemplate + .exchange("cse://springmvc/dateTime/getDate?date={1}", HttpMethod.GET, + entity, Date.class, dateValue).getBody().getTime()); + } + } +} 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 new file mode 100644 index 00000000000..f1ba4b87095 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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 { + RestOperations restTemplate = RestTemplateBuilder.create(); + ReadStreamPart readStreamPart = restTemplate + .getForObject("servicecomb://springmvc/download/deleteAfterFinished?content=hello", ReadStreamPart.class); + 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 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 { + RestOperations restTemplate = RestTemplateBuilder.create(); + ReadStreamPart readStreamPart = restTemplate + .getForObject("servicecomb://springmvc/download/partIsNull?content=test", ReadStreamPart.class); + String result = readStreamPart.saveAsString().get(); + TestMgr.check(result, "test"); + + readStreamPart = restTemplate + .getForObject("servicecomb://springmvc/download/partIsNull?content=", ReadStreamPart.class); + result = readStreamPart.saveAsString().get(); + TestMgr.check(result, ""); + } + + private void testDownloadFileNotDeleted() throws Exception { + RestOperations restTemplate = RestTemplateBuilder.create(); + ReadStreamPart readStreamPart = restTemplate + .getForObject("servicecomb://springmvc/download/notDeleteAfterFinished?content=hello", ReadStreamPart.class); + String hello = readStreamPart.saveAsString().get(); + TestMgr.check(hello, "hello"); + + boolean exists = restTemplate + .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 new file mode 100644 index 00000000000..ce976f10d5b --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestGeneric.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Generic; +import org.apache.servicecomb.demo.TestMgr; +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.RestTemplateBuilder; +import org.springframework.http.HttpStatus; +import org.springframework.web.client.RestOperations; + +public class TestGeneric { + private CodeFirstSpringmvcIntf intf; + + private RestOperations restTemplate = RestTemplateBuilder.create(); + + private String prefix = "cse://springmvc/codeFirstSpringmvc"; + + public TestGeneric() { + intf = Invoker.createProxy("springmvc", "codeFirst", CodeFirstSpringmvcIntf.class); + } + + public void runRest() { + testHolderUser_rest(); + testGenericUser_rest(); + testGenericGenericUser_rest(); + testGenericLong_rest(); + testGenericDate_rest(); + testGenericEnum_rest(); + } + + public void runHighway() { + } + + public void runAllTransport() { + } + + @SuppressWarnings("unchecked") + private void testGenericEnum_rest() { + Generic generic = new Generic<>(); + generic.value = HttpStatus.OK; + Generic result = intf.testGenericEnum(generic); + TestMgr.check(HttpStatus.OK, result.value); + TestMgr.check(HttpStatus.class, result.value.getClass()); + + result = restTemplate.postForObject(prefix + "/genericEnum", generic, Generic.class); + TestMgr.check(HttpStatus.OK, result.value); + TestMgr.check(HttpStatus.class, result.value.getClass()); + } + + @SuppressWarnings({"unchecked", "deprecation"}) + private void testGenericDate_rest() { + Generic generic = new Generic<>(); + generic.value = new Date(1001); + Generic result = intf.testGenericDate(generic); + TestMgr.check("1970-01-01T00:00:01.001Z", + com.fasterxml.jackson.databind.util.ISO8601Utils.format(result.value, true)); + TestMgr.check(Date.class, result.value.getClass()); + + result = restTemplate.postForObject(prefix + "/genericDate", generic, Generic.class); + TestMgr.check("1970-01-01T00:00:01.001Z", + com.fasterxml.jackson.databind.util.ISO8601Utils.format(result.value, true)); + TestMgr.check(Date.class, result.value.getClass()); + } + + @SuppressWarnings("unchecked") + private void testGenericLong_rest() { + Generic generic = new Generic<>(); + generic.value = 100L; + Generic result = intf.testGenericLong(generic); + TestMgr.check(100, result.value); + TestMgr.check(Long.class, result.value.getClass()); + + result = restTemplate.postForObject(prefix + "/genericLong", generic, Generic.class); + TestMgr.check(100, result.value); + TestMgr.check(Long.class, result.value.getClass()); + } + + @SuppressWarnings("unchecked") + private void testGenericGenericUser_rest() { + Generic> generic = new Generic<>(); + generic.value = new Generic<>(); + generic.value.value = new User(); + Generic> result = intf.testGenericGenericUser(generic); + TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.value.jsonString()); + + result = restTemplate.postForObject(prefix + "/genericGenericUser", generic, Generic.class); + TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.value.jsonString()); + } + + @SuppressWarnings("unchecked") + private void testGenericUser_rest() { + Generic generic = new Generic<>(); + generic.value = new User(); + Generic result = intf.testGenericUser(generic); + TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.jsonString()); + + result = restTemplate.postForObject(prefix + "/genericUser", generic, Generic.class); + TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.jsonString()); + } + + @SuppressWarnings("unchecked") + private void testHolderUser_rest() { + Holder holder = new Holder<>(new User()); + Holder result = intf.testHolderUser(holder); + TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.jsonString()); + + result = restTemplate.postForObject(prefix + "/holderUser", holder, Holder.class); + TestMgr.check("{\"name\":\"nameA\",\"age\":100,\"index\":0,\"names\":null}", result.value.jsonString()); + } +} 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 new file mode 100644 index 00000000000..899de3dcdca --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestInvokeWhenServerNotReady.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.demo.springmvc.client; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.core.provider.consumer.InvokerUtils; +import org.apache.servicecomb.demo.CommonSchemaInterface; +import org.apache.servicecomb.provider.pojo.RpcReference; +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.RestOperations; + +@Component +public class TestInvokeWhenServerNotReady { + @RpcReference(schemaId = "SpringMVCCommonSchemaInterface", microserviceName = "springmvc") + private CommonSchemaInterface client; + + // only invoke RPC before system is up, these setup is for testing calls before system ready + public TestInvokeWhenServerNotReady() { + startRestTemplateCall(); + startRpcCall(); + startInvokerUtilsCall(); + } + + private void startRestTemplateCall() { + new Thread(() -> { + for (int i = 0; i < 100; i++) { + try { + RestOperations template = RestTemplateBuilder.create(); + template.getForObject("servicecomb://springmvc/upload/isServerStartUpSuccess", Boolean.class); + } catch (Throwable e) { + // ignore + } + } + }).start(); + } + + private void startRpcCall() { + new Thread(() -> { + for (int i = 0; i < 100; i++) { + try { + InvocationContext context = new InvocationContext(); + client.testInvocationTimeout(context, 1001, "customized"); + } catch (Throwable e) { + // ignore + } + } + }).start(); + } + + private void startInvokerUtilsCall() { + new Thread(() -> { + for (int i = 0; i < 100; i++) { + try { + Map args = new HashMap<>(); + args.put("timeout", 1); + args.put("name", "customized"); + InvokerUtils + .syncInvoke("springmvc", "SpringMVCCommonSchemaInterface", "testInvocationTimeout", args, String.class); + } catch (Throwable e) { + // ignore + } + } + }).start(); + } +} 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 new file mode 100644 index 00000000000..08f9cdda1ee --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestMaxHttpUrlLength.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF 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 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.RestOperations; + +import com.google.common.base.Strings; + +@Component +public class TestMaxHttpUrlLength implements CategorizedTestCase { + @Override + public void testRestTransport() throws Exception { + if (LegacyPropertyFactory.getBooleanProperty("servicecomb.test.vert.transport", true)) { + testUrlNotLongerThan4096(); + } + } + + private void testUrlNotLongerThan4096() { + 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()); + TestMgr.check("hi " + q + " [" + q + "]", + restTemplate.getForObject("cse://springmvc/springmvc/controller/sayhi?name=" + q, + String.class)); + + q = Strings.repeat("q", 4096 + 2 - "GET /api/springmvc/controller/sayhi?name=".length() - " HTTP/1.1\r".length()); + try { + restTemplate.getForObject("cse://springmvc/springmvc/controller/sayhi?name=" + q, + String.class); + TestMgr.check(true, false); + } catch (InvocationException e) { + TestMgr.check(REQUEST_URI_TOO_LONG.getStatusCode(), e.getStatusCode()); + } + } +} 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 new file mode 100644 index 00000000000..498a7d0c832 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestObject.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.demo.EmptyObject; +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.RestTemplateBuilder; +import org.springframework.web.client.RestOperations; + +public class TestObject { + private CodeFirstSpringmvcIntf intf; + + private RestOperations restTemplate = RestTemplateBuilder.create(); + + private String prefix = "cse://springmvc/codeFirstSpringmvc"; + + public TestObject() { + intf = Invoker.createProxy("springmvc", "codeFirst", CodeFirstSpringmvcIntf.class); + } + + public void runRest() { + + } + + public void runHighway() { + + } + + public void runAllTransport() { + testObject(); + testMapObject(); + testEmptyObject(); + testListObject(); + testHolderObject(); + } + + @SuppressWarnings("unchecked") + private void testHolderObject() { + Holder holder = new Holder<>("v"); + Holder result = intf.testHolderObject(holder); + TestMgr.check("v", result.value); + + result = restTemplate.postForObject(prefix + "/holderObject", holder, Holder.class); + TestMgr.check("v", result.value); + } + + @SuppressWarnings("unchecked") + private void testListObject() { + List list = Collections.singletonList("v"); + List result = intf.testListObject(list); + TestMgr.check("[v]", result); + TestMgr.check(ArrayList.class, result.getClass()); + + result = restTemplate.postForObject(prefix + "/listObject", list, List.class); + TestMgr.check("[v]", result); + TestMgr.check(ArrayList.class, result.getClass()); + } + + @SuppressWarnings("unchecked") + private void testMapObject() { + Map map = Collections.singletonMap("k", "v"); + Map result = intf.testMapObject(map); + TestMgr.check("{k=v}", result); + // This is a behavior change in 2.0.0, before 2.0.0 runtime type of RAW is HashMap + 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()); + } + + private void testEmptyObject() { + EmptyObject result = intf.testEmpty(new EmptyObject()); + TestMgr.check(EmptyObject.class, result.getClass()); + + result = restTemplate.postForObject(prefix + "/emptyObject", new EmptyObject(), EmptyObject.class); + TestMgr.check(EmptyObject.class, result.getClass()); + } + + @SuppressWarnings("unchecked") + private void testObject() { + // int + Object result = intf.testObject(1); + TestMgr.check(1, result); + TestMgr.check(Integer.class, result.getClass()); + + result = restTemplate.postForObject(prefix + "/object", 1, Integer.class); + TestMgr.check(1, result); + TestMgr.check(Integer.class, result.getClass()); + + // string + result = intf.testObject("str"); + TestMgr.check("str", result); + TestMgr.check(String.class, result.getClass()); + + result = restTemplate.postForObject(prefix + "/object", "str", String.class); + TestMgr.check("str", result); + TestMgr.check(String.class, result.getClass()); + + // emptyObject + result = intf.testObject(new EmptyObject()); + // result may not be an empty map in highway + // TestMgr.check("{}", result); + TestMgr.check(true, Map.class.isAssignableFrom(result.getClass())); + + result = restTemplate.postForObject(prefix + "/object", new EmptyObject(), EmptyObject.class); + TestMgr.check(EmptyObject.class, result.getClass()); + result = restTemplate.postForObject(prefix + "/object", new EmptyObject(), EmptyObject.class); + TestMgr.check(EmptyObject.class, result.getClass()); + + // map + Map map = Collections.singletonMap("k", "v"); + result = intf.testObject(map); + TestMgr.check("{k=v}", result); + TestMgr.check(true, Map.class.isAssignableFrom(result.getClass())); + + result = restTemplate.postForObject(prefix + "/object", map, Map.class); + TestMgr.check("{k=v}", result); + TestMgr.check(true, Map.class.isAssignableFrom(result.getClass())); + + // list + List list = Collections.singletonList("v"); + result = intf.testObject(list); + TestMgr.check("[v]", result); + TestMgr.check(true, List.class.isAssignableFrom(result.getClass())); + + result = restTemplate.postForObject(prefix + "/object", list, List.class); + TestMgr.check("[v]", result); + 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")); + TestMgr.check(LinkedHashMap.class, result.getClass()); + + result = restTemplate.postForObject(prefix + "/object", holder, Holder.class); + TestMgr.check("v", ((Map) result).get("value")); + TestMgr.check(LinkedHashMap.class, result.getClass()); + } +} 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 new file mode 100644 index 00000000000..bede84dadaf --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestResponse.java @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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.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; + +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); + } + + public void runRest() { + checkQueryGenericObject(); + checkQueryGenericString(); + testDelay(); + testAbort(); + testDecodeResponseError(); + checkQueryObject(); + testCseResponse(); + testResponseEntity(); + testCseResponseCorrect(); + } + + public void runHighway() { + } + + public void runAllTransport() { + testvoidResponse(); + testVoidResponse(); + checkQueryObject(); + testCseResponse(); + testResponseEntity(); + } + + private void testCseResponse() { + 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")); + TestMgr.check("h2v " + srcName, cseResponse.getHeader("h2")); + TestMgr.check(cseResponse.getStatusCode(), 202); + } + + private void testCseResponseCorrect() { + 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")); + TestMgr.check("h2v " + srcName, cseResponse.getHeader("h2")); + TestMgr.check(cseResponse.getStatusCode(), 202); + } + + private void testResponseEntity() { + Date date = new Date(); + + 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.getStatusCode().value()); + } + + private void testvoidResponse() { + intf.testvoidInRPC(); + } + + private void testVoidResponse() { + intf.testVoidInRPC(); + } + + private void checkQueryObject() { + String result = intf.checkQueryObject("name1", "otherName2", new Person("bodyName")); + TestMgr.check("invocationContext_is_null=false,person=name1,otherName=otherName2,name=name1,requestBody=bodyName", + result); + } + + private void checkQueryGenericObject() { + GenericParam requestBody = new GenericParam<>(); + requestBody.num(1).str("str1").setData(new Person("bodyPerson")); + String result = intf.checkQueryGenericObject(requestBody, "str2", 2); + TestMgr.check( + "str=str2,generic=GenericParamWithJsonIgnore{str='str2', num=2, data=null},requestBody=GenericParam{str='str1', num=1, data=bodyPerson}", + result); + } + + private void checkQueryGenericString() { + GenericParam requestBody = new GenericParam<>(); + requestBody.num(1).str("str1").setData(new Person("bodyPerson")); + String result = intf.checkQueryGenericString("str2", requestBody, 2, "dataTest", "strInSubclass", 33); + TestMgr.check( + "str=str2,generic=GenericParamExtended{strExtended='strInSubclass', intExtended=33, super=" + + "GenericParam{str='str2', num=2, data=dataTest}},requestBody=GenericParam{str='str1', num=1, data=bodyPerson}", + result); + } + + private void testDelay() { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < 4; ++i) { + result.append(intf.testDelay()).append("|"); + } + TestMgr.check("OK|OK|OK|OK|", result.toString()); + } + + private void testAbort() { + AtomicInteger count = new AtomicInteger(0); + for (int i = 0; i < 100; ++i) { + try { + String response = intf.testAbort(); + count.incrementAndGet(); + TestMgr.check("OK", response); + } catch (InvocationException e) { + TestMgr.check(421, e.getStatusCode()); + TestMgr.check("aborted by fault inject", ((CommonExceptionData) e.getErrorData()).getMessage()); + } + } + // 50% percent fail + TestMgr.check(true, count.get() >= 30 && count.get() <= 70); + } + + private void testDecodeResponseError() { + InvocationException exception = null; + try { + intf.testDecodeResponseError(); + } catch (InvocationException e) { + // 1. InvocationException: wrapper exception + exception = e; + } + Objects.requireNonNull(exception); + // 2. CseException: bizKeeper exception + Throwable cause = exception.getCause(); + TestMgr.check(InvalidFormatException.class, cause.getClass()); + TestMgr.check( + ((InvalidFormatException) cause).getMessage().contains("Cannot deserialize value of type `java.util.Date`"), + true); + } +} 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 new file mode 100644 index 00000000000..a9d02553bf7 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestRestTemplate.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.demo.springmvc.client; + +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.springmvc.reference.RestTemplateBuilder; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; +import org.springframework.web.client.RestOperations; + +public class TestRestTemplate { + private RestOperations restTemplate = RestTemplateBuilder.create(); + + public void runAllTest() { + testvoidResponse(); + testVoidResponse(); + checkAllVoidTestResult(); + checkQueryObject(); + } + + public void runRest() { + checkQueryGenericObject(); + checkQueryGenericString(); + checkQueryObject(); + } + + private void testvoidResponse() { + ResponseEntity resultEntity = restTemplate + .getForEntity("cse://springmvc/codeFirstSpringmvc/testvoidInRestTemplate", void.class); + 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.getStatusCode().value(), "Void return type invocation failed"); + resultEntity = restTemplate + .getForEntity("servicecomb://springmvc/codeFirstSpringmvc/testVoidInRestTemplate", Void.class); + Assert.isTrue(200 == resultEntity.getStatusCode().value(), "Void return type invocation failed"); + resultEntity = restTemplate + .getForEntity("servicecomb://springmvc/codeFirstSpringmvc/testDefaultGetApiExample", Void.class); + Assert.isTrue(200 == resultEntity.getStatusCode().value(), "Void return type invocation failed"); + } + + private void checkAllVoidTestResult() { + ResponseEntity resultEntity = restTemplate + .getForEntity("cse://springmvc/codeFirstSpringmvc/checkVoidResult", boolean.class); + Assert.isTrue(resultEntity.getBody(), "not all void test is passed"); + restTemplate + .getForEntity("servicecomb://springmvc/codeFirstSpringmvc/checkVoidResult", boolean.class); + Assert.isTrue(resultEntity.getBody(), "not all void test is passed"); + } + + private void checkQueryObject() { + ResponseEntity responseEntity = restTemplate + .postForEntity("cse://springmvc/codeFirstSpringmvc/checkQueryObject?name={1}&otherName={2}", + new Person("bodyName"), String.class, "name1", "otherName2"); + TestMgr.check("invocationContext_is_null=false,person=name1,otherName=otherName2,name=name1,requestBody=bodyName", + responseEntity.getBody()); + } + + private void checkQueryGenericObject() { + GenericParam requestBody = new GenericParam<>(); + requestBody.num(1).str("str1").setData(new Person("bodyPerson")); + HttpEntity> requestEntity = new HttpEntity<>(requestBody); + ResponseEntity responseEntity = restTemplate + .exchange("cse://springmvc/codeFirstSpringmvc/checkQueryGenericObject?str={1}&num={2}", + HttpMethod.PUT, requestEntity, String.class, "str2", 2); + TestMgr.check( + "str=str2,generic=GenericParamWithJsonIgnore{str='str2', num=2, data=null},requestBody=GenericParam{str='str1', num=1, data=bodyPerson}", + responseEntity.getBody()); + } + + private void checkQueryGenericString() { + GenericParam requestBody = new GenericParam<>(); + requestBody.num(1).str("str1").setData(new Person("bodyPerson")); + ResponseEntity responseEntity = restTemplate.exchange( + "cse://springmvc/codeFirstSpringmvc/checkQueryGenericString?str={1}&num={2}&data={3}&strExtended={4}&intExtended={5}", + HttpMethod.PUT, new HttpEntity<>(requestBody), String.class, "str2", 2, "dataTest", + "strInSubclass", 33); + TestMgr.check( + "str=str2,generic=GenericParamExtended{strExtended='strInSubclass', intExtended=33, super=" + + "GenericParam{str='str2', num=2, data=dataTest}},requestBody=GenericParam{str='str1', num=1, data=bodyPerson}", + responseEntity.getBody()); + } +} 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 new file mode 100644 index 00000000000..501ff30f1fc --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestRetrySchema.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.springmvc.client; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +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; + +@Component +public class TestRetrySchema implements CategorizedTestCase { + interface RetrySchemaInf { + boolean successWhenRetry(); + + CompletableFuture successWhenRetryAsync(); + } + + @RpcReference(microserviceName = "springmvc", schemaId = "RetrySchema") + private RetrySchemaInf retrySchemaInf; + + RestOperations restTemplate = RestTemplateBuilder.create(); + + private static final String SERVER = "servicecomb://springmvc"; + + @Override + public void testAllTransport() throws Exception { + testRetryGovernanceRestTemplate(); + testRetryGovernanceRpc(); + } + + private void testRetryGovernanceRpc() throws Exception { + TestMgr.check(retrySchemaInf.successWhenRetry(), true); + TestMgr.check(retrySchemaInf.successWhenRetry(), true); + + TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true); + TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true); + } + + private void testRetryGovernanceRestTemplate() { + TestMgr.check(restTemplate.getForObject(SERVER + "/retry/governance/successWhenRetry", boolean.class), true); + TestMgr.check(restTemplate.getForObject(SERVER + "/retry/governance/successWhenRetry", boolean.class), true); + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestSchemeInterfaceSpringmvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestSchemeInterfaceSpringmvc.java new file mode 100644 index 00000000000..03dd6c019a4 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestSchemeInterfaceSpringmvc.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.springmvc.client; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.stereotype.Component; + +@Component +public class TestSchemeInterfaceSpringmvc implements CategorizedTestCase { + @RpcReference(schemaId = "SchemeInterfaceSpringmvc", microserviceName = "springmvc") + private SchemeInterfaceSpringmvc springmvc; + + public void testAllTransport() throws Exception { + TestMgr.check(3, springmvc.add(1, 2)); + + try { + springmvc.reduce(1, 3); + TestMgr.failed("should throw exception", new Exception()); + } catch (InvocationException e) { + TestMgr.check( + "Consumer method org.apache.servicecomb.demo.springmvc.client.SchemeInterfaceSpringmvc:reduce not " + + "exist in contract, microserviceName=springmvc, " + + "schemaId=SchemeInterfaceSpringmvc.", + ((CommonExceptionData) e.getErrorData()).getMessage()); + } + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestSpringMVCCommonSchemaInterface.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestSpringMVCCommonSchemaInterface.java new file mode 100644 index 00000000000..e832cbbf5b1 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestSpringMVCCommonSchemaInterface.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.concurrent.TimeUnit; + +import org.apache.servicecomb.core.invocation.timeout.ProcessingTimeStrategy; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.CommonSchemaInterface; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.stereotype.Component; + +@Component +public class TestSpringMVCCommonSchemaInterface implements CategorizedTestCase { + @RpcReference(schemaId = "SpringMVCCommonSchemaInterface", microserviceName = "springmvc") + private CommonSchemaInterface client; + + @Override + public void testAllTransport() throws Exception { + testInvocationTimeoutInServer(); + testInvocationTimeoutInServerUserCheck(); + testInvocationAlreadyTimeoutInClient(); + testInvocationTimeoutInClientWait(); + } + + private void testInvocationTimeoutInClientWait() { + try { + long begin = System.currentTimeMillis(); + String result = client.testInvocationTimeoutInClientWait(1000, "hello"); + System.out.println("test code from testInvocationTimeoutInClientWait: " + + (System.currentTimeMillis() - begin) + ":" + result); + TestMgr.fail("should timeout"); + } catch (InvocationException e) { + TestMgr.check(408, e.getStatusCode()); + TestMgr.check("Invocation Timeout.", ((CommonExceptionData) e.getErrorData()).getMessage()); + } + } + + private void testInvocationTimeoutInServerUserCheck() { + try { + InvocationContext context = new InvocationContext(); + client.testInvocationTimeout(context, 1001, "customized"); + TestMgr.fail("should timeout"); + } catch (InvocationException e) { + TestMgr.check(408, e.getStatusCode()); + TestMgr.check("Invocation Timeout.", ((CommonExceptionData) e.getErrorData()).getMessage()); + } + } + + private void testInvocationAlreadyTimeoutInClient() { + try { + InvocationContext context = new InvocationContext(); + context.addLocalContext(ProcessingTimeStrategy.CHAIN_START_TIME, System.nanoTime()); + context.addLocalContext(ProcessingTimeStrategy.CHAIN_PROCESSING, TimeUnit.SECONDS.toNanos(2)); + + long begin = System.currentTimeMillis(); + String result = client.testInvocationTimeout(context, 10, "hello"); + System.out.println("testInvocationAlreadyTimeoutInClient from invocation timeout: " + + (System.currentTimeMillis() - begin) + ":" + result); + TestMgr.fail("should timeout"); + } catch (InvocationException e) { + TestMgr.check(408, e.getStatusCode()); + TestMgr.check("Invocation Timeout.", ((CommonExceptionData) e.getErrorData()).getMessage()); + } + } + + private void testInvocationTimeoutInServer() { + try { + client.testInvocationTimeout(1001, "hello"); + TestMgr.fail("should timeout"); + } catch (InvocationException e) { + TestMgr.check(408, e.getStatusCode()); + TestMgr.check("Invocation Timeout.", ((CommonExceptionData) e.getErrorData()).getMessage()); + } + } +} 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 new file mode 100644 index 00000000000..8dc088010fb --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestThirdPartyRegistration.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 java.util.Date; + +import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; + +@Component +public class TestThirdPartyRegistration implements BootListener, CategorizedTestCase { + @Autowired + private ThirdPartyService thirdPartyService; + + @Override + public void onAfterRegistry(BootEvent event) { + } + + @Override + public void testRestTransport() throws Exception { + Date date = new Date(); + ResponseEntity responseEntity = thirdPartyService.responseEntity(date); + TestMgr.check(date, responseEntity.getBody()); + // 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.getStatusCode().value()); + } + + @Override + public String getMicroserviceName() { + return "testServiceName"; + } +} 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 new file mode 100644 index 00000000000..0e6d810d4a5 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestUploadSchema.java @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.client; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; + +@Component +public class TestUploadSchema implements CategorizedTestCase { + + interface FileUploadMultiInf { + String fileUploadMultiRpc(List files); + } + + @RpcReference(microserviceName = "springmvc", schemaId = "UploadSchema") + private FileUploadMultiInf fileUploadMultiInf; + + @Override + public void testRestTransport() throws Exception { + testServerStartupSuccess(); + testUploadMultiBigFiles(); + testFileUploadMultiRpc(); + testUploadFileAndAttribute(); + testUploadFileAndAttributeCN(); + testUploadFileRequestPartAttribute(); + testUploadFileRequestPartAttributeList(); + } + + private void testServerStartupSuccess() { + RestOperations template = RestTemplateBuilder.create(); + boolean result = template.getForObject("servicecomb://springmvc/upload/isServerStartUpSuccess", Boolean.class); + TestMgr.check(result, true); + } + + private void testUploadMultiBigFiles() throws Exception { + final int fileNum = 5; + List files = new ArrayList<>(fileNum); + + String fileName = UUID.randomUUID().toString(); + for (int i = 0; i < fileNum; i++) { + File tempFile = new File("random-client-" + fileName + i); + files.add(tempFile); + FileOutputStream fo = new FileOutputStream(tempFile); + byte[] data = new byte[1024 * 1024 * 10]; + Arrays.fill(data, (byte) 33); + IOUtils.write(data, fo); + fo.close(); + } + + RestOperations template = RestTemplateBuilder.create(); + + MultiValueMap map = new LinkedMultiValueMap<>(); + for (int i = 0; i < fileNum; i++) { + map.add("files", new FileSystemResource(files.get(i))); + } + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA); + HttpEntity> entity = new HttpEntity<>(map, headers); + + String result = template.postForObject("servicecomb://springmvc/upload/fileUpload", entity, String.class); + TestMgr.check(result, "success"); + + files.forEach(File::delete); + } + + private void testFileUploadMultiRpc() throws IOException { + File file1 = File.createTempFile("file1", ".txt"); + File file2 = File.createTempFile("file2", ".txt"); + List files = new ArrayList<>(); + files.add(new FileSystemResource(file1)); + files.add(new FileSystemResource(file2)); + String result = fileUploadMultiInf.fileUploadMultiRpc(files); + TestMgr.check(result, "fileUploadMulti success, and fileNum is 2"); + } + + private void testUploadFileAndAttribute() throws Exception { + RestOperations template = RestTemplateBuilder.create(); + Map map = new HashMap<>(); + String message = "hi"; + File file = File.createTempFile("file", ".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 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 { + RestOperations template = RestTemplateBuilder.create(); + Map map = new HashMap<>(); + String message = "hi"; + File file = File.createTempFile("file", ".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/uploadFileRequestPartAttribute", + 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 new file mode 100644 index 00000000000..df9f8236215 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestWeakSpringmvc.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.demo.springmvc.client; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.core.provider.consumer.InvokerUtils; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.model.SpecialNameModel; +import org.apache.servicecomb.demo.server.GenericsModel; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.apache.servicecomb.provider.pojo.RpcReference; +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.RestOperations; + +import com.fasterxml.jackson.core.JsonProcessingException; + +/* +This is the provider definition: + + + @GetMapping(path = "/diffNames") + @ApiOperation(value = "differentName", nickname = "differentName") + public int diffNames(@RequestParam("x") int a, @RequestParam("y") int b) + + +and the swagger is: + + + paths: + /diffNames: + get: + summary: "differentName" + operationId: "differentName" + parameters: + - name: "x" + in: "query" + required: true + type: "integer" + format: "int32" + - name: "y" + in: "query" + required: true + type: "integer" + format: "int32" + responses: + "200": + description: "response of 200" + schema: + type: "integer" + format: "int32" + + +In consumer, you can define any prototype that matches generated swagger of provider. + */ + +interface DiffNames { + int differentName(int x, int y); +} + +interface DiffNames2 { + int differentName(int y, int x); +} + +interface Generics { + List> genericParams(int code, List> names); +} + +interface GenericsModelInf { + GenericsModel genericParamsModel(int code, GenericsModel model); +} + +interface SpecialNameModelInf { + SpecialNameModel specialNameModel(int code, SpecialNameModel model); +} + +@Component +public class TestWeakSpringmvc implements CategorizedTestCase { + @Value("${servicecomb.service.application}") + private String applicationName; + + @RpcReference(microserviceName = "springmvc", schemaId = "weakSpringmvc") + private DiffNames diffNames; + + @RpcReference(microserviceName = "springmvc", schemaId = "weakSpringmvc") + private DiffNames2 diffNames2; + + @RpcReference(microserviceName = "springmvc", schemaId = "weakSpringmvc") + private Generics generics; + + @RpcReference(microserviceName = "springmvc", schemaId = "weakSpringmvc") + private GenericsModelInf genericsModelInf; + + @RpcReference(microserviceName = "springmvc", schemaId = "weakSpringmvc") + private SpecialNameModelInf specialNameModelInf; + + @RpcReference(microserviceName = "${servicecomb.service.application}:springmvc", schemaId = "weakSpringmvc") + private SpecialNameModelInf specialNameModelInfWithAppId; + + private RestOperations restTemplate = RestTemplateBuilder.create(); + + @Override + public void testAllTransport() throws Exception { + testDiffName(); + + testGenerics(); + + testGenericsModel(); + + testSpecialNameModel(); + } + + private void testSpecialNameModel() { + SpecialNameModel model = new SpecialNameModel(); + model.setaIntName(30); + SpecialNameModel result = specialNameModelInf.specialNameModel(0, model); + TestMgr.check(30, result.getaIntName()); + result = specialNameModelInfWithAppId.specialNameModel(0, model); + TestMgr.check(30, result.getaIntName()); + } + + private void testGenericsModel() throws JsonProcessingException { + GenericsModel model = new GenericsModel(); + model.setName("model"); + List> namesList = new ArrayList<>(); + List names = new ArrayList<>(); + names.add("hello"); + namesList.add(names); + model.setNameList(namesList); + List>> objectLists = new ArrayList<>(); + List> objectList = new ArrayList<>(); + List objects = new ArrayList<>(); + objects.add("object"); + objectList.add(objects); + objectLists.add(objectList); + model.setObjectLists(objectLists); + GenericsModel result = genericsModelInf.genericParamsModel(100, model); + TestMgr.check(JsonUtils.writeValueAsString(model), JsonUtils.writeValueAsString(result)); + } + + private void testGenerics() { + List> namesList = new ArrayList<>(); + List names = new ArrayList<>(); + names.add("hello"); + namesList.add(names); + List> nameListResult = generics.genericParams(100, namesList); + TestMgr.check(1, nameListResult.size()); + TestMgr.check(1, nameListResult.get(0).size()); + TestMgr.check("hello", nameListResult.get(0).get(0)); + } + + private void testDiffName() { + TestMgr.check(7, diffNames.differentName(2, 3)); + TestMgr.check(8, diffNames2.differentName(2, 3)); + TestMgr.check(7, restTemplate.getForObject("cse://springmvc/weakSpringmvc/diffNames?x=2&y=3", Integer.class)); + TestMgr.check(7, restTemplate + .getForObject("cse://" + applicationName + ":springmvc/weakSpringmvc/diffNames?x=2&y=3", + Integer.class)); + Map args = new HashMap<>(); + args.put("x", 2); + args.put("y", 3); + TestMgr.check(7, InvokerUtils.syncInvoke("springmvc", + "weakSpringmvc", "differentName", args, Integer.class)); + TestMgr.check(7, InvokerUtils.syncInvoke(applicationName + ":springmvc", + "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 new file mode 100644 index 00000000000..e31e1183333 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdPartyService.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.demo.springmvc.client; + +import java.util.Date; + +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.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 { + @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 new file mode 100644 index 00000000000..3cbd8dd97dd --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdSvc.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ArrayList; +import java.util.Date; +import java.util.List; + +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.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 { + @Autowired + Environment environment; + + @RequestMapping(path = "/codeFirstSpringmvc") + public interface ThirdSvcClient { + @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))))); + } + + @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)))); + } + + @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/java/org/apache/servicecomb/demo/springmvc/decoderesponse/DecodeTestResponse.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/decoderesponse/DecodeTestResponse.java new file mode 100644 index 00000000000..f27c15d004a --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/decoderesponse/DecodeTestResponse.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.decoderesponse; + +import java.util.Date; + +public class DecodeTestResponse { + private Date content; + + public Date getContent() { + return content; + } + + public void setContent(Date content) { + this.content = content; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("DecodeTestResponse{"); + sb.append("content=").append(content); + sb.append('}'); + return sb.toString(); + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/resources/META-INF/spring/springmvc.client.bean.xml b/demo/demo-springmvc/springmvc-client/src/main/resources/META-INF/spring/springmvc.client.bean.xml index 62af0a28f58..71ab6615ec1 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/resources/META-INF/spring/springmvc.client.bean.xml +++ b/demo/demo-springmvc/springmvc-client/src/main/resources/META-INF/spring/springmvc.client.bean.xml @@ -1,12 +1,13 @@ - - - + - \ No newline at end of file + + + diff --git a/demo/demo-springmvc/springmvc-client/src/main/resources/SpringMVCSchema.yaml b/demo/demo-springmvc/springmvc-client/src/main/resources/SpringMVCSchema.yaml new file mode 100644 index 00000000000..db67a5779a9 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/resources/SpringMVCSchema.yaml @@ -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. +## --------------------------------------------------------------------------- + +################################################################################ +################################################################################ +################################################################################ +################################################################################ +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.demo.springmvc.client.SpringMVCSchema + version: 1.0.0 +servers: +- url: /springMvcSchema +paths: + /testApiExample: + post: + operationId: testApiExample + requestBody: + content: + application/json: + schema: + type: string + required: true + x-name: name + responses: + "200": + description: success + headers: {} + content: + application/json: + schema: + type: string + description: "" + nullable: false + example: wget http://localhost/springMvcSchema/testApiExample + /testDefaultGetApiExample: + get: + operationId: testDefaultGetApiExample + parameters: + - name: name + in: query + required: true + schema: + type: string + responses: + "200": + 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/log4j2.xml b/demo/demo-springmvc/springmvc-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 636dfb6a474..2fe0f881f55 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml +++ b/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml @@ -1,32 +1,131 @@ -APPLICATION_ID: springmvctest -service_description: - name: springmvcClient - version: 0.0.1 -cse: +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: - registry: - address: http://127.0.0.1:9980 - handler: - chain: - Consumer: - default: loadbalance,bizkeeper-consumer + application: springmvctest + name: springmvcClient + version: 0.0.1 + registry: + sc: + address: http://127.0.0.1:30100 + autodiscovery: true + healthCheckIntervalInSeconds: 3 + pollIntervalInMillis: 90000 + watch: true + + rest: + client: + connection: + compression: true + references: + version-rule: 0+ + request: + clientRequestHeaderFilterEnabled: + # Do not copy cse-context to target for microservice(testServiceName) + testServiceName: true + invocation: + timeout: + check: + enabled: true + strategy: processing-time + springmvc: + SpringMVCCommonSchemaInterface: + testInvocationTimeoutInClientWait: + timeout: 300 tracing: enabled: true samplingRate: 0.5 loadbalance: - retryEnabled: true - retryOnSame: 1 - retryOnNext: 1 - serverListFilters: zoneaware - serverListFilter: - zoneaware: - className: io.servicecomb.loadbalance.filter.ZoneAwareServerListFilterExt + userDefinedEndpoint.enabled: true + strategy: + name: WeightedResponse + filter.status.enabled: false + fallbackpolicy: + Consumer: + springmvc: + codeFirst: + fallbackFromCache: + policy: fromCache + fallbackReturnNull: + policy: returnNull + fallbackThrowException: + policy: throwException + fallbackForce: + policy: mycustom + fallback: + Consumer: + springmvc: + codeFirst: + fallbackForce: + force: true datacenter: name: myDC region: my-Region availableZone: my-Zone + faultInjection: + enabled: true + + governance: + Consumer: + springmvc: + schemas: + codeFirst: + operations: + testDelay: + policy: + fault: + protocols: + rest: + delay: + fixedDelay: 10 + percent: 50 + testAbort: + policy: + fault: + protocols: + rest: + abort: + httpStatus: 421 + percent: 50 + + # 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] + retry-success: | + maxAttempts: 2 + retryOnResponseStatus: [503] + #########SSL options +# open jdk 8 now TLSv1.3 not available +# ssl.protocols: TLSv1.3 +# ssl.ciphers: TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384 ssl.protocols: TLSv1.2 ssl.authPeer: true ssl.checkCN.host: false @@ -39,4 +138,4 @@ ssl.keyStore: server.p12 ssl.keyStoreType: PKCS12 ssl.keyStoreValue: Changeme_123 ssl.crl: revoke.crl -ssl.sslCustomClass: io.servicecomb.demo.DemoSSLCustom +ssl.sslCustomClass: org.apache.servicecomb.demo.DemoSSLCustom diff --git a/demo/demo-springmvc/springmvc-client/src/test/java/io/servicecomb/demo/springmvc/SpringMvcIT.java b/demo/demo-springmvc/springmvc-client/src/test/java/io/servicecomb/demo/springmvc/SpringMvcIT.java deleted file mode 100644 index 360660e8672..00000000000 --- a/demo/demo-springmvc/springmvc-client/src/test/java/io/servicecomb/demo/springmvc/SpringMvcIT.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.servicecomb.demo.springmvc; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -import org.junit.Before; -import org.junit.Test; - -import io.servicecomb.demo.TestMgr; -import io.servicecomb.demo.springmvc.client.SpringmvcClient; - -public class SpringMvcIT { - - @Before - public void setUp() throws Exception { - TestMgr.errors().clear(); - } - - @Test - public void clientGetsNoError() throws Exception { - SpringmvcClient.main(new String[0]); - - assertThat(TestMgr.errors().isEmpty(), is(true)); - } -} 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 new file mode 100644 index 00000000000..c445ce1a647 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/test/java/org/apache/servicecomb/demo/springmvc/SpringMvcIT.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.demo.springmvc; + +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 = SpringmvcClient.class) +public class SpringMvcIT { + @BeforeEach + public void setUp() { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() { + SpringmvcClient.run(); + Assertions.assertTrue(TestMgr.isSuccess()); + } +} diff --git a/demo/demo-springmvc/springmvc-server/pom.xml b/demo/demo-springmvc/springmvc-server/pom.xml index 0b028ccc756..f88876261d6 100644 --- a/demo/demo-springmvc/springmvc-server/pom.xml +++ b/demo/demo-springmvc/springmvc-server/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb.demo - demo-springmvc - 0.3.0-SNAPSHOT - - springmvc-server + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + + org.apache.servicecomb.demo + demo-springmvc + 3.4.0-SNAPSHOT + + springmvc-server + Java Chassis::Demo::Spring MVC::Server - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-springmvc - - + + + org.apache.servicecomb + metrics-prometheus + + - - io.servicecomb.demo.springmvc.server.SpringmvcServer - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - io.servicecomb.demo - docker-build-config - 0.3.0-SNAPSHOT - - - - - - + + org.apache.servicecomb.demo.springmvc.SpringmvcServer + + + + + org.springframework.boot + spring-boot-maven-plugin + + + - - - docker - - - - io.fabric8 - docker-maven-plugin - - - org.commonjava.maven.plugins - directory-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-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java b/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java deleted file mode 100644 index 3c9f09102cd..00000000000 --- a/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.springmvc.server; - -import java.util.Date; -import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Response.Status; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.CookieValue; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestAttribute; -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.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.demo.compute.Person; -import io.servicecomb.demo.server.User; -import io.servicecomb.provider.rest.common.RestSchema; -import io.servicecomb.swagger.extend.annotations.ResponseHeaders; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.context.ContextUtils; -import io.servicecomb.swagger.invocation.context.InvocationContext; -import io.servicecomb.swagger.invocation.response.Headers; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ResponseHeader; - -@RestSchema(schemaId = "codeFirst") -@RequestMapping(path = "/codeFirstSpringmvc", produces = MediaType.APPLICATION_JSON_VALUE) -public class CodeFirstSpringmvc { - @ResponseHeaders({@ResponseHeader(name = "h1", response = String.class), - @ResponseHeader(name = "h2", response = 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().toString()); - - InvocationContext c2 = ContextUtils.getInvocationContext(); - headers.add("h2", "h2v " + c2.getContext().toString()); - - return new ResponseEntity(date, headers, HttpStatus.ACCEPTED); - } - - @ApiResponse(code = 200, response = User.class, message = "") - @ResponseHeaders({@ResponseHeader(name = "h1", response = String.class), - @ResponseHeader(name = "h2", response = String.class)}) - @RequestMapping(path = "/cseResponse", method = RequestMethod.GET) - public Response cseResponse(InvocationContext c1) { - Response response = Response.createSuccess(Status.ACCEPTED, new User()); - Headers headers = response.getHeaders(); - headers.addHeader("h1", "h1v " + c1.getContext().toString()); - - InvocationContext c2 = ContextUtils.getInvocationContext(); - headers.addHeader("h2", "h2v " + c2.getContext().toString()); - - return response; - } - - @RequestMapping(path = "/testUserMap", method = RequestMethod.POST) - public Map testUserMap(@RequestBody Map userMap) { - return userMap; - } - - @RequestMapping(path = "/textPlain", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE) - public String textPlain(@RequestBody String body) { - return body; - } - - @RequestMapping(path = "/bytes", method = RequestMethod.POST) - public byte[] bytes(@RequestBody byte[] input) { - input[0] = (byte) (input[0] + 1); - return input; - } - - @RequestMapping(path = "/addDate", method = RequestMethod.POST) - public Date addDate(@RequestAttribute("date") Date date, @QueryParam("seconds") long seconds) { - return new Date(date.getTime() + seconds * 1000); - } - - @RequestMapping(path = "/add", method = RequestMethod.POST) - public int add(@RequestAttribute("a") int a, @RequestAttribute("b") int b) { - return a + b; - } - - @RequestMapping(path = "/reduce", method = RequestMethod.GET) - @ApiImplicitParams({@ApiImplicitParam(name = "a", dataType = "integer", format = "int32", paramType = "query")}) - public int reduce(HttpServletRequest request, @CookieValue(name = "b") int b) { - int a = Integer.parseInt(request.getParameter("a")); - return a - b; - } - - @RequestMapping(path = "/sayhello", method = RequestMethod.POST) - public Person sayHello(@RequestBody Person user) { - user.setName("hello " + user.getName()); - return user; - } - - @SuppressWarnings("unchecked") - @RequestMapping(path = "/testrawjson", method = RequestMethod.POST) - public String testRawJsonString(String jsonInput) { - Map person; - try { - person = RestObjectMapper.INSTANCE.readValue(jsonInput.getBytes(), Map.class); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - return "hello " + person.get("name"); - } - - @RequestMapping(path = "/saysomething", method = RequestMethod.POST) - public String saySomething(@RequestHeader(name = "prefix") String prefix, @RequestBody Person user) { - return prefix + " " + user.getName(); - } - - @RequestMapping(path = "/sayhi/{name}", method = RequestMethod.PUT) - public String sayHi(@PathVariable(name = "name") String name) { - ContextUtils.getInvocationContext().setStatus(202); - return name + " sayhi"; - } - - @RequestMapping(path = "/sayhi/{name}/v2", method = RequestMethod.PUT) - public String sayHi2(@PathVariable(name = "name") String name) { - return name + " sayhi 2"; - } - - @RequestMapping(path = "/istrue", method = RequestMethod.GET) - public boolean isTrue() { - return true; - } - - @RequestMapping(path = "/addstring", method = RequestMethod.DELETE, produces = MediaType.TEXT_PLAIN_VALUE) - public String addString(@RequestParam(name = "s") List s) { - String result = ""; - for (String x : s) { - result += x; - } - return result; - } -} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/ControllerImpl.java b/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/ControllerImpl.java deleted file mode 100644 index 919397dec09..00000000000 --- a/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/ControllerImpl.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.springmvc.server; - -import java.util.Arrays; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.MediaType; - -import org.springframework.web.bind.annotation.PathVariable; -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.servicecomb.demo.controller.Person; -import io.servicecomb.provider.rest.common.RestSchema; - -@RestSchema(schemaId = "controller") -@RequestMapping(path = "/controller", produces = MediaType.APPLICATION_JSON) -public class ControllerImpl { - @RequestMapping(path = "/add", method = RequestMethod.GET) - public int add(@RequestParam("a") int a, @RequestParam("b") int b) { - return a + b; - } - - @RequestMapping(path = "/sayhello/{name}", method = RequestMethod.POST) - public String sayHello(@PathVariable("name") String name) { - return "hello " + name; - } - - @RequestMapping(path = "/saysomething", method = RequestMethod.POST) - public String saySomething(String prefix, @RequestBody Person user) { - return prefix + " " + user.getName(); - } - - @RequestMapping(path = "/sayhi", method = RequestMethod.GET) - public String sayHi(HttpServletRequest request) { - String[] values = request.getParameterValues("name"); - if (values != null && values.length > 0 && values[0].equals("throwexception")) { - throw new RuntimeException(); - } - return "hi " + request.getParameter("name") + " " + Arrays.toString(values); - } - - @RequestMapping(path = "/sayhei", method = RequestMethod.GET) - public String sayHei(@RequestHeader("name") String name) { - return "hei " + name; - } -} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/SpringmvcServer.java b/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/SpringmvcServer.java deleted file mode 100644 index b5010fd5558..00000000000 --- a/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/SpringmvcServer.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.springmvc.server; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class SpringmvcServer { - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} 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 new file mode 100644 index 00000000000..52ede2792d8 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcServer.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.springmvc; + +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 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/decoderesponse/DecodeTestResponse.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/decoderesponse/DecodeTestResponse.java new file mode 100644 index 00000000000..9a19dc19084 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/decoderesponse/DecodeTestResponse.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.demo.springmvc.decoderesponse; + +public class DecodeTestResponse { + private String content; + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("DecodeTestResponse{"); + sb.append("content='").append(content).append('\''); + sb.append('}'); + return sb.toString(); + } +} 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 new file mode 100644 index 00000000000..310f68e0ca0 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/filter/ProviderTestFilter.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.demo.springmvc.filter; + + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.swagger.invocation.Response; +import org.springframework.stereotype.Component; + +@Component +public class ProviderTestFilter extends AbstractFilter implements ProviderFilter { + @Override + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1800; + } + + @Override + public String getName() { + return "test-provider"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + invocation.addContext("k", "v"); + return nextNode.onFilter(invocation); + } +} 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/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/BizkeeperTest.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/BizkeeperTest.java new file mode 100644 index 00000000000..47026122f88 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/BizkeeperTest.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.demo.springmvc.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; +import org.springframework.web.bind.annotation.RequestParam; + +@RestSchema(schemaId = "codeFirstBizkeeperTest") +@RequestMapping(path = "/codeFirstBizkeeperTest", produces = MediaType.APPLICATION_JSON_VALUE) +public class BizkeeperTest { + @GetMapping(path = "/testTimeout") + public String testTimeout(@RequestParam("name") String name, @RequestParam("delaytime") long delaytime) { + try { + Thread.sleep(delaytime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return name; + } +} 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 new file mode 100644 index 00000000000..36f8518afe4 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java @@ -0,0 +1,600 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.commons.io.IOUtils; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +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; +import org.apache.servicecomb.demo.compute.GenericParamExtended; +import org.apache.servicecomb.demo.compute.GenericParamWithJsonIgnore; +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.demo.springmvc.decoderesponse.DecodeTestResponse; +import org.apache.servicecomb.foundation.common.Holder; +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.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; +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.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +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 org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; + +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) +public class CodeFirstSpringmvc { + private static final Logger LOGGER = LoggerFactory.getLogger(CodeFirstSpringmvc.class); + + private AtomicInteger invocationCounter = new AtomicInteger(0); + + private String _fileUpload(MultipartFile file1, Part file2) { + 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.getOriginalFilename(), + file1.getContentType(), + content1, + file2.getSubmittedFileName(), + file2.getContentType(), + content2); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + + @GetMapping(path = "/retrySuccess") + public int retrySuccess(@RequestParam("a") int a, @RequestParam("b") int b) { + if (invocationCounter.getAndIncrement() % 3 != 0) { + throw new InvocationException(Status.SERVICE_UNAVAILABLE, "try again later."); + } + return a + b; + } + + @PostMapping(path = "/upload1", produces = MediaType.TEXT_PLAIN_VALUE) + public String fileUpload1(@RequestPart(name = "file1") MultipartFile file1) throws IOException { + try (InputStream is = file1.getInputStream()) { + return IOUtils.toString(is, StandardCharsets.UTF_8); + } + } + + @PostMapping(path = "/upload", produces = MediaType.TEXT_PLAIN_VALUE) + public String fileUpload(@RequestPart(name = "file1") MultipartFile file1, + @RequestPart(name = "someFile") Part file2) { + return _fileUpload(file1, file2); + } + + @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(CoreConst.SRC_MICROSERVICE)); + + InvocationContext c2 = ContextUtils.getInvocationContext(); + headers.add("h2", "h2v " + c2.getContext().get(CoreConst.SRC_MICROSERVICE)); + + return new ResponseEntity<>(date, headers, HttpStatus.ACCEPTED); + } + + @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); + } + + // This definition is not correct, response type is not + // same as the actual one. May be not support in future. + @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(CoreConst.SRC_MICROSERVICE)); + + InvocationContext c2 = ContextUtils.getInvocationContext(); + 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(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(CoreConst.SRC_MICROSERVICE)); + + InvocationContext c2 = ContextUtils.getInvocationContext(); + response.addHeader("h2", "h2v " + c2.getContext().get(CoreConst.SRC_MICROSERVICE)); + + return response; + } + + @PostMapping(path = "/testUserMap") + public Map testUserMap(@RequestBody Map userMap) { + return userMap; + } + + @RequestMapping(path = "/textPlain", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE) + public String textPlain(@RequestBody String body) { + return body; + } + + @RequestMapping(path = "/bytes", method = RequestMethod.POST) + public byte[] bytes(@RequestBody byte[] input) { + input[0] = (byte) (input[0] + 1); + return input; + } + + @RequestMapping(path = "/addDate", method = RequestMethod.POST) + public Date addDate(@RequestAttribute("date") Date date, @QueryParam("seconds") long seconds) { + return new Date(date.getTime() + seconds * 1000); + } + + // this should be ignored as it's hidden + @Operation(summary = "", hidden = true, method = "POST") + public int add(@RequestParam("a") int a) { + return a; + } + + @RequestMapping(path = "/add", method = RequestMethod.POST) + public int add(@RequestAttribute("a") int a, @RequestAttribute("b") int b) { + return a + b; + } + + @GetMapping(path = "/reduce") + @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; + } + + @RequestMapping(path = "/sayhello", method = RequestMethod.POST) + public Person sayHello(@RequestBody Person user) { + user.setName("hello " + user.getName()); + return user; + } + + @SuppressWarnings("unchecked") + @RequestMapping(path = "/testrawjson", method = RequestMethod.POST) + public String testRawJsonString(String jsonInput) { + Map person; + try { + person = RestObjectMapperFactory.getRestObjectMapper() + .readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + return "hello " + person.get("name"); + } + + @RequestMapping(path = "/saysomething", method = RequestMethod.POST) + public String saySomething(@RequestHeader(name = "prefix") String prefix, @RequestBody Person user) { + return prefix + " " + user.getName(); + } + + @PutMapping(path = "/sayhi/{name}") + public String sayHi(@PathVariable(name = "name") String name) { + ContextUtils.getInvocationContext().setStatus(202); + return name + " sayhi"; + } + + @RequestMapping(path = "/sayhi/compressed/{name}/v2", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE) + public String sayHiForCompressed(@PathVariable(name = "name") String name) { + String bigText = + "This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text," + + "This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text," + + "This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text," + + "This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text," + + "This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text," + + "This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text,This is a big text!"; + return name + " sayhi compressed:" + bigText; + } + + @RequestMapping(path = "/sayhi/{name}/v2", method = RequestMethod.PUT) + public String sayHi2(@PathVariable(name = "name") String name) { + return name + " sayhi 2"; + } + + @RequestMapping(path = "/istrue", method = RequestMethod.GET) + public boolean isTrue() { + return true; + } + + @DeleteMapping(path = "/addstring", produces = MediaType.TEXT_PLAIN_VALUE) + public String addString(@RequestParam(name = "s") List s) { + StringBuilder result = new StringBuilder(); + for (String x : s) { + result.append(x); + } + return result.toString(); + } + + // Using 490, 590 error code, the response type should be CommonExceptionData. Or we need + // complex ExceptionConverters to deal with exceptions thrown by Handlers, etc. + @RequestMapping(path = "/fallback/returnnull/{name}", method = RequestMethod.GET) + @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")); + } + return name; + } + + @RequestMapping(path = "/fallback/throwexception/{name}", method = RequestMethod.GET) + @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")); + } + return name; + } + + @RequestMapping(path = "/fallback/fromcache/{name}", method = RequestMethod.GET) + @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")); + } + return name; + } + + @RequestMapping(path = "/fallback/force/{name}", method = RequestMethod.GET) + @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")); + } + return name; + } + + public enum NameType { + abc, + def + } + + @RequestMapping(path = "/testenum/{name}", method = RequestMethod.GET) + @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(); + } + + @RequestMapping(method = RequestMethod.POST, value = "/ignore") + @ResponseBody + public OutputModelForTestIgnore testModelWithIgnoreField(@RequestBody InputModelForTestIgnore input) { + return new OutputModelForTestIgnore("output_id", input.getInputId(), input.getContent(), input.getInputObject(), + input.getInputJsonObject(), input.getInputIgnoreInterface(), + new Person("outputSomeone"), new JsonObject("{\"OutputJsonKey\" : \"OutputJsonValue\"}"), () -> { + }); + } + + @SuppressWarnings("unchecked") + @RequestMapping(method = RequestMethod.POST, value = "/rawJsonAnnotation") + @ResponseBody + public String testRawJsonAnnotation(@RawJsonRequestBody String jsonInput) { + Map person; + try { + person = RestObjectMapperFactory.getRestObjectMapper() + .readValue(jsonInput.getBytes(StandardCharsets.UTF_8), Map.class); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + return "hello " + person.get("name"); + } + + @PostMapping(path = "/testform") + @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"); + Assert.notNull(form1, "from1 is null"); + return form1 + form2; + } + + @Autowired + MetricsBootListener metricsBootListener; + + //Only for Prometheus integration test + @RequestMapping(path = "/prometheusForTest", method = RequestMethod.GET) + public String prometheusForTest() { + // just for test, this makes client always can got latest metrics + metricsBootListener.getMetricsBootstrap().pollMeters(); + + RestTemplate defaultRestTemplate = new RestTemplate(); + return defaultRestTemplate.getForObject("http://localhost:9696/metrics", String.class); + } + + @GetMapping(path = "/traceId") + public String getTraceId() { + return ContextUtils.getInvocationContext().getContext(CoreConst.TRACE_ID_NAME); + } + + @PostMapping(path = "/emptyObject") + public EmptyObject testEmpty(@RequestBody EmptyObject input) { + return input; + } + + @PostMapping(path = "/object") + public Object testObject(@RequestBody Object input) { + return input; + } + + @PostMapping(path = "/mapObject") + public Map testMapObject(@RequestBody Map input) { + return input; + } + + @PostMapping(path = "/listObject") + public List testListObject(@RequestBody List input) { + return input; + } + + @PostMapping(path = "/holderObject") + public Holder testHolderObject(@RequestBody Holder input) { + return input; + } + + @PostMapping(path = "/holderUser") + public Holder testHolderUser(@RequestBody Holder input) { + Assert.isInstanceOf(Holder.class, input); + Assert.isInstanceOf(User.class, input.value); + return input; + } + + @PostMapping(path = "/genericUser") + public Generic testGenericUser(@RequestBody Generic input) { + Assert.isInstanceOf(Generic.class, input); + Assert.isInstanceOf(User.class, input.value); + return input; + } + + @PostMapping(path = "/genericLong") + public Generic testGenericLong(@RequestBody Generic input) { + Assert.isInstanceOf(Generic.class, input); + Assert.isInstanceOf(Long.class, input.value); + return input; + } + + @PostMapping(path = "/genericDate") + public Generic testGenericDate(@RequestBody Generic input) { + Assert.isInstanceOf(Generic.class, input); + Assert.isInstanceOf(Date.class, input.value); + System.out.println(input.value); + return input; + } + + @PostMapping(path = "/genericEnum") + public Generic testGenericEnum(@RequestBody Generic input) { + Assert.isInstanceOf(Generic.class, input); + Assert.isInstanceOf(HttpStatus.class, input.value); + return input; + } + + @PostMapping(path = "/genericGenericUser") + public Generic> testGenericGenericUser(@RequestBody Generic> input) { + Assert.isInstanceOf(Generic.class, input); + Assert.isInstanceOf(Generic.class, input.value); + Assert.isInstanceOf(User.class, input.value.value); + return input; + } + + private volatile boolean testvoidInRPCSuccess = false; + + @GetMapping(path = "/testvoidInRPC") + public void testvoidInRPC() { + LOGGER.info("testvoidInRPC() is called!"); + testvoidInRPCSuccess = true; + } + + private volatile boolean testVoidInRPCSuccess = false; + + @GetMapping(path = "/testVoidInRPC") + public Void testVoidInRPC() { + LOGGER.info("testVoidInRPC() is called!"); + testVoidInRPCSuccess = true; + return null; + } + + private volatile boolean testvoidInRestTemplateSuccess = false; + + @GetMapping(path = "/testvoidInRestTemplate") + public void testvoidInRestTemplate() { + LOGGER.info("testvoidInRestTemplate() is called!"); + testvoidInRestTemplateSuccess = true; + } + + private volatile boolean testVoidInRestTemplateSuccess = false; + + @GetMapping(path = "/testVoidInRestTemplate") + public Void testVoidInRestTemplate() { + LOGGER.info("testVoidInRestTemplate() is called!"); + testVoidInRestTemplateSuccess = true; + return null; + } + + @GetMapping(path = "/checkVoidResult") + public boolean checkVoidResult() { + LOGGER.info("checkVoidResult() is called!"); + return testvoidInRPCSuccess && testVoidInRPCSuccess && testvoidInRestTemplateSuccess + && testVoidInRestTemplateSuccess; + } + + /** + * Simple query object test, users can use it mixed with InvocationContext and plain query param, RequestBody + */ + @PostMapping(path = "/checkQueryObject") + public String checkQueryObject(Person person, @RequestParam(name = "otherName") String otherName, + InvocationContext invocationContext, @RequestBody Person requestBody) { + LOGGER.info("checkQueryObject() is called!"); + return "invocationContext_is_null=" + (null == invocationContext) + ",person=" + + person + ",otherName=" + otherName + ",name=" + person.getName() + ",requestBody=" + requestBody; + } + + /** + * For the nesting object params, including the generic params whose generic field is an object, + * the inner object field is not supported. + */ + @PutMapping(path = "/checkQueryGenericObject") + public String checkQueryGenericObject(@RequestBody GenericParam requestBody, + GenericParamWithJsonIgnore generic) { + LOGGER.info("checkQueryGenericObject() is called!"); + return "str=" + generic.getStr() + ",generic=" + generic + ",requestBody=" + requestBody; + } + + /** + * If the generic field is simple type, it's supported to be deserialized. + * The same for those simple type field inherited from the parent class. + */ + @PutMapping(path = "/checkQueryGenericString") + public String checkQueryGenericString(@RequestBody GenericParam requestBody, + GenericParamExtended generic) { + LOGGER.info("checkQueryGenericObject() is called!"); + return "str=" + generic.getStr() + ",generic=" + generic + ",requestBody=" + requestBody; + } + + @GetMapping(path = "/testDelay") + public String testDelay() { + LOGGER.info("testDelay() is called!"); + return "OK"; + } + + @GetMapping(path = "/testAbort") + public String testAbort() { + LOGGER.info("testAbort() is called!"); + return "OK"; + } + + @GetMapping(path = "/testDecodeResponseError") + public DecodeTestResponse testDecodeResponseError() { + DecodeTestResponse response = new DecodeTestResponse(); + response.setContent("returnOK"); + return response; + } + + @RequestMapping(path = "/testDefaultGetApiExample") + public void testDefaultGetApiExample() { + LOGGER.info("testDefaultGetApiExample() is called!"); + } + + @RequestMapping(path = "/testHolder") + public void testHolder(@RequestBody Holder name) { + LOGGER.info("testDefaultGetApiExample() is called!"); + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvcForSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvcForSchema.java new file mode 100644 index 00000000000..7b5c7e4f30b --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvcForSchema.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.server; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; + +/** + * Created for testing schema to accommodate swagger api specification. When change this class, please use + * http://editor.swagger.io/ to validate the generated schema and should not give any errors. In test case, + * only checksum is validated to make sure schema is not changed. + */ +@RestSchema(schemaId = "CodeFirstSpringmvcForSchema") +@RequestMapping(path = "/forSchema") +public class CodeFirstSpringmvcForSchema { + /* + * Using http://editor.swagger.io/ . Listing errors not handled: + * + * #1. Should NOT have additional properties additionalProperty: type, format, name, in, required + /reduce: + get: + operationId: "reduce" + parameters: + - name: "b" + in: "cookie" + required: false + This schema gives error, but according to https://swagger.io/docs/specification/describing-parameters/#cookie-parameters + This should supported. + + + * #2. Operations with Parameters of "in: formData" must include "application/x-www-form-urlencoded" or "multipart/form-data" in their "consumes" property + This error can be fixed by user code. + + + * + */ + + + @RequestMapping(path = "/uploadFile", method = RequestMethod.POST, + consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public boolean uploadAwardFile(@RequestParam("fileType") String fileType, @RequestParam("zoneId") String zoneId, + @RequestPart("file") MultipartFile file) { + throw new UnsupportedOperationException("only for testing schema"); + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/Compatible1xTestSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/Compatible1xTestSchema.java new file mode 100644 index 00000000000..41f034dfcab --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/Compatible1xTestSchema.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.demo.springmvc.server; + +import jakarta.ws.rs.core.MediaType; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.provider.rest.common.RestSchema; +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.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RestSchema(schemaId = "Compatible1xTestSchema") +@RequestMapping(path = "/compatible", produces = MediaType.APPLICATION_JSON) +public class Compatible1xTestSchema { + @GetMapping(path = "/parameterName") + public String parameterName(@RequestParam(name = "a", defaultValue = "10") int a, + @RequestParam(name = "b", defaultValue = "10") int b) { + return ContextUtils.getInvocationContext().getContext(CoreConst.SRC_MICROSERVICE) + a + b * 2; + } + + @GetMapping(path = "/parameterNameServerContext") + public String parameterNameServerContext(InvocationContext context, + @RequestParam(name = "a", defaultValue = "10") int a, + @RequestParam(name = "b", defaultValue = "10") int b) { + return context.getContext(CoreConst.SRC_MICROSERVICE) + a + b * 2; + } + + @GetMapping(path = "/beanParameter") + public String beanParameter(CompatibleQueryBean bean) { + return bean.getName() + bean.getAge(); + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CompatibleQueryBean.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CompatibleQueryBean.java new file mode 100644 index 00000000000..65135037278 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CompatibleQueryBean.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.demo.springmvc.server; + +public class CompatibleQueryBean { + private String name; + + private int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ConfigurationProblemsCollectorTest.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ConfigurationProblemsCollectorTest.java new file mode 100644 index 00000000000..7f020450d4e --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ConfigurationProblemsCollectorTest.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.demo.springmvc.server; + +import org.apache.servicecomb.core.bootup.ConfigurationProblemsAlarmEvent; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.springframework.stereotype.Component; + +import com.google.common.eventbus.Subscribe; + +@Component +public class ConfigurationProblemsCollectorTest implements CategorizedTestCase { + private ConfigurationProblemsAlarmEvent event; + + public ConfigurationProblemsCollectorTest() { + EventManager.register(this); + } + + @Subscribe + public void onConfigurationProblemsAlarmEvent(ConfigurationProblemsAlarmEvent event) { + this.event = event; + } + + @Override + public void testRestTransport() throws Exception { + TestMgr.check(event != null, true); + TestMgr.check(event.getProblems(), "[WARN]Configurations warnings:\n" + + "Configurations with prefix `service_description` is deprecated, " + + "use `servicecomb.service` instead. Find keys [service_description.initialStatus]\n" + + "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"); + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvc.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvc.java new file mode 100644 index 00000000000..14bc6e78f73 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvc.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.demo.springmvc.server; + +import java.util.Arrays; + +import org.apache.servicecomb.demo.server.User; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.MediaType; + +@RestSchema(schemaId = "contentTypeSpringmvc") +@RequestMapping(value = "/contentTypeSpringmvc", consumes = MediaType.TEXT_PLAIN, produces = MediaType.TEXT_PLAIN) +public class ContentTypeSpringmvc { + @RequestMapping(path = "/testGlobalSetting", method = RequestMethod.POST) + public String testGlobalSetting(@RequestBody String name, HttpServletRequest request) { + return String.format("testGlobalSetting: name=[%s], request content-type=[%s]", name, request.getContentType()); + } + + @RequestMapping(path = "/testApiOperation", method = RequestMethod.POST) + public String testApiOperation(@RequestBody String name, HttpServletRequest request) { + return String.format("testApiOperation: name=[%s], request content-type=[%s]", name, request.getContentType()); + } + + @RequestMapping(path = "/testRequestMapping", method = RequestMethod.POST, + consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON) + public String testRequestMapping(@RequestBody String name, HttpServletRequest request) { + return String.format("testRequestMapping: name=[%s], request content-type=[%s]", name, request.getContentType()); + } + + @RequestMapping(path = "/testProtoBuffer", method = RequestMethod.POST, + consumes = SwaggerConst.PROTOBUF_TYPE, produces = SwaggerConst.PROTOBUF_TYPE) + public String testProtoBuffer(@RequestBody User user, HttpServletRequest request) { + return String.format("testRequestMapping: name=[%s], request content-type=[%s]", + user.getName() + ":" + user.getIndex() + ":" + + Arrays.toString(user.getNames()), request.getContentType()); + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvcOverwrite.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvcOverwrite.java new file mode 100644 index 00000000000..e690512bd2a --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ContentTypeSpringmvcOverwrite.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.server; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import jakarta.ws.rs.core.MediaType; + +@RestSchema(schemaId = "contentTypeSpringmvcOverwrite") +@RequestMapping(value = "/contentTypeSpringmvcOverwrite", produces = MediaType.TEXT_PLAIN) +public class ContentTypeSpringmvcOverwrite { + @RequestMapping(value = "/testResponseTypeOverwrite", method = RequestMethod.GET) + public String testResponseTypeOverwrite() { + return "testResponseTypeOverwrite: OK"; + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ControllerImpl.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ControllerImpl.java new file mode 100644 index 00000000000..3d49ba734fc --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ControllerImpl.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.Arrays; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.demo.controller.Person; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.constraints.Min; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response.Status; + +// This class tests "contract first", the controller.yaml will override annotations defined in class. + +@RestSchema(schemaId = "controller") +@RequestMapping(path = "/springmvc/controller", produces = MediaType.APPLICATION_JSON) +public class ControllerImpl { + @GetMapping(path = "/add") + public int add(@Min(1) @RequestParam("a") int a, @Min(1) @RequestParam("b") int b) { + return a + b; + } + + @PostMapping(path = "/sayhello/{name}") + public String sayHello(@PathVariable("name") String name) { + if ("exception".equals(name)) { + throw new InvocationException(Status.SERVICE_UNAVAILABLE, ""); + } + return "hello " + name; + } + + @RequestMapping(path = "/saysomething", method = RequestMethod.POST) + public String saySomething(String prefix, @RequestBody Person user) { + return prefix + " " + user.getName(); + } + + @Transport(name = CoreConst.RESTFUL) + @RequestMapping(path = "/sayhi", method = RequestMethod.GET) + public String sayHi(HttpServletRequest request) throws Exception { + String addr = request.getRemoteAddr(); + if (addr == null || addr.isEmpty()) { + throw new Exception("Can't get remote addr!"); + } + String[] values = request.getParameterValues("name"); + if (values != null && values.length > 0 && values[0].equals("throwexception")) { + throw new RuntimeException(); + } + return "hi " + request.getParameter("name") + " " + Arrays.toString(values); + } + + @RequestMapping(path = "/sayhei", method = RequestMethod.GET) + public String sayHei(@RequestHeader("name") String name) { + return "hei " + name; + } + + @RequestMapping(path = "/sayHello1", method = RequestMethod.GET) + public String sayHello1(@RequestParam("name") String name) { + return "Hello " + name + "," + ContextUtils.getInvocationContext().getContext("k"); + } + + @RequestMapping(path = "/testResponseModel", method = RequestMethod.GET) + public Person testResponseModel() { + Person person = new Person(); + person.setName("jack"); + return person; + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DataTypesAnnotationsSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DataTypesAnnotationsSchema.java new file mode 100644 index 00000000000..21077009ccf --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DataTypesAnnotationsSchema.java @@ -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. + */ +package org.apache.servicecomb.demo.springmvc.server; + +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 = "DataTypesAnnotationsSchema") +@RequestMapping(path = "/dataTypes") +public class DataTypesAnnotationsSchema { + @GetMapping(path = "/testIntArrayQuery") + public int[] testIntArrayQuery(@RequestParam("param") int[] param) { + return param; + } + + @GetMapping(path = "/testIntegerArrayQuery") + public Integer[] testIntegerArrayQuery(@RequestParam("param") Integer[] param) { + return param; + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DateTimeSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DateTimeSchema.java new file mode 100644 index 00000000000..4dfdba44ba0 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DateTimeSchema.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.server; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Date; + +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.core.MediaType; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +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; + +@RestSchema(schemaId = "DateTimeSchema") +@RequestMapping(path = "/dateTime", produces = MediaType.APPLICATION_JSON) +public class DateTimeSchema { + @GetMapping(path = "/getDate") + public Date getDate(@RequestParam("date") Date date) { + return date; + } + + @GetMapping(path = "/getDatePath/{date}") + public Date getDatePath(@PathParam("date") Date date) { + return date; + } + + @PostMapping(path = "/postDate") + public Date postDate(@RequestBody Date date) { + return date; + } + + @GetMapping(path = "/getLocalDate") + public LocalDate getLocalDate(@RequestParam("date") LocalDate date) { + return date; + } + + @GetMapping(path = "/getLocalDate/{date}") + public LocalDate getLocalDatePath(@PathParam("date") LocalDate date) { + return date; + } + + @PostMapping(path = "/postLocalDate") + public LocalDate postLocalDate(@RequestBody LocalDate date) { + return date; + } + + @GetMapping(path = "/getLocalDateTime") + public LocalDateTime getLocalDateTime(@RequestParam("date") LocalDateTime date) { + return date; + } + + @GetMapping(path = "/getLocalDateTime/{date}") + public LocalDateTime getLocalDateTimePath(@PathParam("date") LocalDateTime date) { + return date; + } + + @PostMapping(path = "/postLocalDateTime") + public LocalDateTime postLocalDateTime(@RequestBody LocalDateTime date) { + return date; + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DownloadSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DownloadSchema.java new file mode 100644 index 00000000000..d73824f1ffc --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DownloadSchema.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.demo.springmvc.server; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.common.part.FilePart; +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.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import jakarta.servlet.http.Part; + +@RestSchema(schemaId = "DownloadSchema") +@RequestMapping(path = "/download", produces = MediaType.APPLICATION_JSON_VALUE) +public class DownloadSchema { + private File tempDir = new File("target/downloadTemp"); + + private File lastFile; + + private File createTempFile(String content) throws IOException { + return createTempFile(null, content); + } + + private File createTempFile(String name, String content) throws IOException { + if (name == null) { + name = "download-" + UUID.randomUUID() + ".txt"; + } + File file = new File(tempDir, name); + FileUtils.write(file, content, StandardCharsets.UTF_8, false); + lastFile = file; + return file; + } + + @GetMapping(path = "/deleteAfterFinished") + 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() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") + .body(new FilePart(null, file) + .setDeleteAfterFinished(true)); + } + + @GetMapping(path = "/partIsNull") + public ResponseEntity partIsNull(@RequestParam("content") String content) throws IOException { + if (StringUtils.isEmpty(content)) { + return ResponseEntity + .ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") + .body(null); + } + File file = createTempFile(content); + return ResponseEntity + .ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") + .body(new FilePart(null, file)); + } + + @GetMapping(path = "/notDeleteAfterFinished") + public ResponseEntity notDeleteAfterFinished(@RequestParam("content") String content) throws IOException { + File file = createTempFile(content); + + return ResponseEntity + .ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") + .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/MyStrategy.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/MyStrategy.java new file mode 100644 index 00000000000..1f6544863fb --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/MyStrategy.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.server; + +import org.apache.servicecomb.qps.strategy.AbstractQpsStrategy; + +public class MyStrategy extends AbstractQpsStrategy { + + @Override + public boolean isLimitNewRequest() { + return false; + } + + @Override + public String name() { + return "MyStrategy"; + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/MyStrategyFactory.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/MyStrategyFactory.java new file mode 100644 index 00000000000..002aa95a6d8 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/MyStrategyFactory.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.demo.springmvc.server; + +import org.apache.servicecomb.qps.strategy.AbstractQpsStrategy; +import org.apache.servicecomb.qps.strategy.IStrategyFactory; + +public class MyStrategyFactory implements IStrategyFactory { + + @Override + public AbstractQpsStrategy createStrategy(String strategyName) { + if (strategyName.equals("MyStrategy")) { + return new MyStrategy(); + } + return null; + } +} 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 new file mode 100644 index 00000000000..ddf56fb5a3f --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.BootListener; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.definition.SchemaMeta; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectWriter; + +import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.oas.models.OpenAPI; + +/** + * Testing after bootup. + */ +@Component +public class ProducerTestsAfterBootup implements BootListener { + private static final Logger LOGGER = LoggerFactory.getLogger(ProducerTestsAfterBootup.class); + + private ObjectWriter writer = Yaml.pretty(); + + public void testSchemaNotChange(SCBEngine scbEngine) { + LOGGER.info("ProducerTestsAfterBootup testing start"); + SchemaMeta meta = scbEngine.getProducerMicroserviceMeta().findSchemaMeta("CodeFirstSpringmvcForSchema"); + String codeFirst = getSwaggerContent(meta.getSwagger()); + + 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(OpenAPI swagger) { + try { + return writer.writeValueAsString(swagger); + } catch (JsonProcessingException e) { + throw new Error(e); + } + } + + @Override + public void onBootEvent(BootEvent event) { + if (event.getEventType() == BootListener.EventType.AFTER_REGISTRY) { + testSchemaNotChange(event.getScbEngine()); + if (!TestMgr.isSuccess()) { + TestMgr.summary(); + throw new IllegalStateException("some tests are failed. "); + } + } + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ReadFileSource.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ReadFileSource.java new file mode 100644 index 00000000000..77d78324666 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ReadFileSource.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.springmvc.server; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "testFileSource") +@RequestMapping(path = "/springmvc/fileSource") +public class ReadFileSource { + @Value("${int:-1}") + public int testInt; + + @GetMapping(path = "/int") + public int getTestInt() { + return testInt; + } + + @Value("${String:error}") + public String testString; + + @GetMapping(path = "/String") + public String getTestString() { + return testString; + } +} 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/RetrySchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RetrySchema.java new file mode 100644 index 00000000000..b3a32b34020 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RetrySchema.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.server; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; + +import jakarta.ws.rs.core.Response.Status; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +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; + +// test cases for retry +@RestSchema(schemaId = "RetrySchema") +@RequestMapping(path = "/retry", produces = MediaType.APPLICATION_JSON_VALUE) +public class RetrySchema { + private AtomicLong counter = new AtomicLong(0); + + @GetMapping(path = "/governance/successWhenRetry") + public boolean successWhenRetry() { + if (counter.getAndIncrement() % 3 != 0) { + throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "try again later."); + } + return true; + } + + @GetMapping(path = "/governance/successWhenRetryAsync") + public CompletableFuture successWhenRetryAsync() { + 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; + } +} 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 new file mode 100644 index 00000000000..8d4b02e85b5 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SchemeInterfaceSpringmvc.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.demo.springmvc.server; + +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 new file mode 100644 index 00000000000..828a9fc160d --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SchemeInterfaceSpringmvcImpl.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.server; + +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 + 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 new file mode 100644 index 00000000000..1bd1abba2e3 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SpringMVCCommonSchemaInterface.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.demo.springmvc.server; + +import jakarta.ws.rs.core.Response.Status; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.demo.CommonSchemaInterface; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +@RestSchema(schemaId = "SpringMVCCommonSchemaInterface", schemaInterface = CommonSchemaInterface.class) +public class SpringMVCCommonSchemaInterface implements CommonSchemaInterface { + @Override + public String testInvocationTimeout(long timeout, String name) { + try { + Thread.sleep(timeout); + } catch (InterruptedException e) { + + } + + return name; + } + + @Override + public String testInvocationTimeout(InvocationContext context, long timeout, + String name) { + + if ("customized".equals(name)) { + try { + Thread.sleep(timeout); + } catch (InterruptedException e) { + + } + + Invocation invocation = (Invocation) context; + invocation.ensureInvocationNotTimeout(); + + throw new InvocationException(Status.BAD_REQUEST, "not expected result"); + } + + return testInvocationTimeout(timeout, name); + } + + @Override + public String testInvocationTimeoutInClientWait(long timeout, String name) { + return testInvocationTimeout(timeout, name); + } +} 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 new file mode 100644 index 00000000000..a30b9ad84d3 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/SpringMvcDefaultValues.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.springmvc.server; + +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; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RestSchema(schemaId = "SpringMvcDefaultValues") +@RequestMapping(path = "/springmvc/SpringMvcDefaultValues", produces = MediaType.APPLICATION_JSON) +public class SpringMvcDefaultValues { + + @PostMapping(path = "/form", consumes = MediaType.APPLICATION_FORM_URLENCODED) + public String form(@RequestParam(name = "a", defaultValue = "20") int a, + @RequestParam(name = "b", defaultValue = "bobo") String b) { + return "Hello " + a + b; + } + + @PostMapping(path = "/header") + public String header(@RequestHeader(name = "a", defaultValue = "20") int a, + @RequestHeader(name = "b", defaultValue = "bobo") String b, + @RequestHeader(name = "c", defaultValue = "30") Integer c) { + return "Hello " + a + b + c; + } + + @GetMapping("/query") + public String query(@RequestParam(name = "a", defaultValue = "20") int a, + @RequestParam(name = "b", defaultValue = "bobo") String b, + @RequestParam(name = "c", defaultValue = "40") Integer c, @RequestParam(name = "d") int d) { + return "Hello " + a + b + c + d; + } + + @GetMapping("/query2") + public String query2(@RequestParam(name = "e", required = false) int e, + @RequestParam(name = "a", defaultValue = "20") int a, + @RequestParam(name = "b", defaultValue = "bobo") String b, + @RequestParam(name = "c", defaultValue = "40") Integer c, + @Min(value = 20) @Max(value = 30) @RequestParam(name = "d", required = false) int d) { + return "Hello " + a + b + c + d + e; + } + + @GetMapping("/query3") + public String query3(@RequestParam("a") @Min(value = 20) int a, @RequestParam(name = "b", required = false) String b) { + return "Hello " + a + b; + } + + @PostMapping("/javaprimitiveint") + public String springJavaPrimitiveInt(@RequestParam(name = "a", required = false) int a, + @RequestParam(name = "b", defaultValue = "bobo") String b) { + return "Hello " + a + b; + } + + @PostMapping("/javaprimitivenumber") + public String springJavaPrimitiveNumber(@RequestParam(name = "a", required = false) float a, + @RequestParam(name = "b", required = false) boolean b) { + return "Hello " + a + b; + } + + @PostMapping("/javaprimitivestr") + public String springJavaPrimitiveStr(@RequestParam(name = "a", required = false) int a, + @RequestParam(name = "b", required = false) String b) { + if (b == null || b.equals("")) { + return "Hello"; + } + return "Hello " + b + a; + } + + @PostMapping("/javaprimitivecomb") + public String springJavaPrimitiveCombination(@RequestParam(name = "a", required = false) Integer a, + @RequestParam(name = "b", required = false) Float b) { + return "Hello " + a + b; + } + + @PostMapping("/allprimitivetypes") + public String allprimitivetypes(@RequestParam(name = "pBoolean", required = false) boolean pBoolean, + @RequestParam(name = "pChar", required = false) char pChar, + @RequestParam(name = "pByte", required = false) byte pByte, + @RequestParam(name = "pShort", required = false) short pShort, + @RequestParam(name = "pInt", required = false) int pInt, + @RequestParam(name = "pLong", required = false) long pLong, + @RequestParam(name = "pFloat", required = false) float pFloat, + @RequestParam(name = "pDouble", required = false) double pDouble, + @RequestParam(name = "pDoubleWrap", required = false) Double pDoubleWrap) { + return "Hello " + pBoolean + "," + + pChar + "," + + pByte + "," + + pShort + "," + + pInt + "," + + pLong + "," + + pFloat + "," + + pDouble + "," + + pDoubleWrap; + } +} 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 new file mode 100644 index 00000000000..1a3a737d592 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/UploadSchema.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.apache.commons.io.IOUtils; +import org.apache.servicecomb.demo.TestMgr; +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.RequestAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +@RestSchema(schemaId = "UploadSchema") +@RequestMapping(path = "/upload", produces = MediaType.APPLICATION_JSON_VALUE) +public class UploadSchema { + @PostMapping(path = "/fileUpload", produces = MediaType.TEXT_PLAIN_VALUE, + consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public String fileUpload(@RequestPart(name = "files") List files) { + try { + String fileName = UUID.randomUUID().toString(); + List savedFiles = new ArrayList<>(); + int index = 0; + for (MultipartFile file : files) { + File tempFile = new File("random-server-" + fileName + index); + savedFiles.add(tempFile); + file.transferTo(tempFile); + index++; + } + savedFiles.forEach(File::delete); + return "success"; + } catch (IOException e) { + return "failed"; + } + } + + @GetMapping(path = "/isServerStartUpSuccess") + public boolean isServerStartUpSuccess() { + return TestMgr.isSuccess(); + } + + @PostMapping(path = "/fileUploadMultiRpc", produces = MediaType.TEXT_PLAIN_VALUE, + consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public String fileUploadMultiRpc(@RequestPart(name = "files") MultipartFile[] files) { + return "fileUploadMulti success, and fileNum is " + files.length; + } + + @PostMapping(path = "/uploadFileRequestPartAttribute", produces = MediaType.TEXT_PLAIN_VALUE) + public String uploadFileRequestPartAttribute(@RequestPart(name = "file") MultipartFile file, + @RequestPart(name = "attribute") String attribute) throws IOException { + try (InputStream is = file.getInputStream()) { + return attribute + " " + IOUtils.toString(is, StandardCharsets.UTF_8); + } + } + + @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 { + try (InputStream is = file.getInputStream()) { + return attribute + " " + IOUtils.toString(is, StandardCharsets.UTF_8); + } + } +} 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 new file mode 100644 index 00000000000..b9d6a119817 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/WeakSpringmvc.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.server; + +import java.util.List; + +import org.apache.servicecomb.demo.model.SpecialNameModel; +import org.apache.servicecomb.demo.server.GenericsModel; +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.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import io.swagger.v3.oas.annotations.Operation; + +@RestSchema(schemaId = "weakSpringmvc") +@RequestMapping(path = "/weakSpringmvc", produces = MediaType.APPLICATION_JSON_VALUE) +public class WeakSpringmvc { + @GetMapping(path = "/diffNames") + @Operation(summary = "differentName", operationId = "differentName") + public int diffNames(@RequestParam("x") int a, @RequestParam("y") int b) { + return a * 2 + b; + } + + @GetMapping(path = "/genericParams") + @Operation(summary = "genericParams", operationId = "genericParams") + public List> genericParams(@RequestParam("code") int code, @RequestBody List> names) { + return names; + } + + @GetMapping(path = "/genericParamsModel") + @Operation(summary = "genericParamsModel", operationId = "genericParamsModel") + public GenericsModel genericParamsModel(@RequestParam("code") int code, @RequestBody GenericsModel model) { + return model; + } + + @GetMapping(path = "/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 new file mode 100644 index 00000000000..79b89668560 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/EarlyConsumer.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.springmvc.third; + +import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * see: https://github.com/apache/servicecomb-java-chassis/issues/2534 + */ +@Component +public class EarlyConsumer implements BootListener { + private static final Logger LOGGER = LoggerFactory.getLogger(EarlyConsumer.class); + + @RpcReference(microserviceName = "third", schemaId = "heartbeat") + private HealthSchema healthSchema; + + private volatile boolean stopped = false; + + public EarlyConsumer() { + new Thread(() -> { + while (!stopped) { + LOGGER.info("calling service"); + try { + healthSchema.heartbeat(); + } catch (Throwable e) { + // ignore error + } + try { + Thread.sleep(500); + } catch (Throwable e) { + // ignore error + } + } + }).start(); + } + + @Override + public void onAfterRegistry(BootEvent event) { + stopped = true; + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/HealthSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/HealthSchema.java new file mode 100644 index 00000000000..eab4a09e89b --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/HealthSchema.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.springmvc.third; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping("/v1") +public interface HealthSchema { + @GetMapping(value = "/heartbeat") + void heartbeat(); +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/HeartBeatService.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/HeartBeatService.java new file mode 100644 index 00000000000..d01b2103343 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/HeartBeatService.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.third; + +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "health", schemaInterface = HealthSchema.class) +public class HeartBeatService implements HealthSchema { + @Override + public void heartbeat() { + + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/NormalConsumer.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/NormalConsumer.java new file mode 100644 index 00000000000..b9bd2daa7c6 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/NormalConsumer.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.springmvc.third; + +import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * see: https://github.com/apache/servicecomb-java-chassis/issues/2534 + */ +@Component +public class NormalConsumer implements BootListener { + private static final Logger LOGGER = LoggerFactory.getLogger(NormalConsumer.class); + + @RpcReference(microserviceName = "third", schemaId = "heartbeat") + private HealthSchema healthSchema; + + @Override + public void onAfterRegistry(BootListener.BootEvent event) { + try { + LOGGER.info("calling service after register"); + + healthSchema.heartbeat(); + LOGGER.info("heartbeat succ"); + } catch (Throwable e) { + TestMgr.failed("3rd invoke failed", e); + throw e; + } + } + + + @Override + public int getOrder() { + // 比ThirdServiceRegister晚 + return 0; + } +} 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 new file mode 100644 index 00000000000..cc9144fe617 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/third/Register.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.demo.springmvc.third; + +import java.util.List; + +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 + */ +@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 { + 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/META-INF/services/org.apache.servicecomb.qps.strategy.IStrategyFactory b/demo/demo-springmvc/springmvc-server/src/main/resources/META-INF/services/org.apache.servicecomb.qps.strategy.IStrategyFactory new file mode 100644 index 00000000000..f1941bac9de --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/resources/META-INF/services/org.apache.servicecomb.qps.strategy.IStrategyFactory @@ -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.MyStrategyFactory diff --git a/demo/demo-springmvc/springmvc-server/src/main/resources/log4j2.xml b/demo/demo-springmvc/springmvc-server/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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 f7740309c91..c5c10c5b41b 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/resources/microservice.yaml +++ b/demo/demo-springmvc/springmvc-server/src/main/resources/microservice.yaml @@ -1,26 +1,70 @@ -APPLICATION_ID: springmvctest +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +# test not supported configuration +# service_description.initialStatus service_description: - name: springmvc - version: 0.0.2 - paths: - - path: /test1/testpath - property: - checksession: false - - path: /test2/testpath - property: - checksession: true -cse: + initialStatus: TESTING + +servicecomb: service: - registry: - address: http://127.0.0.1:9980 + application: springmvctest + name: springmvc + version: 0.0.3 + + registry: + sc: + address: http://127.0.0.1:9980,http://127.0.0.1:30100 + autodiscovery: true + healthCheckIntervalInSeconds: 3 + pollIntervalInMillis: 10000 + watch: true + + uploads: + directory: target + http: + dispatcher: + rest: + pattern: "/api/(.*)" rest: - address: 0.0.0.0:8080?sslEnabled=true + address: 0.0.0.0:8080?sslEnabled=false + server: + compression: true highway: address: 0.0.0.0:7070?sslEnabled=true - handler: - chain: - Provider: - default: bizkeeper-provider + invocation: + timeout: + check: + enabled: true + strategy: processing-time + SpringMVCCommonSchemaInterface: + testInvocationTimeout: + timeout: 1000 + testInvocationTimeoutWithInvocation: + timeout: 1000 + + flowcontrol: + strategy: MyStrategy + Provider: + qps: + enabled: true + global: + limit: 10 + bucket: 30 tracing: enabled: true samplingRate: 0.5 @@ -28,7 +72,16 @@ cse: name: myDC 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 +# ssl.ciphers: TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384 ssl.protocols: TLSv1.2 ssl.authPeer: true ssl.checkCN.host: true @@ -41,4 +94,6 @@ ssl.keyStore: server.p12 ssl.keyStoreType: PKCS12 ssl.keyStoreValue: Changeme_123 ssl.crl: revoke.crl -ssl.sslCustomClass: io.servicecomb.demo.DemoSSLCustom \ No newline at end of file +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-registry/demo-zeroconfig-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 new file mode 100644 index 00000000000..eaea52f6aa8 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientApplication.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.zeroconfig.client; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ClientApplication { + + public static void main(final String[] args) throws Exception { + new SpringApplicationBuilder().sources(ClientApplication.class).web(WebApplicationType.SERVLET) + .build().run(args); + } +} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-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 new file mode 100644 index 00000000000..ed8458a575c --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/ClientModel.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.zeroconfig.client; + +import java.util.Date; + +public class ClientModel { + private Date updateDate; + + public Date getUpdateDate() { + return updateDate; + } + + public void setUpdateDate(Date updateDate) { + this.updateDate = updateDate; + } +} 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-registry/demo-zeroconfig-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 new file mode 100644 index 00000000000..edbfc7f28b6 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/IRpcEndpoint.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.demo.zeroconfig.client; + +import io.vertx.core.json.JsonObject; + +public interface IRpcEndpoint { + JsonObject getJsonObject(JsonObject message); + + String getString(String message); +} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-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 new file mode 100644 index 00000000000..4687b6936d1 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/java/org/apache/servicecomb/demo/zeroconfig/client/IServerEndpoint.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.zeroconfig.client; + +public interface IServerEndpoint { + String getName(String name); +} 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-registry/demo-zeroconfig-registry-client/src/main/resources/log4j2.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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-registry/demo-zeroconfig-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 new file mode 100644 index 00000000000..6ba5795c2a8 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/EdgeApplication.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.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 EdgeApplication { + public static void main(final String[] args) throws Exception { + new SpringApplicationBuilder().sources(EdgeApplication.class).web(WebApplicationType.NONE).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-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-registry/demo-zeroconfig-registry-edge/src/main/resources/log4j2.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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-registry/demo-zeroconfig-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 new file mode 100644 index 00000000000..0c5121cac33 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/GovernanceNoPrefixEndpoint.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.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; + +@RestSchema(schemaId = "GovernanceNoPrefixEndpoint") +@RequestMapping("/") +public class GovernanceNoPrefixEndpoint { + private static final Logger LOGGER = LoggerFactory.getLogger(GovernanceNoPrefixEndpoint.class); + + private Map retryTimes = new HashMap<>(); + + @GetMapping("/noPrefixRetry") + @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 noPrefixRetry(@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"); + } +} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-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 new file mode 100644 index 00000000000..37dba6191ab --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/RpcEndpoint.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.zeroconfig.server; + +import org.apache.servicecomb.provider.pojo.RpcSchema; + +import io.vertx.core.json.JsonObject; + +@RpcSchema(schemaId = "RpcEndpoint") +public class RpcEndpoint { + public JsonObject getJsonObject(JsonObject message) { + JsonObject inner = new JsonObject(); + inner.put("map", message); + return inner; + } + + public String getString(String message) { + return message; + } +} 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-registry/demo-zeroconfig-registry-server/src/main/resources/log4j2.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..ea906df4019 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 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-registry/demo-zeroconfig-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 new file mode 100644 index 00000000000..b664eef319d --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ClientModel.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.zeroconfig.tests; + +import java.util.Date; + +public class ClientModel { + private Date updateDate; + + public Date getUpdateDate() { + return updateDate; + } + + public void setUpdateDate(Date updateDate) { + this.updateDate = updateDate; + } +} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-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 new file mode 100644 index 00000000000..24899c822c6 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/GovernanceTest.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.UUID; +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.springmvc.reference.RestTemplateBuilder; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; + +@Component +public class GovernanceTest implements CategorizedTestCase { + String url = "servicecomb://demo-zeroconfig-registry-client/governance"; + + RestOperations template = RestTemplateBuilder.create(); + + @Override + public void testRestTransport() throws Exception { + testCircuitBreaker(); + testBulkhead(); + testRateLimitingRest(); + testRateLimitingRpc(); + testRetryRest(); + testRetryRestNoPrefix(); + testRetryRpc(); + } + + private void testRetryRestNoPrefix() { + testRetry("/noPrefixRetry"); + } + + private void testRetryRest() { + testRetry("/retry"); + } + + private void testRetryRpc() { + testRetry("/retryRpc"); + } + + private void testRetry(String operation) { + String invocationID = UUID.randomUUID().toString(); + String result = template.getForObject(url + operation + "?invocationID={1}", String.class, invocationID); + TestMgr.check(result, "try times: 3"); + } + + private void testCircuitBreaker() 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 { + String result = template.getForObject(url + "/circuitBreaker", String.class); + if (!"ok".equals(result)) { + notExpectedFailed.set(true); + } + } catch (Exception e) { + if ("InvocationException: code=429;msg=CommonExceptionData [message=circuitBreaker is open.]" + .equals(e.getMessage())) { + expectedFailed.set(true); + } else if ("InvocationException: code=503;msg=CommonExceptionData [message=test error]" + .equals(e.getMessage())) { + // by pass + } else { + notExpectedFailed.set(true); + } + } + latch.countDown(); + } + }.start(); + } + Thread.sleep(100); + } + + latch.await(20, TimeUnit.SECONDS); + TestMgr.check(true, expectedFailed.get()); + TestMgr.check(false, notExpectedFailed.get()); + } + + private void testBulkhead() 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 { + String result = template.getForObject(url + "/bulkhead", String.class); + if (!"Hello world!".equals(result)) { + notExpectedFailed.set(true); + } + } catch (Exception e) { + if (!"InvocationException: code=429;msg=CommonExceptionData [message=bulkhead is full and does not permit further calls.]" + .equals(e.getMessage())) { + notExpectedFailed.set(true); + } + expectedFailed.set(true); + } + latch.countDown(); + } + }.start(); + } + Thread.sleep(100); + } + + latch.await(20, TimeUnit.SECONDS); + TestMgr.check(true, expectedFailed.get()); + TestMgr.check(false, notExpectedFailed.get()); + } + + private void testRateLimitingRest() throws Exception { + testRateLimiting("/hello"); + } + + private void testRateLimitingRpc() throws Exception { + testRateLimiting("/helloRpc"); + } + + private void testRateLimiting(String operation) 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 { + String result = template.getForObject(url + operation, String.class); + if (!"Hello world!".equals(result)) { + notExpectedFailed.set(true); + } + } catch (Exception e) { + if (!"InvocationException: code=429;msg=CommonExceptionData [message=rate limited.]" + .equals(e.getMessage())) { + notExpectedFailed.set(true); + } + expectedFailed.set(true); + } + latch.countDown(); + } + }.start(); + } + Thread.sleep(100); + } + + latch.await(20, TimeUnit.SECONDS); + TestMgr.check(true, expectedFailed.get()); + TestMgr.check(false, notExpectedFailed.get()); + } +} diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-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 new file mode 100644 index 00000000000..767f43ff74b --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/java/org/apache/servicecomb/demo/zeroconfig/tests/ServerTest.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.net.URI; +import java.net.URISyntaxException; +import java.util.Date; +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.RestOperations; + +import io.vertx.core.json.JsonObject; + +@Component +public class ServerTest implements CategorizedTestCase { + + RestOperations template = RestTemplateBuilder.create(); + + @Override + public void testRestTransport() throws Exception { + testServerGetName(); + testGetAllMicroservice(); + testJsonObject(); + testString(); + testDateForEdge(); + testContextMapper(); + } + + private void testContextMapper() throws URISyntaxException { + MultiValueMap headers = new HttpHeaders(); + headers.add("clientHeader", "v1"); + headers.add("gatewayHeader", "v2"); + RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, + 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); + TestMgr.check(response.getBody(), "success"); + response = template.exchange(requestEntity, String.class); + TestMgr.check(response.getBody(), "success"); + } + + @SuppressWarnings("unchecked") + private void testDateForEdge() { + for (int i = 0; i < 3; i++) { + ClientModel clientModelReq = new ClientModel(); + Date date = new Date(1663590135202L); + clientModelReq.setUpdateDate(date); + Map response = template + .postForObject( + "cse://demo-zeroconfig-registry-edge" + + "/register/url/prefix/postModel", clientModelReq, + Map.class); + Object result = response.get("updateDate"); + // TODO: highway and rest Date field will give different result + // we can not change this now, because it is incompatible + if (result instanceof Long) { + TestMgr.check(response.get("updateDate"), 1663590135202L); + } else { + TestMgr.check(response.get("updateDate"), "2022-09-19T12:22:15.202+00:00"); + } + } + } + + private void testServerGetName() throws Exception { + // invoke demo-zeroconfig-registry-client + TestMgr.check("world", template + .getForObject( + "cse://demo-zeroconfig-registry-client/register/url/prefix/getName?name=world", + String.class)); + // invoke demo-zeroconfig-registry-edge + // create many threads to test event-loop not blocking + int thread = 32; + CountDownLatch latch = new CountDownLatch(thread); + for (int i = 0; i < thread; i++) { + new Thread(() -> { + for (int j = 0; j < 20; j++) { + try { + TestMgr.check("world", template + .getForObject( + "cse://demo-zeroconfig-registry-edge/register/url/prefix/getName?name=world", + String.class)); + } catch (Exception e) { + TestMgr.failed("test failed", e); + } + } + latch.countDown(); + }).start(); + } + + latch.await(); + } + + @SuppressWarnings("rawTypes") + private void testGetAllMicroservice() { + // invoke demo-zeroconfig-registry-client + TestMgr.check("2", template + .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 + .exchange( + "cse://demo-zeroconfig-registry-edge" + + "/register/url/prefix/getRegisteredMicroservice", HttpMethod.GET, null, + new ParameterizedTypeReference>() { + + }).getBody().size()); + } + + private void testJsonObject() { + JsonObject in = new JsonObject(); + JsonObject inner = new JsonObject(); + //调用者需要按照swagger传参 + inner.put("hello", "world"); + in.put("map", inner); + + JsonObject result = template + .postForObject( + "cse://demo-zeroconfig-registry-client" + + "/register/url/prefix/jsonObject", in, JsonObject.class); + TestMgr.check(inner.toString(), result.toString()); + TestMgr.check(result.getString("hello"), "world"); + } + + private void testString() { + String in = "{\"hello\":\"world\"}"; + String result = template + .postForObject( + "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-registry/demo-zeroconfig-registry-tests/src/main/resources/logback.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/logback.xml new file mode 100644 index 00000000000..cc214e46e64 --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/logback.xml @@ -0,0 +1,29 @@ + + + + + + + %d [%level] [%thread] - %msg (%F:%L\)%n + + + + + + + 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-registry/demo-zeroconfig-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 new file mode 100644 index 00000000000..cd531243e6e --- /dev/null +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/test/java/org/apache/servicecomb/demo/zeroconfig/tests/ZeroConfigRegistryIT.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.demo.zeroconfig.tests; + +import org.apache.servicecomb.demo.TestMgr; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ZeroConfigRegistryIT { + @BeforeEach + public void setUp() throws Exception { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + Application.main(new String[0]); + + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} 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-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 5f7ee22ee24..00000000000 --- a/demo/docker-build-config/pom.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - demo-parent - io.servicecomb.demo - 0.3.0-SNAPSHOT - - 4.0.0 - - docker-build-config - pom - - - - - - io.fabric8 - docker-maven-plugin - - - - ${project.artifactId}:${project.version} - ${project.artifactId} - - openjdk:8-jre-alpine - - 7070 - 8080 - - - tar - ${root.basedir}/demo/assembly/assembly.xml - - - java $JAVA_OPTS -jar $JAR_PATH - - - - - - - - build - package - - build - - - - - - org.commonjava.maven.plugins - directory-maven-plugin - - - directories - - highest-basedir - - initialize - - root.basedir - - - - - - - - diff --git a/demo/docker-run-config/pom.xml b/demo/docker-run-config/pom.xml deleted file mode 100644 index e6512a98b5c..00000000000 --- a/demo/docker-run-config/pom.xml +++ /dev/null @@ -1,171 +0,0 @@ - - - - demo-parent - io.servicecomb.demo - 0.3.0-SNAPSHOT - - 4.0.0 - - docker-run-config - pom - - - - - - io.fabric8 - docker-maven-plugin - - - - servicecomb/service-center - service-center - - - listen on server - - - - service.center.port:30100 - - - - - ${demo.service.name}:${project.version} - ${demo.service.name} - - - - ${demo.vm.options} -Dcse.service.registry.address=http://sc.servicecomb.io:30100 -Dcse.service.publishAddress=${docker.hostname} - - /maven/${demo.service.name}-${project.version}.jar - - - service-center:sc.servicecomb.io - - - Register microservice instance success - - - http://${docker.hostname}:8080 - GET - 404 - - - - - 7070:7070 - 8080:8080 - - - service-center - - - - - - - - start - pre-integration-test - - start - - - - stop - post-integration-test - - stop - - - - - - - - - - - docker - - - - org.codehaus.gmaven - gmaven-plugin - - - add-default-properties - initialize - - execute - - - - project.properties.setProperty('docker.hostname', 'localhost') - log.info("Docker hostname is " + project.properties['docker.hostname']) - - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - 2.19.1 - - - http://${docker.hostname}:${service.center.port} - - ${jacoco.failsafe.argLine} - - - - - integration-test - verify - - - - - - - - - docker-machine - - - - org.codehaus.gmaven - gmaven-plugin - - - add-dynamic-properties - prepare-package - - execute - - - - def process = "docker-machine ip default".execute() - process.waitFor() - project.properties.setProperty('docker.hostname', process.in.text.trim()) - - log.info("Docker hostname is " + project.properties['docker.hostname']) - - - - - - - - - - diff --git a/demo/pom.xml b/demo/pom.xml index d08e7b11c64..d0376883c6b 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb - java-chassis-parent - 0.3.0-SNAPSHOT - ../parent - - io.servicecomb.demo - demo-parent - pom - - docker-build-config - docker-run-config - demo-local - demo-server-servlet - demo-pojo - demo-jaxrs - demo-perf-client - demo-springmvc - demo-schema - demo-crossapp - demo-spring-boot-provider - demo-spring-boot-transport - demo-spring-boot-discovery - demo-multiple - + 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-parent + 3.4.0-SNAPSHOT + ../parents/default + + org.apache.servicecomb.demo + demo-parent + Java Chassis::Demo + + + + + + pom + + demo-schema + demo-pojo + demo-filter + demo-jaxrs + demo-springmvc + demo-crossapp + demo-register-url-prefix + demo-local-registry + demo-multi-registries + demo-zeroconfig-registry + demo-spring-boot-transport + demo-edge + demo-multiple + demo-multi-service-center + demo-cse-v1 + demo-cse-v2 + demo-nacos + + + demo-zookeeper + demo-consul + + + + + + org.apache.servicecomb.demo + demo-schema + ${project.version} + + + org.apache.servicecomb.demo + all-client + ${project.version} + + + org.apache.servicecomb.demo + pojo-server + ${project.version} + + + org.apache.servicecomb.demo + jaxrs-server + ${project.version} + + + org.apache.servicecomb.demo + springmvc-server + ${project.version} + + + org.apache.servicecomb.demo + crossapp-server + ${project.version} + + + org.apache.servicecomb.demo + multiple-server + ${project.version} + + + org.apache.servicecomb.demo + multiple-client + ${project.version} + + + org.apache.servicecomb.demo + pojo-client + ${project.version} + + + org.apache.servicecomb.demo + jaxrs-client + ${project.version} + + + org.apache.servicecomb.demo + springmvc-client + ${project.version} + + + org.apache.servicecomb.demo + crossapp-client + ${project.version} + + + org.apache.servicecomb + foundation-test-scaffolding + ${project.version} + + + + + + + org.junit.jupiter + junit-jupiter + compile + + - - - - io.servicecomb.demo - demo-schema - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - all-client - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - pojo-server - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - jaxrs-server - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - springmvc-server - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - crossapp-server - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - multiple-server - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - multiple-client - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - pojo-client - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - jaxrs-client - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - springmvc-client - 0.3.0-SNAPSHOT - - - io.servicecomb.demo - crossapp-client - 0.3.0-SNAPSHOT - - - + + + + + org.commonjava.maven.plugins + directory-maven-plugin + 1.0 + + + maven-deploy-plugin + + true + + + + maven-install-plugin + + false + + + + org.apache.maven.plugins + maven-jar-plugin + + + assembly + package + + + + + + true + lib/ + ${demo.main} + + + + + + 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 + + + - - - org.slf4j - slf4j-log4j12 - - - - - - - - com.github.odavid.maven.plugins - mixin-maven-plugin - 0.1-alpha-39 - true - - - org.commonjava.maven.plugins - directory-maven-plugin - 0.1 - - - maven-deploy-plugin - 2.8.2 - - true - - - - maven-install-plugin - 2.5.2 - - false - - - - org.apache.maven.plugins - maven-jar-plugin - 2.6 - - - assembly - package - - - - - - true - lib/ - ${demo.main} - - - - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - true - - true - - - - - - - - demo-run-release - - - - org.commonjava.maven.plugins - directory-maven-plugin - - - directoryies - - directory-of - - initialize - - root.dir - - io.servicecomb - java-chassis - - - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - ${demo.run.dir}/${project.name} - - - true - ../lib/ - ${demo.main} - - - . .. - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - copy-dependencies - package - - copy-dependencies - - - ${demo.run.dir}/lib - - - - - - - - - ${root.dir}/demo/target - - - + + + 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 new file mode 100644 index 00000000000..ed5e1bc91a2 --- /dev/null +++ b/dependencies/bom/pom.xml @@ -0,0 +1,414 @@ + + + + + 4.0.0 + + org.apache.servicecomb + java-chassis-dependencies-parent + 3.4.0-SNAPSHOT + + + java-chassis-bom + Java Chassis::Bom + pom + + + + + + + + org.apache.servicecomb + config-center-client + ${project.version} + + + org.apache.servicecomb + config-clients-common + ${project.version} + + + org.apache.servicecomb + config-kie-client + ${project.version} + + + org.apache.servicecomb + dashboard-client + ${project.version} + + + org.apache.servicecomb + http-client-common + ${project.version} + + + org.apache.servicecomb + service-center-client + ${project.version} + + + + + org.apache.servicecomb + common-access-log + ${project.version} + + + org.apache.servicecomb + common-protobuf + ${project.version} + + + org.apache.servicecomb + common-rest + ${project.version} + + + + + org.apache.servicecomb + java-chassis-core + ${project.version} + + + + + org.apache.servicecomb + config-apollo + ${project.version} + + + org.apache.servicecomb + config-cc + ${project.version} + + + org.apache.servicecomb + config-kie + ${project.version} + + + org.apache.servicecomb + config-nacos + ${project.version} + + + org.apache.servicecomb + config-zookeeper + ${project.version} + + + org.apache.servicecomb + config-consul + ${project.version} + + + + + org.apache.servicecomb + edge-core + ${project.version} + + + + org.apache.servicecomb + foundation-common + ${project.version} + + + org.apache.servicecomb + foundation-config + ${project.version} + + + org.apache.servicecomb + foundation-metrics + ${project.version} + + + org.apache.servicecomb + foundation-protobuf + ${project.version} + + + org.apache.servicecomb + foundation-registry + ${project.version} + + + org.apache.servicecomb + foundation-spi + ${project.version} + + + org.apache.servicecomb + foundation-ssl + ${project.version} + + + org.apache.servicecomb + foundation-test-scaffolding + ${project.version} + test + + + org.apache.servicecomb + foundation-vertx + ${project.version} + + + + + org.apache.servicecomb + servicecomb-governance + ${project.version} + + + + + org.apache.servicecomb + handler-fault-injection + ${project.version} + + + org.apache.servicecomb + handler-flowcontrol-qps + ${project.version} + + + org.apache.servicecomb + handler-governance + ${project.version} + + + org.apache.servicecomb + handler-loadbalance + ${project.version} + + + org.apache.servicecomb + handler-publickey-auth + ${project.version} + + + org.apache.servicecomb + handler-router + ${project.version} + + + org.apache.servicecomb + handler-tracing-zipkin + ${project.version} + + + + + org.apache.servicecomb + metrics-core + ${project.version} + + + org.apache.servicecomb + metrics-prometheus + ${project.version} + + + + org.apache.servicecomb + provider-jaxrs + ${project.version} + + + org.apache.servicecomb + provider-pojo + ${project.version} + + + org.apache.servicecomb + provider-rest-common + ${project.version} + + + org.apache.servicecomb + provider-springmvc + ${project.version} + + + + org.apache.servicecomb + registry-lightweight + ${project.version} + + + org.apache.servicecomb + registry-local + ${project.version} + + + org.apache.servicecomb + registry-service-center + ${project.version} + + + org.apache.servicecomb + registry-zero-config + ${project.version} + + + org.apache.servicecomb + registry-nacos + ${project.version} + + + org.apache.servicecomb + registry-zookeeper + ${project.version} + + + org.apache.servicecomb + registry-etcd + ${project.version} + + + org.apache.servicecomb + config-etcd + ${project.version} + + + org.apache.servicecomb + registry-consul + ${project.version} + + + + org.apache.servicecomb + solution-basic + ${project.version} + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-servlet + ${project.version} + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + ${project.version} + + + + org.apache.servicecomb + servicestage + ${project.version} + + + org.apache.servicecomb + dashboard + ${project.version} + + + org.apache.servicecomb + darklaunch + ${project.version} + + + + org.apache.servicecomb + swagger-generator-core + ${project.version} + + + org.apache.servicecomb + swagger-generator-jaxrs + ${project.version} + + + org.apache.servicecomb + swagger-generator-spring-data + ${project.version} + + + org.apache.servicecomb + swagger-generator-springmvc + ${project.version} + + + org.apache.servicecomb + swagger-invocation-core + ${project.version} + + + org.apache.servicecomb + swagger-invocation-jaxrs + ${project.version} + + + org.apache.servicecomb + swagger-invocation-springmvc + ${project.version} + + + org.apache.servicecomb + swagger-invocation-validator + ${project.version} + + + + org.apache.servicecomb + tracing-common + ${project.version} + + + org.apache.servicecomb + tracing-zipkin + ${project.version} + + + + org.apache.servicecomb + transport-common + ${project.version} + + + org.apache.servicecomb + transport-highway + ${project.version} + + + org.apache.servicecomb + transport-rest-client + ${project.version} + + + org.apache.servicecomb + transport-rest-servlet + ${project.version} + + + org.apache.servicecomb + transport-rest-vertx + ${project.version} + + + + diff --git a/dependencies/default/pom.xml b/dependencies/default/pom.xml new file mode 100644 index 00000000000..22f2190ddd1 --- /dev/null +++ b/dependencies/default/pom.xml @@ -0,0 +1,510 @@ + + + + + 4.0.0 + + org.apache.servicecomb + java-chassis-dependencies-parent + 3.4.0-SNAPSHOT + + + java-chassis-dependencies + Java Chassis::Dependencies::Default + pom + + + + 1.2.2 + 3.27.7 + 4.3.0 + 6.3.0 + 1.11.0 + 2.21.0 + 2.6 + 3.20.0 + 1.3.5 + 1.15.0 + 5.9.0 + 1.0.3 + 3.0.2 + 1.17.12 + 1.17.13 + 33.5.0-jre + 5.1.0 + 1.3 + 2.2.2 + 9.1.0.Final + 4.5.14 + 1.5.18 + 2.20.1 + 1.6.0 + 1.3.2 + 1 + 1.5 + 4.0.2 + 1.34 + 4.13.2 + 5.13.4 + 1.2.17 + 2.25.3 + 3.9.12 + 1.16.4 + 5.21.0 + 5.2.0 + 5.15.0 + 3.1.1 + 0.3.0 + 0.16.0 + 3.23.4 + 1.8.0 + 2.2.27 + 1.0.4 + 1.7.0 + 2.7.18 + 3.1.12 + 1.0.0 + 0.13.2 + 6.0.0 + 1.7.36 + 2.5 + 2.2.41 + 5.0.8 + 3.5.1 + 3.4.0 + 0.8.6 + 1.8.0 + + ${basedir}/../.. + + + + + + + com.fasterxml.jackson + jackson-bom + ${jackson.version} + import + pom + + + + org.mockito + mockito-bom + ${mockito.version} + import + pom + + + org.mockito + mockito-inline + ${mockito-inline.version} + test + + + + com.github.seanyinx + unit-scaffolding + ${seanyinx.version} + 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 + ${findbugs-jsr305.version} + provided + + + + com.google.guava + guava + ${guava.version} + + + * + * + + + + + com.google.guava + failureaccess + ${failureaccess.version} + + + + com.google.inject + guice + ${guice.version} + + + aopalliance + aopalliance + + + + + com.google.inject.extensions + guice-assistedinject + ${guice.version} + + + + + com.google.protobuf + protobuf-java + ${protobuf.version} + test + + + + commons-beanutils + commons-beanutils + ${commons-beanutils.version} + + + + commons-io + commons-io + ${commons-io.version} + + + + commons-lang + commons-lang + ${commons-lang.version} + + + + commons-logging + commons-logging + ${commons-logging.version} + + + + org.apache.maven + maven-model + ${maven-model.version} + + + + io.micrometer + micrometer-bom + ${micrometer.version} + pom + import + + + io.prometheus + simpleclient_bom + ${prometheus.version} + pom + import + + + + io.protostuff + protostuff-core + ${protostuff.version} + + + io.protostuff + protostuff-parser + ${protostuff-parser.version} + + + org.abego.treelayout + org.abego.treelayout.core + + + com.google.inject.extensions + guice-multibindings + + + + + io.protostuff + protostuff-runtime + ${protostuff.version} + + + + io.swagger.core.v3 + swagger-core-jakarta + ${swagger.version} + + + + io.reactivex.rxjava3 + rxjava + ${rxjava.version} + + + org.reactivestreams + reactive-streams + ${reactive-streams.version} + + + + io.zipkin.brave + brave-bom + ${brave.version} + pom + import + + + + io.zipkin.reporter2 + zipkin-reporter-bom + ${zipkin-reporter.version} + + + + io.zipkin.zipkin2 + zipkin + ${zipkin.version} + + + + jakarta.servlet + jakarta.servlet-api + ${servlet-api.version} + + + com.sun.activation + jakarta.activation + ${activation.version} + + + javax.annotation + javax.annotation-api + ${javax-annotation.version} + + + + javax.inject + javax.inject + ${javax-inject.version} + + + + javax.resource + connector-api + ${javax-resource.version} + + + + junit + junit + ${junit.version} + test + + + org.junit + junit-bom + ${junit5.version} + pom + import + + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + + org.apache.commons + commons-text + ${commons-text.version} + + + + org.apache.httpcomponents + httpclient + ${httpcomponents.version} + + + + org.apache.logging.log4j + log4j-api + ${log4j2.version} + + + org.apache.logging.log4j + log4j-core + ${log4j2.version} + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j2.version} + + + + org.awaitility + awaitility + ${awaitility.version} + test + + + + org.assertj + assertj-core + ${assertj.version} + test + + + + org.glassfish.jersey.core + jersey-common + ${jersey.version} + + + + org.glassfish.jersey.core + jersey-client + ${jersey.version} + + + + org.hamcrest + hamcrest-all + ${hamcrest.version} + test + + + org.hamcrest + hamcrest-core + ${hamcrest.version} + test + + + + org.hibernate.validator + hibernate-validator + ${hibernate-validator.version} + + + org.hibernate.validator + hibernate-validator-annotation-processor + ${hibernate-validator.version} + + + + org.hdrhistogram + HdrHistogram + ${hdr-histogram.version} + + + + org.jmockit + jmockit + ${jmockit.version} + test + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + io.vertx + vertx-dependencies + ${vertx.version} + pom + import + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + org.yaml + snakeyaml + ${snakeyaml.version} + + + + com.alibaba.nacos + nacos-client + ${nacos-client.version} + + + + io.github.resilience4j + resilience4j-bom + ${resilience4j.versioin} + pom + import + + + + org.java-websocket + Java-WebSocket + ${java-websocket.version} + + + + org.apache.servicecomb + java-chassis-bom + ${project.version} + 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 new file mode 100644 index 00000000000..4ee9c9ea071 --- /dev/null +++ b/dependencies/pom.xml @@ -0,0 +1,38 @@ + + + + + 4.0.0 + + + org.apache.servicecomb + java-chassis + 3.4.0-SNAPSHOT + + + java-chassis-dependencies-parent + Java Chassis::Dependencies + pom + + + bom + default + + + diff --git a/distribution/pom.xml b/distribution/pom.xml new file mode 100644 index 00000000000..ddd1e3de25d --- /dev/null +++ b/distribution/pom.xml @@ -0,0 +1,361 @@ + + + + 4.0.0 + + org.apache.servicecomb + java-chassis-parent + 3.4.0-SNAPSHOT + ../parents/default + + apache-servicecomb-java-chassis-distribution + Java Chassis::Distribution + pom + + + + + org.apache.servicecomb + config-center-client + + + org.apache.servicecomb + config-clients-common + + + org.apache.servicecomb + config-kie-client + + + org.apache.servicecomb + dashboard-client + + + org.apache.servicecomb + http-client-common + + + org.apache.servicecomb + service-center-client + + + org.apache.servicecomb + registry-nacos + + + + + org.apache.servicecomb + common-access-log + + + org.apache.servicecomb + common-protobuf + + + org.apache.servicecomb + common-rest + + + + + org.apache.servicecomb + java-chassis-core + + + + + org.apache.servicecomb + config-apollo + + + org.apache.servicecomb + config-cc + + + org.apache.servicecomb + config-kie + + + org.apache.servicecomb + config-nacos + + + + + org.apache.servicecomb + edge-core + + + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-metrics + + + org.apache.servicecomb + foundation-protobuf + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + foundation-spi + + + org.apache.servicecomb + foundation-ssl + + + org.apache.servicecomb + foundation-test-scaffolding + + + org.apache.servicecomb + foundation-vertx + + + + + org.apache.servicecomb + servicecomb-governance + + + + + org.apache.servicecomb + handler-fault-injection + + + org.apache.servicecomb + handler-flowcontrol-qps + + + org.apache.servicecomb + handler-governance + + + org.apache.servicecomb + handler-loadbalance + + + org.apache.servicecomb + handler-publickey-auth + + + org.apache.servicecomb + handler-router + + + org.apache.servicecomb + handler-tracing-zipkin + + + + + org.apache.servicecomb + metrics-core + + + org.apache.servicecomb + metrics-prometheus + + + + + org.apache.servicecomb + provider-jaxrs + + + org.apache.servicecomb + provider-pojo + + + org.apache.servicecomb + provider-rest-common + + + org.apache.servicecomb + provider-springmvc + + + + + org.apache.servicecomb + registry-lightweight + + + org.apache.servicecomb + registry-local + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + registry-zero-config + + + + + org.apache.servicecomb + solution-basic + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-servlet + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + + + org.apache.servicecomb + servicestage + + + org.apache.servicecomb + dashboard + + + org.apache.servicecomb + darklaunch + + + + org.apache.servicecomb + swagger-generator-core + + + org.apache.servicecomb + swagger-generator-jaxrs + + + org.apache.servicecomb + swagger-generator-spring-data + + + org.apache.servicecomb + swagger-generator-springmvc + + + + org.apache.servicecomb + swagger-invocation-core + + + org.apache.servicecomb + swagger-invocation-jaxrs + + + org.apache.servicecomb + swagger-invocation-springmvc + + + org.apache.servicecomb + swagger-invocation-validator + + + + + org.apache.servicecomb + tracing-common + + + org.apache.servicecomb + tracing-zipkin + + + + + org.apache.servicecomb + transport-common + + + org.apache.servicecomb + transport-highway + + + org.apache.servicecomb + transport-rest-client + + + org.apache.servicecomb + transport-rest-servlet + + + org.apache.servicecomb + transport-rest-vertx + + + + + + release + + + + maven-assembly-plugin + + + bin + package + + single + + + + src/assembly/bin.xml + + + + + src + package + + single + + + + src/assembly/src.xml + + + + + + + + + + diff --git a/distribution/src/assembly/bin.xml b/distribution/src/assembly/bin.xml new file mode 100644 index 00000000000..214ed04a649 --- /dev/null +++ b/distribution/src/assembly/bin.xml @@ -0,0 +1,58 @@ + + + + true + ${project.build.finalName}-bin + bin + + zip + + + + ../ + ./ + + README_ZH.md + README.md + + + + src/release + ./ + + NOTICE + LICENSE + licenses/** + + + + + + true + false + ./libs + runtime + + org.apache.servicecomb:* + org.apache.servicecomb.archetypes:* + + + + diff --git a/distribution/src/assembly/src.xml b/distribution/src/assembly/src.xml new file mode 100644 index 00000000000..79a8a3c89ca --- /dev/null +++ b/distribution/src/assembly/src.xml @@ -0,0 +1,61 @@ + + + + src + true + ${project.build.finalName}-src + + zip + + + + .. + + **/* + + + + etc/eclipse-java-google-style.xml + etc/intellij-java-google-style.xml + + **/eclipse-classes/** + **/target/** + **/build/** + **/eclipse-classes/** + **/.* + **/.*/** + *.enc + *.gpg + random_seed + **/surefire* + **/svn-commit* + + **/.idea/** + **/*.iml + **/*.ipr + **/*.iws + + **/cobertura.ser + + **/node_modules/** + + + + diff --git a/distribution/src/release/LICENSE b/distribution/src/release/LICENSE new file mode 100644 index 00000000000..b75be79468a --- /dev/null +++ b/distribution/src/release/LICENSE @@ -0,0 +1,240 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +======================================================================= +Apache ServiceComb Java Chassis Subcomponents: + +The Apache ServiceComb Java Chassis project contains subcomponents with +separate copyright notices and license terms. Your use of these +subcomponents is subject to the terms and conditions of the +following licenses. + +================================================================ +For foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/common/utils/MimeTypesUtils.java + transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java +================================================================ +This product bundles files from vertx which is licensed under the Apache License v2. +For details, see https://github.com/vert-x3/vertx-web + +================================================================ +For swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/AbstractBaseIntegerProperty.java +================================================================ +This product bundles files from swagger which is licensed under the Apache License v2. +For details, see https://github.com/swagger-api/swagger-core + +================================================================ +For foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/ArrayFieldMapEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldMapEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldSchema.java + foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldTypeUtils.java + foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/HashFieldMapEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/ByteArrayInputEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/InputEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/OutputEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/package-info.java + foundations/foundation-protobuf/src/main/java/io/protostuff/ProtobufOutputEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaEx.java + foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaReader.java + 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 diff --git a/distribution/src/release/NOTICE b/distribution/src/release/NOTICE new file mode 100644 index 00000000000..5ed927047d5 --- /dev/null +++ b/distribution/src/release/NOTICE @@ -0,0 +1,5 @@ +Apache ServiceComb Java Chassis +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 new file mode 100644 index 00000000000..1620af43b30 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-abego @@ -0,0 +1,27 @@ +[The "BSD license"] +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 +modification, are permitted provided that the following conditions are met: + +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 + 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 + 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 +POSSIBILITY OF SUCH DAMAGE. diff --git a/distribution/src/release/licenses/LICENSE-animalsniffer b/distribution/src/release/licenses/LICENSE-animalsniffer new file mode 100644 index 00000000000..4e28c7bc322 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-animalsniffer @@ -0,0 +1,21 @@ + The MIT License + + Copyright (c) 2009 codehaus.org. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + 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-antlr b/distribution/src/release/licenses/LICENSE-antlr new file mode 100644 index 00000000000..2042d1bda6c --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-antlr @@ -0,0 +1,52 @@ +[The "BSD 3-clause license"] +Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. + +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, 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 and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder 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 AUTHOR ``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 AUTHOR 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. + +===== + +MIT License for codepointat.js from https://git.io/codepointat +MIT License for fromcodepoint.js from https://git.io/vDW1m + +Copyright Mathias Bynens + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE 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-asm b/distribution/src/release/licenses/LICENSE-asm new file mode 100644 index 00000000000..8806c7d030f --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-asm @@ -0,0 +1,27 @@ + ASM: a very small and fast Java bytecode manipulation framework + Copyright (c) 2000-2011 INRIA, France Telecom + All rights reserved. + + 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, 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 and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders 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. diff --git a/distribution/src/release/licenses/LICENSE-bouncycastle b/distribution/src/release/licenses/LICENSE-bouncycastle new file mode 100644 index 00000000000..40fcf76996c --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-bouncycastle @@ -0,0 +1,7 @@ +Copyright (c) 2000 - 2017 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 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-cc0 b/distribution/src/release/licenses/LICENSE-cc0 new file mode 100644 index 00000000000..4417dbeef04 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-cc0 @@ -0,0 +1,26 @@ +Statement of Purpose +The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: + +the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; +moral rights retained by the original author(s) and/or performer(s); +publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; +rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; +rights protecting the extraction, dissemination, use and reuse of data in a Work; +database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and +other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. +2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + +No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. +Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. +Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. +Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. diff --git a/distribution/src/release/licenses/LICENSE-cddl b/distribution/src/release/licenses/LICENSE-cddl new file mode 100644 index 00000000000..b1c74f95ede --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-cddl @@ -0,0 +1,759 @@ +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates or + contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), and + the Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing Original + Software with files containing Modifications, in each case including + portions thereof. + + 1.4. "Executable" means the Covered Software in any form other than + Source Code. + + 1.5. "Initial Developer" means the individual or entity that first + makes Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means the Source Code and Executable form of + any of the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original Software + or previous Modifications; + + B. Any new file that contains any part of the Original Software or + previous Modification; or + + C. Any new file that is contributed or otherwise made available + under the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable form + of computer software code that is originally released under this + License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.12. "Source Code" means (a) the common form of computer software + code in which modifications are made and (b) associated + documentation included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, + this License. For legal entities, "You" includes any entity which + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject + to third party intellectual property claims, the Initial Developer + hereby grants You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer, to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Software (or portions thereof), with or without Modifications, + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using or selling of + Original Software, to make, have made, use, practice, sell, and + offer for sale, and/or otherwise dispose of the Original Software + (or portions thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are effective on + the date Initial Developer first distributes or otherwise makes the + Original Software available to a third party under the terms of this + License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: (1) for code that You delete from the Original Software, or + (2) for infringements caused by: (i) the modification of the + Original Software, or (ii) the combination of the Original Software + with other software or devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject + to third party intellectual property claims, each Contributor hereby + grants You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof), either on an + unmodified basis, with other Modifications, as Covered Software + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or selling + of Modifications made by that Contributor either alone and/or in + combination with its Contributor Version (or portions of such + combination), to make, use, sell, offer for sale, have made, and/or + otherwise dispose of: (1) Modifications made by that Contributor (or + portions thereof); and (2) the combination of Modifications made by + that Contributor with its Contributor Version (or portions of such + combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective + on the date Contributor first distributes or otherwise makes the + Modifications available to a third party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: (1) for any code that Contributor has deleted from the + Contributor Version; (2) for infringements caused by: (i) third + party modifications of Contributor Version, or (ii) the combination + of Modifications made by that Contributor with other software + (except as part of the Contributor Version) or other devices; or (3) + under Patent Claims infringed by Covered Software in the absence of + Modifications made by that Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make available + in Executable form must also be made available in Source Code form + and that Source Code form must be distributed only under the terms + of this License. You must include a copy of this License with every + copy of the Source Code form of the Covered Software You distribute + or otherwise make available. You must inform recipients of any such + Covered Software in Executable form as to how they can obtain such + Covered Software in Source Code form in a reasonable manner on or + through a medium customarily used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You + believe Your Modifications are Your original creation(s) and/or You + have sufficient rights to grant the rights conveyed by this License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that + identifies You as the Contributor of the Modification. You may not + remove or alter any copyright, patent or trademark notices contained + within the Covered Software, or any notices of licensing or any + descriptive text giving attribution to any Contributor or the + Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in + Source Code form that alters or restricts the applicable version of + this License or the recipients' rights hereunder. You may choose to + offer, and to charge a fee for, warranty, support, indemnity or + liability obligations to one or more recipients of Covered Software. + However, you may do so only on Your own behalf, and not on behalf of + the Initial Developer or any Contributor. You must make it + absolutely clear that any such warranty, support, indemnity or + liability obligation is offered by You alone, and You hereby agree + to indemnify the Initial Developer and every Contributor for any + liability incurred by the Initial Developer or such Contributor as a + result of warranty, support, indemnity or liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software under + the terms of this License or under the terms of a license of Your + choice, which may contain terms different from this License, + provided that You are in compliance with the terms of this License + and that the license for the Executable form does not attempt to + limit or alter the recipient's rights in the Source Code form from + the rights set forth in this License. If You distribute the Covered + Software in Executable form under a different license, You must make + it absolutely clear that any terms which differ from this License + are offered by You alone, not by the Initial Developer or + Contributor. You hereby agree to indemnify the Initial Developer and + every Contributor for any liability incurred by the Initial + Developer or such Contributor as a result of any such terms You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with + other code not governed by the terms of this License and distribute + the Larger Work as a single product. In such a case, You must make + sure the requirements of this License are fulfilled for the Covered + Software. + +4. Versions of the License. + + 4.1. New Versions. + + Oracle is the initial license steward and may publish revised and/or + new versions of this License from time to time. Each version will be + given a distinguishing version number. Except as provided in Section + 4.3, no one other than the license steward has the right to modify + this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the + Covered Software available under the terms of the version of the + License under which You originally received the Covered Software. If + the Initial Developer includes a notice in the Original Software + prohibiting it from being distributed or otherwise made available + under any subsequent version of the License, You must distribute and + make the Covered Software available under the terms of the version + of the License under which You originally received the Covered + Software. Otherwise, You may also choose to use, distribute or + otherwise make the Covered Software available under the terms of any + subsequent version of the License published by the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new + license for Your Original Software, You may create and use a + modified version of this License if You: (a) rename the license and + remove any references to the name of the license steward (except to + note that the license differs from this License); and (b) otherwise + make it clear that the license contains terms which differ from this + License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE + IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR + NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF + THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE + DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY + OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, + REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN + ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS + AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to + cure such breach within 30 days of becoming aware of the breach. + Provisions which, by their nature, must remain in effect beyond the + termination of this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding + declaratory judgment actions) against Initial Developer or a + Contributor (the Initial Developer or Contributor against whom You + assert such claim is referred to as "Participant") alleging that the + Participant Software (meaning the Contributor Version where the + Participant is a Contributor or the Original Software where the + Participant is the Initial Developer) directly or indirectly + infringes any patent, then any and all rights granted directly or + indirectly to You by such Participant, the Initial Developer (if the + Initial Developer is not the Participant) and all Contributors under + Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice + from Participant terminate prospectively and automatically at the + expiration of such 60 day notice period, unless if within such 60 + day period You withdraw Your claim with respect to the Participant + Software against such Participant either unilaterally or pursuant to + a written agreement with Participant. + + 6.3. If You assert a patent infringement claim against Participant + alleging that the Participant Software directly or indirectly + infringes any patent where such claim is resolved (such as by + license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 6.4. In the event of termination under Sections 6.1 or 6.2 above, + all end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses + granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE + INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF + COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE + TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER + FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR + LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE + POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT + APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH + PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH + LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR + LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION + AND LIMITATION MAY NOT APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is defined + in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" (as that term is defined at 48 C.F.R. § + 252.227-7014(a)(1)) and "commercial computer software documentation" + as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent + with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 + (June 1995), all U.S. Government End Users acquire Covered Software + with only those rights set forth herein. This U.S. Government Rights + clause is in lieu of, and supersedes, any other FAR, DFAR, or other + clause or provision that addresses Government rights in computer + software under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + the law of the jurisdiction specified in a notice contained within + the Original Software (except to the extent applicable law, if any, + provides otherwise), excluding such jurisdiction's conflict-of-law + provisions. Any litigation relating to this License shall be subject + to the jurisdiction of the courts located in the jurisdiction and + venue specified in a notice contained within the Original Software, + with the losing party responsible for costs, including, without + limitation, court costs and reasonable attorneys' fees and expenses. + The application of the United Nations Convention on Contracts for + the International Sale of Goods is expressly excluded. Any law or + regulation which provides that the language of a contract shall be + construed against the drafter shall not apply to this License. You + agree that You alone are responsible for compliance with the United + States export administration regulations (and the export control + laws and regulation of any other countries) when You use, distribute + or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +------------------------------------------------------------------------ + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION +LICENSE (CDDL) + +The code released under the CDDL shall be governed by the laws of the +State of California (excluding conflict-of-law provisions). Any +litigation relating to this License shall be subject to the jurisdiction +of the Federal Courts of the Northern District of California and the +state courts of the State of California, with venue lying in Santa Clara +County, California. + + + + The GNU General Public License (GPL) Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor +Boston, MA 02110-1335 +USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software--to +make sure the software is free for all its users. This General Public +License applies to most of the Free Software Foundation's software and +to any other program whose authors commit to using it. (Some other Free +Software Foundation software is covered by the GNU Library General +Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this +service if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone +to deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis +or for a fee, you must give the recipients all the rights that you have. +You must make sure that they, too, receive or can get the source code. +And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must +be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a +notice placed by the copyright holder saying it may be distributed under +the terms of this General Public License. The "Program", below, refers +to any such program or work, and a "work based on the Program" means +either the Program or any derivative work under copyright law: that is +to say, a work containing the Program or a portion of it, either +verbatim or with modifications and/or translated into another language. +(Hereinafter, translation is included without limitation in the term +"modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of running +the Program is not restricted, and the output from the Program is +covered only if its contents constitute a work based on the Program +(independent of having been made by running the Program). Whether that +is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously +and appropriately publish on each copy an appropriate copyright notice +and disclaimer of warranty; keep intact all the notices that refer to +this License and to the absence of any warranty; and give any other +recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of +it, thus forming a work based on the Program, and copy and distribute +such modifications or work under the terms of Section 1 above, provided +that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any part + thereof, to be licensed as a whole at no charge to all third parties + under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this License. + (Exception: if the Program itself is interactive but does not + normally print such an announcement, your work based on the Program + is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, and +can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based on +the Program, the distribution of the whole must be on the terms of this +License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of a +storage or distribution medium does not bring the other work under the +scope of this License. + +3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your cost + of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed + only for noncommercial distribution and only if you received the + program in object code or executable form with such an offer, in + accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source code +means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to control +compilation and installation of the executable. However, as a special +exception, the source code distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies the +executable. + +If distribution of executable or object code is made by offering access +to copy from a designated place, then offering equivalent access to copy +the source code from the same place counts as distribution of the source +code, even though third parties are not compelled to copy the source +along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt otherwise +to copy, modify, sublicense or distribute the Program is void, and will +automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will +not have their licenses terminated so long as such parties remain in +full compliance. + +5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and all +its terms and conditions for copying, distributing or modifying the +Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further restrictions +on the recipients' exercise of the rights granted herein. You are not +responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot distribute +so as to satisfy simultaneously your obligations under this License and +any other pertinent obligations, then as a consequence you may not +distribute the Program at all. For example, if a patent license would +not permit royalty-free redistribution of the Program by all those who +receive copies directly or indirectly through you, then the only way you +could satisfy both it and this License would be to refrain entirely from +distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is implemented +by public license practices. Many people have made generous +contributions to the wide range of software distributed through that +system in reliance on consistent application of that system; it is up to +the author/donor to decide if he or she is willing to distribute +software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be +a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License may +add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among countries +not thus excluded. In such case, this License incorporates the +limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new +versions of the General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a version +number of this License, you may choose any version ever published by the +Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the +author to ask for permission. For software which is copyrighted by the +Free Software Foundation, write to the Free Software Foundation; we +sometimes make exceptions for this. Our decision will be guided by the +two goals of preserving the free status of all derivatives of our free +software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH +YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL +NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR +DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL +DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM +(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF +THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR +OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type + `show w'. This is free software, and you are welcome to redistribute + it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the commands +you use may be called something other than `show w' and `show c'; they +could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (which makes passes at compilers) written by + James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications +with the library. If this is what you want to do, use the GNU Library +General Public License instead of this License. + +# + +Certain source files distributed by Oracle America, Inc. and/or its +affiliates are subject to the following clarification and special +exception to the GPLv2, based on the GNU Project exception for its +Classpath libraries, known as the GNU Classpath Exception, but only +where Oracle has expressly included in the particular source file's +header the words "Oracle designates this particular file as subject to +the "Classpath" exception as provided by Oracle in the LICENSE file +that accompanied this code." + +You should also note that Oracle includes multiple, independent +programs in this software package. Some of those programs are provided +under licenses deemed incompatible with the GPLv2 by the Free Software +Foundation and others. For example, the package includes programs +licensed under the Apache License, Version 2.0. Such programs are +licensed to you under their original licenses. + +Oracle facilitates your further distribution of this package by adding +the Classpath Exception to the necessary parts of its GPLv2 code, which +permits you to use that code in combination with other independent +modules not licensed under the GPLv2. However, note that this would +not permit you to commingle code under an incompatible license with +Oracle's GPLv2 licensed code by, for example, cutting and pasting such +code into a file also containing Oracle's GPLv2 licensed code and then +distributing the result. Additionally, if you were to remove the +Classpath Exception from any of the files to which it applies and +distribute the result, you would likely be required to license some or +all of the other code in that distribution under the GPLv2 as well, and +since the GPLv2 is incompatible with the license terms of some items +included in the distribution by Oracle, removing the Classpath +Exception could therefore effectively compromise your ability to +further distribute the package. + +Proceed with caution and we recommend that you obtain the advice of a +lawyer skilled in open source matters before removing the Classpath +Exception or making modifications to this package which may +subsequently be redistributed and/or involve the use of third party +software. + +CLASSPATH EXCEPTION +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License version 2 cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from or +based on this library. If you modify this library, you may extend this +exception to your version of the library, but you are not obligated to +do so. If you do not wish to do so, delete this exception statement +from your version. diff --git a/distribution/src/release/licenses/LICENSE-checkerqual b/distribution/src/release/licenses/LICENSE-checkerqual new file mode 100644 index 00000000000..7f197e96dec --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-checkerqual @@ -0,0 +1,19 @@ +MIT License: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +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 new file mode 100644 index 00000000000..90202907fd2 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-edl-v10 @@ -0,0 +1,12 @@ +Eclipse Distribution License - v 1.0 + +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. + +All rights reserved. + +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 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. diff --git a/distribution/src/release/licenses/LICENSE-epl-v10 b/distribution/src/release/licenses/LICENSE-epl-v10 new file mode 100644 index 00000000000..b0476ee93e8 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-epl-v10 @@ -0,0 +1,86 @@ +Eclipse Public License - v 1.0 +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and + +b) in the case of each subsequent Contributor: + +i) changes to the Program, and + +ii) additions to the Program; + +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. + +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. + +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. + +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and + +b) its license agreement: + +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; + +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; + +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and + +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and + +b) a copy of this Agreement must be included with each copy of the Program. + +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +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. diff --git a/distribution/src/release/licenses/LICENSE-epl-v20 b/distribution/src/release/licenses/LICENSE-epl-v20 new file mode 100644 index 00000000000..cc0fb9400de --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-epl-v20 @@ -0,0 +1,81 @@ +Eclipse Public License - v 2.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (“AGREEMENT”). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS +“Contribution” means: + +a) in the case of the initial Contributor, the initial content Distributed under this Agreement, and +b) in the case of each subsequent Contributor: +i) changes to the Program, and +ii) additions to the Program; +where such changes and/or additions to the Program originate from and are Distributed by that particular Contributor. A Contribution “originates” from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include changes or additions to the Program that are not Modified Works. +“Contributor” means any person or entity that Distributes the Program. + +“Licensed Patents” mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +“Program” means the Contributions Distributed in accordance with this Agreement. + +“Recipient” means anyone who receives the Program under this Agreement or any Secondary License (as applicable), including Contributors. + +“Derivative Works” shall mean any work, whether in Source Code or other form, that is based on (or derived from) the Program and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. + +“Modified Works” shall mean any work in Source Code or other form that results from an addition to, deletion from, or modification of the contents of the Program, including, for purposes of clarity any new file in Source Code form that contains any contents of the Program. Modified Works shall not include works that contain only declarations, interfaces, types, classes, structures, or files of the Program solely in each case in order to link to, bind by name, or subclass the Program or Modified Works thereof. + +“Distribute” means the acts of a) distributing or b) making available in any manner that enables the transfer of a copy. + +“Source Code” means the form of a Program preferred for making modifications, including but not limited to software source code, documentation source, and configuration files. + +“Secondary License” means either the GNU General Public License, Version 2.0, or any later versions of that license, including any exceptions or additional permissions as identified by the initial Contributor. + +2. GRANT OF RIGHTS +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, Distribute and sublicense the Contribution of such Contributor, if any, and such Derivative Works. +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in Source Code or other form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to Distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. +e) Notwithstanding the terms of any Secondary License, no Contributor makes additional grants to any Recipient (other than those set forth in this Agreement) as a result of such Recipient's receipt of the Program under the terms of a Secondary License (if permitted under the terms of Section 3). +3. REQUIREMENTS +3.1 If a Contributor Distributes the Program in any form, then: + +a) the Program must also be made available as Source Code, in accordance with section 3.2, and the Contributor must accompany the Program with a statement that the Source Code for the Program is available under this Agreement, and informs Recipients how to obtain it in a reasonable manner on or through a medium customarily used for software exchange; and +b) the Contributor may Distribute the Program under a license different than this Agreement, provided that such license: +i) effectively disclaims on behalf of all other Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; +ii) effectively excludes on behalf of all other Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; +iii) does not attempt to limit or alter the recipients' rights in the Source Code under section 3.2; and +iv) requires any subsequent distribution of the Program by any party to be under a license that satisfies the requirements of this section 3. +3.2 When the Program is Distributed as Source Code: + +a) it must be made available under this Agreement, or if the Program (i) is combined with other material in a separate file or files made available under a Secondary License, and (ii) the initial Contributor attached to the Source Code the notice described in Exhibit A of this Agreement, then the Program may be made available under the terms of such Secondary Licenses, and +b) a copy of this Agreement must be included with each copy of the Program. +3.3 Contributors may not remove or alter any copyright, patent, trademark, attribution notices, disclaimers of warranty, or limitations of liability (‘notices’) contained within the Program from any copy of the Program which they Distribute, provided that Contributors may add their own appropriate notices. + +4. COMMERCIAL DISTRIBUTION +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor (“Commercial Contributor”) hereby agrees to defend and indemnify every other Contributor (“Indemnified Contributor”) against any losses, damages and costs (collectively “Losses”) arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +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. Nothing in this Agreement is intended to be enforceable by any entity that is not a Contributor or Recipient. No third-party beneficiary rights are created under this Agreement. + +Exhibit A – Form of Secondary Licenses Notice +“This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), version(s), and exceptions or additional permissions here}.” + +Simply including a copy of this Agreement, including this Exhibit A is not sufficient to license the Source Code under Secondary Licenses. + +If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. diff --git a/distribution/src/release/licenses/LICENSE-hamcrest b/distribution/src/release/licenses/LICENSE-hamcrest new file mode 100644 index 00000000000..4933bda5bac --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-hamcrest @@ -0,0 +1,27 @@ +BSD License + +Copyright (c) 2000-2015 www.hamcrest.org +All rights reserved. + +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 Hamcrest 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. diff --git a/distribution/src/release/licenses/LICENSE-hdrhistogram b/distribution/src/release/licenses/LICENSE-hdrhistogram new file mode 100644 index 00000000000..401ccfb0ec5 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-hdrhistogram @@ -0,0 +1,41 @@ +The code in this repository code was Written by Gil Tene, Michael Barker, +and Matt Warren, and released to the public domain, as explained at +http://creativecommons.org/publicdomain/zero/1.0/ + +For users of this code who wish to consume it under the "BSD" license +rather than under the public domain or CC0 contribution text mentioned +above, the code found under this directory is *also* provided under the +following license (commonly referred to as the BSD 2-Clause License). This +license does not detract from the above stated release of the code into +the public domain, and simply represents an additional license granted by +the Author. + +----------------------------------------------------------------------------- +** Beginning of "BSD 2-Clause License" text. ** + + Copyright (c) 2012, 2013, 2014, 2015, 2016 Gil Tene + Copyright (c) 2014 Michael Barker + Copyright (c) 2014 Matt Warren + All rights reserved. + + 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, + 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 + and/or other materials provided with the distribution. + + 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-icu4j b/distribution/src/release/licenses/LICENSE-icu4j new file mode 100644 index 00000000000..25b6eb9d341 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-icu4j @@ -0,0 +1,414 @@ +COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later) + +Copyright © 1991-2018 Unicode, Inc. All rights reserved. +Distributed under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the "Data Files") or Unicode software and any associated documentation +(the "Software") to deal in the Data Files or Software +without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files +or Software are furnished to do so, provided that either +(a) this copyright and permission notice appear with all copies +of the Data Files or Software, or +(b) this copyright and permission notice appear in associated +Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. + +--------------------- + +Third-Party Software Licenses + +This section contains third-party software notices and/or additional +terms for licensed third-party software components included within ICU +libraries. + +1. ICU License - ICU 1.8.1 to ICU 57.1 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1995-2016 International Business Machines Corporation and others +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, provided that the above +copyright notice(s) and this permission notice appear in all copies of +the Software and that both the above copyright notice(s) and this +permission notice appear in supporting documentation. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY +SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization +of the copyright holder. + +All trademarks and registered trademarks mentioned herein are the +property of their respective owners. + +2. Chinese/Japanese Word Break Dictionary Data (cjdict.txt) + + # The Google Chrome software developed by Google is licensed under + # the BSD license. Other software included in this distribution is + # provided under other licenses, as set forth below. + # + # The BSD License + # http://opensource.org/licenses/bsd-license.php + # Copyright (C) 2006-2008, Google Inc. + # + # All rights reserved. + # + # 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. + # + # + # The word list in cjdict.txt are generated by combining three word lists + # listed below with further processing for compound word breaking. The + # frequency is generated with an iterative training against Google web + # corpora. + # + # * Libtabe (Chinese) + # - https://sourceforge.net/project/?group_id=1519 + # - Its license terms and conditions are shown below. + # + # * IPADIC (Japanese) + # - http://chasen.aist-nara.ac.jp/chasen/distribution.html + # - Its license terms and conditions are shown below. + # + # ---------COPYING.libtabe ---- BEGIN-------------------- + # + # /* + # * Copyright (c) 1999 TaBE Project. + # * Copyright (c) 1999 Pai-Hsiang Hsiao. + # * All rights reserved. + # * + # * 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 the TaBE Project 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 + # * REGENTS 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. + # */ + # + # /* + # * Copyright (c) 1999 Computer Systems and Communication Lab, + # * Institute of Information Science, Academia + # * Sinica. All rights reserved. + # * + # * 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 the Computer Systems and Communication Lab + # * 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 + # * REGENTS 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. + # */ + # + # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, + # University of Illinois + # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 + # + # ---------------COPYING.libtabe-----END-------------------------------- + # + # + # ---------------COPYING.ipadic-----BEGIN------------------------------- + # + # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science + # and Technology. All Rights Reserved. + # + # Use, reproduction, and distribution of this software is permitted. + # Any copy of this software, whether in its original form or modified, + # must include both the above copyright notice and the following + # paragraphs. + # + # Nara Institute of Science and Technology (NAIST), + # the copyright holders, disclaims all warranties with regard to this + # software, including all implied warranties of merchantability and + # fitness, in no event shall NAIST be liable for + # any special, indirect or consequential damages or any damages + # whatsoever resulting from loss of use, data or profits, whether in an + # action of contract, negligence or other tortuous action, arising out + # of or in connection with the use or performance of this software. + # + # A large portion of the dictionary entries + # originate from ICOT Free Software. The following conditions for ICOT + # Free Software applies to the current dictionary as well. + # + # Each User may also freely distribute the Program, whether in its + # original form or modified, to any third party or parties, PROVIDED + # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear + # on, or be attached to, the Program, which is distributed substantially + # in the same form as set out herein and that such intended + # distribution, if actually made, will neither violate or otherwise + # contravene any of the laws and regulations of the countries having + # jurisdiction over the User or the intended distribution itself. + # + # NO WARRANTY + # + # The program was produced on an experimental basis in the course of the + # research and development conducted during the project and is provided + # to users as so produced on an experimental basis. Accordingly, the + # program is provided without any warranty whatsoever, whether express, + # implied, statutory or otherwise. The term "warranty" used herein + # includes, but is not limited to, any warranty of the quality, + # performance, merchantability and fitness for a particular purpose of + # the program and the nonexistence of any infringement or violation of + # any right of any third party. + # + # Each user of the program will agree and understand, and be deemed to + # have agreed and understood, that there is no warranty whatsoever for + # the program and, accordingly, the entire risk arising from or + # otherwise connected with the program is assumed by the user. + # + # Therefore, neither ICOT, the copyright holder, or any other + # organization that participated in or was otherwise related to the + # development of the program and their respective officials, directors, + # officers and other employees shall be held liable for any and all + # damages, including, without limitation, general, special, incidental + # and consequential damages, arising out of or otherwise in connection + # with the use or inability to use the program or any product, material + # or result produced or otherwise obtained by using the program, + # regardless of whether they have been advised of, or otherwise had + # knowledge of, the possibility of such damages at any time during the + # project or thereafter. Each user will be deemed to have agreed to the + # foregoing by his or her commencement of use of the program. The term + # "use" as used herein includes, but is not limited to, the use, + # modification, copying and distribution of the program and the + # production of secondary products from the program. + # + # In the case where the program, whether in its original form or + # modified, was distributed or delivered to or received by a user from + # any person, organization or entity other than ICOT, unless it makes or + # grants independently of ICOT any specific warranty to the user in + # writing, such person, organization or entity, will also be exempted + # from and not be held liable to the user for any such damages as noted + # above as far as the program is concerned. + # + # ---------------COPYING.ipadic-----END---------------------------------- + +3. Lao Word Break Dictionary Data (laodict.txt) + + # Copyright (c) 2013 International Business Machines Corporation + # and others. All Rights Reserved. + # + # Project: http://code.google.com/p/lao-dictionary/ + # Dictionary: http://lao-dictionary.googlecode.com/git/Lao-Dictionary.txt + # License: http://lao-dictionary.googlecode.com/git/Lao-Dictionary-LICENSE.txt + # (copied below) + # + # This file is derived from the above dictionary, with slight + # modifications. + # ---------------------------------------------------------------------- + # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. + # All rights reserved. + # + # 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. + # + # + # 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. + # -------------------------------------------------------------------------- + +4. Burmese Word Break Dictionary Data (burmesedict.txt) + + # Copyright (c) 2014 International Business Machines Corporation + # and others. All Rights Reserved. + # + # This list is part of a project hosted at: + # github.com/kanyawtech/myanmar-karen-word-lists + # + # -------------------------------------------------------------------------- + # Copyright (c) 2013, LeRoy Benjamin Sharon + # All rights reserved. + # + # 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 Myanmar Karen Word Lists, 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 POSSIBILITY OF + # SUCH DAMAGE. + # -------------------------------------------------------------------------- + +5. Time Zone Database + + ICU uses the public domain data and code derived from Time Zone +Database for its time zone support. The ownership of the TZ database +is explained in BCP 175: Procedure for Maintaining the Time Zone +Database section 7. + + # 7. Database Ownership + # + # The TZ database itself is not an IETF Contribution or an IETF + # document. Rather it is a pre-existing and regularly updated work + # that is in the public domain, and is intended to remain in the + # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do + # not apply to the TZ Database or contributions that individuals make + # to it. Should any claims be made and substantiated against the TZ + # Database, the organization that is providing the IANA + # Considerations defined in this RFC, under the memorandum of + # understanding with the IETF, currently ICANN, may act in accordance + # with all competent court orders. No ownership claims will be made + # by ICANN or the IETF Trust on the database or the code. Any person + # making a contribution to the database or code waives all rights to + # future claims in that contribution or in the TZ Database. + +6. Google double-conversion + +Copyright 2006-2011, the V8 project authors. All rights reserved. +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. diff --git a/distribution/src/release/licenses/LICENSE-jcodings b/distribution/src/release/licenses/LICENSE-jcodings new file mode 100644 index 00000000000..f85e365de02 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-jcodings @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +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-joni b/distribution/src/release/licenses/LICENSE-joni new file mode 100644 index 00000000000..9341f05eee9 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-joni @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 JRuby Team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +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-jopt b/distribution/src/release/licenses/LICENSE-jopt new file mode 100644 index 00000000000..6b6553bc94e --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-jopt @@ -0,0 +1,22 @@ +The MIT License + + Copyright (c) 2004-2016 Paul R. Holser, Jr. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE 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 new file mode 100644 index 00000000000..75372e3d2c9 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-jsonp @@ -0,0 +1,348 @@ +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates or + contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), and + the Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing Original + Software with files containing Modifications, in each case including + portions thereof. + + 1.4. "Executable" means the Covered Software in any form other than + Source Code. + + 1.5. "Initial Developer" means the individual or entity that first + makes Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means the Source Code and Executable form of + any of the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original Software + or previous Modifications; + + B. Any new file that contains any part of the Original Software or + previous Modification; or + + C. Any new file that is contributed or otherwise made available + under the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable form + of computer software code that is originally released under this + License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.12. "Source Code" means (a) the common form of computer software + code in which modifications are made and (b) associated + documentation included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, + this License. For legal entities, "You" includes any entity which + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject + to third party intellectual property claims, the Initial Developer + hereby grants You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer, to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Software (or portions thereof), with or without Modifications, + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using or selling of + Original Software, to make, have made, use, practice, sell, and + offer for sale, and/or otherwise dispose of the Original Software + (or portions thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are effective on + the date Initial Developer first distributes or otherwise makes the + Original Software available to a third party under the terms of this + License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: (1) for code that You delete from the Original Software, or + (2) for infringements caused by: (i) the modification of the + Original Software, or (ii) the combination of the Original Software + with other software or devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject + to third party intellectual property claims, each Contributor hereby + grants You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof), either on an + unmodified basis, with other Modifications, as Covered Software + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or selling + of Modifications made by that Contributor either alone and/or in + combination with its Contributor Version (or portions of such + combination), to make, use, sell, offer for sale, have made, and/or + otherwise dispose of: (1) Modifications made by that Contributor (or + portions thereof); and (2) the combination of Modifications made by + that Contributor with its Contributor Version (or portions of such + combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective + on the date Contributor first distributes or otherwise makes the + Modifications available to a third party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: (1) for any code that Contributor has deleted from the + Contributor Version; (2) for infringements caused by: (i) third + party modifications of Contributor Version, or (ii) the combination + of Modifications made by that Contributor with other software + (except as part of the Contributor Version) or other devices; or (3) + under Patent Claims infringed by Covered Software in the absence of + Modifications made by that Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make available + in Executable form must also be made available in Source Code form + and that Source Code form must be distributed only under the terms + of this License. You must include a copy of this License with every + copy of the Source Code form of the Covered Software You distribute + or otherwise make available. You must inform recipients of any such + Covered Software in Executable form as to how they can obtain such + Covered Software in Source Code form in a reasonable manner on or + through a medium customarily used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You + believe Your Modifications are Your original creation(s) and/or You + have sufficient rights to grant the rights conveyed by this License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that + identifies You as the Contributor of the Modification. You may not + remove or alter any copyright, patent or trademark notices contained + within the Covered Software, or any notices of licensing or any + descriptive text giving attribution to any Contributor or the + Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in + Source Code form that alters or restricts the applicable version of + this License or the recipients' rights hereunder. You may choose to + offer, and to charge a fee for, warranty, support, indemnity or + liability obligations to one or more recipients of Covered Software. + However, you may do so only on Your own behalf, and not on behalf of + the Initial Developer or any Contributor. You must make it + absolutely clear that any such warranty, support, indemnity or + liability obligation is offered by You alone, and You hereby agree + to indemnify the Initial Developer and every Contributor for any + liability incurred by the Initial Developer or such Contributor as a + result of warranty, support, indemnity or liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software under + the terms of this License or under the terms of a license of Your + choice, which may contain terms different from this License, + provided that You are in compliance with the terms of this License + and that the license for the Executable form does not attempt to + limit or alter the recipient's rights in the Source Code form from + the rights set forth in this License. If You distribute the Covered + Software in Executable form under a different license, You must make + it absolutely clear that any terms which differ from this License + are offered by You alone, not by the Initial Developer or + Contributor. You hereby agree to indemnify the Initial Developer and + every Contributor for any liability incurred by the Initial + Developer or such Contributor as a result of any such terms You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with + other code not governed by the terms of this License and distribute + the Larger Work as a single product. In such a case, You must make + sure the requirements of this License are fulfilled for the Covered + Software. + +4. Versions of the License. + + 4.1. New Versions. + + Oracle is the initial license steward and may publish revised and/or + new versions of this License from time to time. Each version will be + given a distinguishing version number. Except as provided in Section + 4.3, no one other than the license steward has the right to modify + this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the + Covered Software available under the terms of the version of the + License under which You originally received the Covered Software. If + the Initial Developer includes a notice in the Original Software + prohibiting it from being distributed or otherwise made available + under any subsequent version of the License, You must distribute and + make the Covered Software available under the terms of the version + of the License under which You originally received the Covered + Software. Otherwise, You may also choose to use, distribute or + otherwise make the Covered Software available under the terms of any + subsequent version of the License published by the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new + license for Your Original Software, You may create and use a + modified version of this License if You: (a) rename the license and + remove any references to the name of the license steward (except to + note that the license differs from this License); and (b) otherwise + make it clear that the license contains terms which differ from this + License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE + IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR + NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF + THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE + DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY + OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, + REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN + ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS + AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to + cure such breach within 30 days of becoming aware of the breach. + Provisions which, by their nature, must remain in effect beyond the + termination of this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding + declaratory judgment actions) against Initial Developer or a + Contributor (the Initial Developer or Contributor against whom You + assert such claim is referred to as "Participant") alleging that the + Participant Software (meaning the Contributor Version where the + Participant is a Contributor or the Original Software where the + Participant is the Initial Developer) directly or indirectly + infringes any patent, then any and all rights granted directly or + indirectly to You by such Participant, the Initial Developer (if the + Initial Developer is not the Participant) and all Contributors under + Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice + from Participant terminate prospectively and automatically at the + expiration of such 60 day notice period, unless if within such 60 + day period You withdraw Your claim with respect to the Participant + Software against such Participant either unilaterally or pursuant to + a written agreement with Participant. + + 6.3. If You assert a patent infringement claim against Participant + alleging that the Participant Software directly or indirectly + infringes any patent where such claim is resolved (such as by + license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 6.4. In the event of termination under Sections 6.1 or 6.2 above, + all end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses + granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE + INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF + COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE + TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER + FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR + LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE + POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT + APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH + PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH + LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR + LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION + AND LIMITATION MAY NOT APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is defined + in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" (as that term is defined at 48 C.F.R. § + 252.227-7014(a)(1)) and "commercial computer software documentation" + as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent + with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 + (June 1995), all U.S. Government End Users acquire Covered Software + with only those rights set forth herein. This U.S. Government Rights + clause is in lieu of, and supersedes, any other FAR, DFAR, or other + clause or provision that addresses Government rights in computer + software under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + the law of the jurisdiction specified in a notice contained within + the Original Software (except to the extent applicable law, if any, + provides otherwise), excluding such jurisdiction's conflict-of-law + provisions. Any litigation relating to this License shall be subject + to the jurisdiction of the courts located in the jurisdiction and + venue specified in a notice contained within the Original Software, + with the losing party responsible for costs, including, without + limitation, court costs and reasonable attorneys' fees and expenses. + The application of the United Nations Convention on Contracts for + the International Sale of Goods is expressly excluded. Any law or + regulation which provides that the language of a contract shall be + construed against the drafter shall not apply to this License. You + agree that You alone are responsible for compliance with the United + States export administration regulations (and the export control + laws and regulation of any other countries) when You use, distribute + or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. diff --git a/distribution/src/release/licenses/LICENSE-jsoup b/distribution/src/release/licenses/LICENSE-jsoup new file mode 100644 index 00000000000..6ae61c02ba6 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-jsoup @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2009-2018 Jonathan Hedley + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +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-jsr311-api b/distribution/src/release/licenses/LICENSE-jsr311-api new file mode 100644 index 00000000000..b1c74f95ede --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-jsr311-api @@ -0,0 +1,759 @@ +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates or + contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), and + the Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing Original + Software with files containing Modifications, in each case including + portions thereof. + + 1.4. "Executable" means the Covered Software in any form other than + Source Code. + + 1.5. "Initial Developer" means the individual or entity that first + makes Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means the Source Code and Executable form of + any of the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original Software + or previous Modifications; + + B. Any new file that contains any part of the Original Software or + previous Modification; or + + C. Any new file that is contributed or otherwise made available + under the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable form + of computer software code that is originally released under this + License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.12. "Source Code" means (a) the common form of computer software + code in which modifications are made and (b) associated + documentation included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, + this License. For legal entities, "You" includes any entity which + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject + to third party intellectual property claims, the Initial Developer + hereby grants You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer, to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Software (or portions thereof), with or without Modifications, + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using or selling of + Original Software, to make, have made, use, practice, sell, and + offer for sale, and/or otherwise dispose of the Original Software + (or portions thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are effective on + the date Initial Developer first distributes or otherwise makes the + Original Software available to a third party under the terms of this + License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: (1) for code that You delete from the Original Software, or + (2) for infringements caused by: (i) the modification of the + Original Software, or (ii) the combination of the Original Software + with other software or devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject + to third party intellectual property claims, each Contributor hereby + grants You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof), either on an + unmodified basis, with other Modifications, as Covered Software + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or selling + of Modifications made by that Contributor either alone and/or in + combination with its Contributor Version (or portions of such + combination), to make, use, sell, offer for sale, have made, and/or + otherwise dispose of: (1) Modifications made by that Contributor (or + portions thereof); and (2) the combination of Modifications made by + that Contributor with its Contributor Version (or portions of such + combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective + on the date Contributor first distributes or otherwise makes the + Modifications available to a third party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: (1) for any code that Contributor has deleted from the + Contributor Version; (2) for infringements caused by: (i) third + party modifications of Contributor Version, or (ii) the combination + of Modifications made by that Contributor with other software + (except as part of the Contributor Version) or other devices; or (3) + under Patent Claims infringed by Covered Software in the absence of + Modifications made by that Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make available + in Executable form must also be made available in Source Code form + and that Source Code form must be distributed only under the terms + of this License. You must include a copy of this License with every + copy of the Source Code form of the Covered Software You distribute + or otherwise make available. You must inform recipients of any such + Covered Software in Executable form as to how they can obtain such + Covered Software in Source Code form in a reasonable manner on or + through a medium customarily used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You + believe Your Modifications are Your original creation(s) and/or You + have sufficient rights to grant the rights conveyed by this License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that + identifies You as the Contributor of the Modification. You may not + remove or alter any copyright, patent or trademark notices contained + within the Covered Software, or any notices of licensing or any + descriptive text giving attribution to any Contributor or the + Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in + Source Code form that alters or restricts the applicable version of + this License or the recipients' rights hereunder. You may choose to + offer, and to charge a fee for, warranty, support, indemnity or + liability obligations to one or more recipients of Covered Software. + However, you may do so only on Your own behalf, and not on behalf of + the Initial Developer or any Contributor. You must make it + absolutely clear that any such warranty, support, indemnity or + liability obligation is offered by You alone, and You hereby agree + to indemnify the Initial Developer and every Contributor for any + liability incurred by the Initial Developer or such Contributor as a + result of warranty, support, indemnity or liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software under + the terms of this License or under the terms of a license of Your + choice, which may contain terms different from this License, + provided that You are in compliance with the terms of this License + and that the license for the Executable form does not attempt to + limit or alter the recipient's rights in the Source Code form from + the rights set forth in this License. If You distribute the Covered + Software in Executable form under a different license, You must make + it absolutely clear that any terms which differ from this License + are offered by You alone, not by the Initial Developer or + Contributor. You hereby agree to indemnify the Initial Developer and + every Contributor for any liability incurred by the Initial + Developer or such Contributor as a result of any such terms You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with + other code not governed by the terms of this License and distribute + the Larger Work as a single product. In such a case, You must make + sure the requirements of this License are fulfilled for the Covered + Software. + +4. Versions of the License. + + 4.1. New Versions. + + Oracle is the initial license steward and may publish revised and/or + new versions of this License from time to time. Each version will be + given a distinguishing version number. Except as provided in Section + 4.3, no one other than the license steward has the right to modify + this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the + Covered Software available under the terms of the version of the + License under which You originally received the Covered Software. If + the Initial Developer includes a notice in the Original Software + prohibiting it from being distributed or otherwise made available + under any subsequent version of the License, You must distribute and + make the Covered Software available under the terms of the version + of the License under which You originally received the Covered + Software. Otherwise, You may also choose to use, distribute or + otherwise make the Covered Software available under the terms of any + subsequent version of the License published by the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new + license for Your Original Software, You may create and use a + modified version of this License if You: (a) rename the license and + remove any references to the name of the license steward (except to + note that the license differs from this License); and (b) otherwise + make it clear that the license contains terms which differ from this + License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE + IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR + NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF + THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE + DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY + OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, + REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN + ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS + AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to + cure such breach within 30 days of becoming aware of the breach. + Provisions which, by their nature, must remain in effect beyond the + termination of this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding + declaratory judgment actions) against Initial Developer or a + Contributor (the Initial Developer or Contributor against whom You + assert such claim is referred to as "Participant") alleging that the + Participant Software (meaning the Contributor Version where the + Participant is a Contributor or the Original Software where the + Participant is the Initial Developer) directly or indirectly + infringes any patent, then any and all rights granted directly or + indirectly to You by such Participant, the Initial Developer (if the + Initial Developer is not the Participant) and all Contributors under + Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice + from Participant terminate prospectively and automatically at the + expiration of such 60 day notice period, unless if within such 60 + day period You withdraw Your claim with respect to the Participant + Software against such Participant either unilaterally or pursuant to + a written agreement with Participant. + + 6.3. If You assert a patent infringement claim against Participant + alleging that the Participant Software directly or indirectly + infringes any patent where such claim is resolved (such as by + license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 6.4. In the event of termination under Sections 6.1 or 6.2 above, + all end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses + granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE + INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF + COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE + TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER + FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR + LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE + POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT + APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH + PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH + LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR + LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION + AND LIMITATION MAY NOT APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is defined + in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" (as that term is defined at 48 C.F.R. § + 252.227-7014(a)(1)) and "commercial computer software documentation" + as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent + with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 + (June 1995), all U.S. Government End Users acquire Covered Software + with only those rights set forth herein. This U.S. Government Rights + clause is in lieu of, and supersedes, any other FAR, DFAR, or other + clause or provision that addresses Government rights in computer + software under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + the law of the jurisdiction specified in a notice contained within + the Original Software (except to the extent applicable law, if any, + provides otherwise), excluding such jurisdiction's conflict-of-law + provisions. Any litigation relating to this License shall be subject + to the jurisdiction of the courts located in the jurisdiction and + venue specified in a notice contained within the Original Software, + with the losing party responsible for costs, including, without + limitation, court costs and reasonable attorneys' fees and expenses. + The application of the United Nations Convention on Contracts for + the International Sale of Goods is expressly excluded. Any law or + regulation which provides that the language of a contract shall be + construed against the drafter shall not apply to this License. You + agree that You alone are responsible for compliance with the United + States export administration regulations (and the export control + laws and regulation of any other countries) when You use, distribute + or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +------------------------------------------------------------------------ + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION +LICENSE (CDDL) + +The code released under the CDDL shall be governed by the laws of the +State of California (excluding conflict-of-law provisions). Any +litigation relating to this License shall be subject to the jurisdiction +of the Federal Courts of the Northern District of California and the +state courts of the State of California, with venue lying in Santa Clara +County, California. + + + + The GNU General Public License (GPL) Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor +Boston, MA 02110-1335 +USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software--to +make sure the software is free for all its users. This General Public +License applies to most of the Free Software Foundation's software and +to any other program whose authors commit to using it. (Some other Free +Software Foundation software is covered by the GNU Library General +Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this +service if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone +to deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis +or for a fee, you must give the recipients all the rights that you have. +You must make sure that they, too, receive or can get the source code. +And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must +be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a +notice placed by the copyright holder saying it may be distributed under +the terms of this General Public License. The "Program", below, refers +to any such program or work, and a "work based on the Program" means +either the Program or any derivative work under copyright law: that is +to say, a work containing the Program or a portion of it, either +verbatim or with modifications and/or translated into another language. +(Hereinafter, translation is included without limitation in the term +"modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of running +the Program is not restricted, and the output from the Program is +covered only if its contents constitute a work based on the Program +(independent of having been made by running the Program). Whether that +is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously +and appropriately publish on each copy an appropriate copyright notice +and disclaimer of warranty; keep intact all the notices that refer to +this License and to the absence of any warranty; and give any other +recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of +it, thus forming a work based on the Program, and copy and distribute +such modifications or work under the terms of Section 1 above, provided +that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any part + thereof, to be licensed as a whole at no charge to all third parties + under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this License. + (Exception: if the Program itself is interactive but does not + normally print such an announcement, your work based on the Program + is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, and +can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based on +the Program, the distribution of the whole must be on the terms of this +License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of a +storage or distribution medium does not bring the other work under the +scope of this License. + +3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your cost + of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed + only for noncommercial distribution and only if you received the + program in object code or executable form with such an offer, in + accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source code +means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to control +compilation and installation of the executable. However, as a special +exception, the source code distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies the +executable. + +If distribution of executable or object code is made by offering access +to copy from a designated place, then offering equivalent access to copy +the source code from the same place counts as distribution of the source +code, even though third parties are not compelled to copy the source +along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt otherwise +to copy, modify, sublicense or distribute the Program is void, and will +automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will +not have their licenses terminated so long as such parties remain in +full compliance. + +5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and all +its terms and conditions for copying, distributing or modifying the +Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further restrictions +on the recipients' exercise of the rights granted herein. You are not +responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot distribute +so as to satisfy simultaneously your obligations under this License and +any other pertinent obligations, then as a consequence you may not +distribute the Program at all. For example, if a patent license would +not permit royalty-free redistribution of the Program by all those who +receive copies directly or indirectly through you, then the only way you +could satisfy both it and this License would be to refrain entirely from +distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is implemented +by public license practices. Many people have made generous +contributions to the wide range of software distributed through that +system in reliance on consistent application of that system; it is up to +the author/donor to decide if he or she is willing to distribute +software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be +a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License may +add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among countries +not thus excluded. In such case, this License incorporates the +limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new +versions of the General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a version +number of this License, you may choose any version ever published by the +Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the +author to ask for permission. For software which is copyrighted by the +Free Software Foundation, write to the Free Software Foundation; we +sometimes make exceptions for this. Our decision will be guided by the +two goals of preserving the free status of all derivatives of our free +software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH +YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL +NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR +DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL +DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM +(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF +THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR +OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type + `show w'. This is free software, and you are welcome to redistribute + it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the commands +you use may be called something other than `show w' and `show c'; they +could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (which makes passes at compilers) written by + James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications +with the library. If this is what you want to do, use the GNU Library +General Public License instead of this License. + +# + +Certain source files distributed by Oracle America, Inc. and/or its +affiliates are subject to the following clarification and special +exception to the GPLv2, based on the GNU Project exception for its +Classpath libraries, known as the GNU Classpath Exception, but only +where Oracle has expressly included in the particular source file's +header the words "Oracle designates this particular file as subject to +the "Classpath" exception as provided by Oracle in the LICENSE file +that accompanied this code." + +You should also note that Oracle includes multiple, independent +programs in this software package. Some of those programs are provided +under licenses deemed incompatible with the GPLv2 by the Free Software +Foundation and others. For example, the package includes programs +licensed under the Apache License, Version 2.0. Such programs are +licensed to you under their original licenses. + +Oracle facilitates your further distribution of this package by adding +the Classpath Exception to the necessary parts of its GPLv2 code, which +permits you to use that code in combination with other independent +modules not licensed under the GPLv2. However, note that this would +not permit you to commingle code under an incompatible license with +Oracle's GPLv2 licensed code by, for example, cutting and pasting such +code into a file also containing Oracle's GPLv2 licensed code and then +distributing the result. Additionally, if you were to remove the +Classpath Exception from any of the files to which it applies and +distribute the result, you would likely be required to license some or +all of the other code in that distribution under the GPLv2 as well, and +since the GPLv2 is incompatible with the license terms of some items +included in the distribution by Oracle, removing the Classpath +Exception could therefore effectively compromise your ability to +further distribute the package. + +Proceed with caution and we recommend that you obtain the advice of a +lawyer skilled in open source matters before removing the Classpath +Exception or making modifications to this package which may +subsequently be redistributed and/or involve the use of third party +software. + +CLASSPATH EXCEPTION +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License version 2 cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from or +based on this library. If you modify this library, you may extend this +exception to your version of the library, but you are not obligated to +do so. If you do not wish to do so, delete this exception statement +from your version. diff --git a/distribution/src/release/licenses/LICENSE-logback b/distribution/src/release/licenses/LICENSE-logback new file mode 100644 index 00000000000..1f99506bfa4 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-logback @@ -0,0 +1,14 @@ +Logback LICENSE +--------------- + +Logback: the reliable, generic, fast and flexible logging framework. +Copyright (C) 1999-2015, QOS.ch. All rights reserved. + +This program and the accompanying materials are dual-licensed under +either the terms of the Eclipse Public License v1.0 as published by +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. diff --git a/distribution/src/release/licenses/LICENSE-mozilla-v20 b/distribution/src/release/licenses/LICENSE-mozilla-v20 new file mode 100644 index 00000000000..b3b8d93f36c --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-mozilla-v20 @@ -0,0 +1,151 @@ +Mozilla Public License +Version 2.0 +1. Definitions +1.1. “Contributor” +means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. + +1.2. “Contributor Version” +means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” +means Covered Software of a particular Contributor. + +1.4. “Covered Software” +means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. + +1.5. “Incompatible With Secondary Licenses” +means + +that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or + +that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. + +1.6. “Executable Form” +means any form of the work other than Source Code Form. + +1.7. “Larger Work” +means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. + +1.8. “License” +means this document. + +1.9. “Licensable” +means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. + +1.10. “Modifications” +means any of the following: + +any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or + +any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor +means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. + +1.12. “Secondary License” +means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” +means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) +means an individual or a legal entity exercising rights under this License. For legal entities, “You” includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, “control” means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. + +2. License Grants and Conditions +2.1. Grants +Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: + +under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and + +under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. + +2.2. Effective Date +The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. + +2.3. Limitations on Grant Scope +The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: + +for any code that a Contributor has removed from Covered Software; or + +for infringements caused by: (i) Your and any other third party’s modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or + +under Patent Claims infringed by Covered Software in the absence of its Contributions. + +This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). + +2.4. Subsequent Licenses +No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). + +2.5. Representation +Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use +This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. + +3. Responsibilities +3.1. Distribution of Source Form +All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form +If You distribute Covered Software in Executable Form then: + +such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and + +You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients’ rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work +You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). + +3.4. Notices +You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms +You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. + +5. Termination +5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. + +6. Disclaimer of Warranty +Covered Software is provided under this License on an “as is” basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer. + +7. Limitation of Liability +Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party’s negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation +Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous +This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. + +10. Versions of the License +10.1. New Versions +Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. + +10.2. Effect of New Versions +You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. + +10.3. Modified Versions +If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses +If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice +This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0. diff --git a/distribution/src/release/licenses/LICENSE-protobuf b/distribution/src/release/licenses/LICENSE-protobuf new file mode 100644 index 00000000000..19b305b0006 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-protobuf @@ -0,0 +1,32 @@ +Copyright 2008 Google Inc. All rights reserved. + +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. + +Code generated by the Protocol Buffer compiler is owned by the owner +of the input file used when generating it. This code is not +standalone and requires a support library to be linked with it. This +support library is itself covered by the above license. diff --git a/distribution/src/release/licenses/LICENSE-slf4j b/distribution/src/release/licenses/LICENSE-slf4j new file mode 100644 index 00000000000..744377c4372 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-slf4j @@ -0,0 +1,21 @@ +Copyright (c) 2004-2017 QOS.ch +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE 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-woodstox-stax2-api b/distribution/src/release/licenses/LICENSE-woodstox-stax2-api new file mode 100644 index 00000000000..7c44536f547 --- /dev/null +++ b/distribution/src/release/licenses/LICENSE-woodstox-stax2-api @@ -0,0 +1,13 @@ +This copy of Stax2 API is licensed under the +Simplified BSF License (also known as "2-clause BSD", or "FreeBSD License") +See the License for details about distribution rights, and the +specific rights regarding derivate works. + +You may obtain a copy of the License at: + +http://www.opensource.org/licenses/bsd-license.php + +with details of: + + = FasterXML.com + = 2010- diff --git a/distribution/src/release/licenses/NOTICE-apache-commons-codec b/distribution/src/release/licenses/NOTICE-apache-commons-codec new file mode 100644 index 00000000000..950f3dbe814 --- /dev/null +++ b/distribution/src/release/licenses/NOTICE-apache-commons-codec @@ -0,0 +1,17 @@ +Apache Commons Codec +Copyright 2002-2017 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java +contains test data from http://aspell.net/test/orig/batch0.tab. +Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org) + +=============================================================================== + +The content of package org.apache.commons.codec.language.bm has been translated +from the original php source code available at http://stevemorse.org/phoneticinfo.htm +with permission from the original authors. +Original source copyright: +Copyright (c) 2008 Alexander Beider & Stephen P. Morse. diff --git a/distribution/src/release/licenses/NOTICE-netty b/distribution/src/release/licenses/NOTICE-netty new file mode 100644 index 00000000000..8e31ccc6f97 --- /dev/null +++ b/distribution/src/release/licenses/NOTICE-netty @@ -0,0 +1,222 @@ + The Netty Project + ================= + +Please visit the Netty web site for more information: + + * http://netty.io/ + +Copyright 2014 The Netty Project + +The Netty Project licenses this file to you under the Apache License, +version 2.0 (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. + +Also, please refer to each LICENSE..txt file, which is located in +the 'license' directory of the distribution file, for the license terms of the +components that this product depends on. + +------------------------------------------------------------------------------- +This product contains the extensions to Java Collections Framework which has +been derived from the works by JSR-166 EG, Doug Lea, and Jason T. Greene: + + * LICENSE: + * license/LICENSE.jsr166y.txt (Public Domain) + * HOMEPAGE: + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/ + * http://viewvc.jboss.org/cgi-bin/viewvc.cgi/jbosscache/experimental/jsr166/ + +This product contains a modified version of Robert Harder's Public Domain +Base64 Encoder and Decoder, which can be obtained at: + + * LICENSE: + * license/LICENSE.base64.txt (Public Domain) + * HOMEPAGE: + * http://iharder.sourceforge.net/current/java/base64/ + +This product contains a modified portion of 'Webbit', an event based +WebSocket and HTTP server, which can be obtained at: + + * LICENSE: + * license/LICENSE.webbit.txt (BSD License) + * HOMEPAGE: + * https://github.com/joewalnes/webbit + +This product contains a modified portion of 'SLF4J', a simple logging +facade for Java, which can be obtained at: + + * LICENSE: + * license/LICENSE.slf4j.txt (MIT License) + * HOMEPAGE: + * http://www.slf4j.org/ + +This product contains a modified portion of 'Apache Harmony', an open source +Java SE, which can be obtained at: + + * NOTICE: + * license/NOTICE.harmony.txt + * LICENSE: + * license/LICENSE.harmony.txt (Apache License 2.0) + * HOMEPAGE: + * http://archive.apache.org/dist/harmony/ + +This product contains a modified portion of 'jbzip2', a Java bzip2 compression +and decompression library written by Matthew J. Francis. It can be obtained at: + + * LICENSE: + * license/LICENSE.jbzip2.txt (MIT License) + * HOMEPAGE: + * https://code.google.com/p/jbzip2/ + +This product contains a modified portion of 'libdivsufsort', a C API library to construct +the suffix array and the Burrows-Wheeler transformed string for any input string of +a constant-size alphabet written by Yuta Mori. It can be obtained at: + + * LICENSE: + * license/LICENSE.libdivsufsort.txt (MIT License) + * HOMEPAGE: + * https://github.com/y-256/libdivsufsort + +This product contains a modified portion of Nitsan Wakart's 'JCTools', Java Concurrency Tools for the JVM, + which can be obtained at: + + * LICENSE: + * license/LICENSE.jctools.txt (ASL2 License) + * HOMEPAGE: + * https://github.com/JCTools/JCTools + +This product optionally depends on 'JZlib', a re-implementation of zlib in +pure Java, which can be obtained at: + + * LICENSE: + * license/LICENSE.jzlib.txt (BSD style License) + * HOMEPAGE: + * http://www.jcraft.com/jzlib/ + +This product optionally depends on 'Compress-LZF', a Java library for encoding and +decoding data in LZF format, written by Tatu Saloranta. It can be obtained at: + + * LICENSE: + * license/LICENSE.compress-lzf.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/ning/compress + +This product optionally depends on 'lz4', a LZ4 Java compression +and decompression library written by Adrien Grand. It can be obtained at: + + * LICENSE: + * license/LICENSE.lz4.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/jpountz/lz4-java + +This product optionally depends on 'lzma-java', a LZMA Java compression +and decompression library, which can be obtained at: + + * LICENSE: + * license/LICENSE.lzma-java.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/jponge/lzma-java + +This product contains a modified portion of 'jfastlz', a Java port of FastLZ compression +and decompression library written by William Kinney. It can be obtained at: + + * LICENSE: + * license/LICENSE.jfastlz.txt (MIT License) + * HOMEPAGE: + * https://code.google.com/p/jfastlz/ + +This product contains a modified portion of and optionally depends on 'Protocol Buffers', Google's data +interchange format, which can be obtained at: + + * LICENSE: + * license/LICENSE.protobuf.txt (New BSD License) + * HOMEPAGE: + * https://github.com/google/protobuf + +This product optionally depends on 'Bouncy Castle Crypto APIs' to generate +a temporary self-signed X.509 certificate when the JVM does not provide the +equivalent functionality. It can be obtained at: + + * LICENSE: + * license/LICENSE.bouncycastle.txt (MIT License) + * HOMEPAGE: + * http://www.bouncycastle.org/ + +This product optionally depends on 'Snappy', a compression library produced +by Google Inc, which can be obtained at: + + * LICENSE: + * license/LICENSE.snappy.txt (New BSD License) + * HOMEPAGE: + * https://github.com/google/snappy + +This product optionally depends on 'JBoss Marshalling', an alternative Java +serialization API, which can be obtained at: + + * LICENSE: + * license/LICENSE.jboss-marshalling.txt (GNU LGPL 2.1) + * HOMEPAGE: + * http://www.jboss.org/jbossmarshalling + +This product optionally depends on 'Caliper', Google's micro- +benchmarking framework, which can be obtained at: + + * LICENSE: + * license/LICENSE.caliper.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/google/caliper + +This product optionally depends on 'Apache Commons Logging', a logging +framework, which can be obtained at: + + * LICENSE: + * license/LICENSE.commons-logging.txt (Apache License 2.0) + * HOMEPAGE: + * http://commons.apache.org/logging/ + +This product optionally depends on 'Apache Log4J', a logging framework, which +can be obtained at: + + * LICENSE: + * license/LICENSE.log4j.txt (Apache License 2.0) + * HOMEPAGE: + * http://logging.apache.org/log4j/ + +This product optionally depends on 'Aalto XML', an ultra-high performance +non-blocking XML processor, which can be obtained at: + + * LICENSE: + * license/LICENSE.aalto-xml.txt (Apache License 2.0) + * HOMEPAGE: + * http://wiki.fasterxml.com/AaltoHome + +This product contains a modified version of 'HPACK', a Java implementation of +the HTTP/2 HPACK algorithm written by Twitter. It can be obtained at: + + * LICENSE: + * license/LICENSE.hpack.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/twitter/hpack + +This product contains a modified portion of 'Apache Commons Lang', a Java library +provides utilities for the java.lang API, which can be obtained at: + + * LICENSE: + * license/LICENSE.commons-lang.txt (Apache License 2.0) + * HOMEPAGE: + * https://commons.apache.org/proper/commons-lang/ + + +This product contains the Maven wrapper scripts from 'Maven Wrapper', that provides an easy way to ensure a user has everything necessary to run the Maven build. + + * LICENSE: + * license/LICENSE.mvn-wrapper.txt (Apache License 2.0) + * HOMEPAGE: + * https://github.com/takari/maven-wrapper diff --git a/distribution/src/release/licenses/NOTICE-prometheus b/distribution/src/release/licenses/NOTICE-prometheus new file mode 100644 index 00000000000..c920ec3fe34 --- /dev/null +++ b/distribution/src/release/licenses/NOTICE-prometheus @@ -0,0 +1,11 @@ +Prometheus instrumentation library for JVM applications +Copyright 2012-2015 The Prometheus Authors + +This product includes software developed at +Boxever Ltd. (http://www.boxever.com/). + +This product includes software developed at +SoundCloud Ltd. (http://soundcloud.com/). + +This product includes software developed as part of the +Ocelli project by Netflix Inc. (https://github.com/Netflix/ocelli/). diff --git a/distribution/src/release/licenses/NOTICE-protostuff b/distribution/src/release/licenses/NOTICE-protostuff new file mode 100644 index 00000000000..1f95779a7fb --- /dev/null +++ b/distribution/src/release/licenses/NOTICE-protostuff @@ -0,0 +1,27 @@ +============================================================== + protostuff + Copyright 2009 David Yu dyuproject@gmail.com +============================================================== + +protobuf is copyright Google inc unless otherwise noted. +It is licensed under the BSD license. + +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. +It is licensed under the BSD license. + +stringtemplate is copyright Terence Parr unless otherwise noted. +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. +It is licensed under the apache 2.0 license. + +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. +It is licensed under the apache 2.0 license. diff --git a/distribution/src/release/licenses/NOTICE-tomcat b/distribution/src/release/licenses/NOTICE-tomcat new file mode 100644 index 00000000000..d359afad345 --- /dev/null +++ b/distribution/src/release/licenses/NOTICE-tomcat @@ -0,0 +1,55 @@ +Apache Tomcat +Copyright 1999-2018 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +This software contains code derived from netty-native +developed by the Netty project +(http://netty.io, https://github.com/netty/netty-tcnative/) +and from finagle-native developed at Twitter +(https://github.com/twitter/finagle). + +The Windows Installer is built with the Nullsoft +Scriptable Install System (NSIS), which is +open source software. The original software and +related information is available at +http://nsis.sourceforge.net. + +Java compilation software for JSP pages is provided by the Eclipse +JDT Core Batch Compiler component, which is open source software. +The original software and related information is available at +http://www.eclipse.org/jdt/core/. + +For portions of the Tomcat JNI OpenSSL API and the OpenSSL JSSE integration +The org.apache.tomcat.jni and the org.apache.tomcat.net.openssl packages +are derivative work originating from the Netty project and the finagle-native +project developed at Twitter +* Copyright 2014 The Netty Project +* Copyright 2014 Twitter + +The original XML Schemas for Java EE Deployment Descriptors: + - javaee_5.xsd + - javaee_web_services_1_2.xsd + - javaee_web_services_client_1_2.xsd + - javaee_6.xsd + - javaee_web_services_1_3.xsd + - javaee_web_services_client_1_3.xsd + - jsp_2_2.xsd + - web-app_3_0.xsd + - web-common_3_0.xsd + - web-fragment_3_0.xsd + - javaee_7.xsd + - javaee_web_services_1_4.xsd + - javaee_web_services_client_1_4.xsd + - jsp_2_3.xsd + - web-app_3_1.xsd + - web-common_3_1.xsd + - web-fragment_3_1.xsd + - javaee_8.xsd + - web-app_4_0.xsd + - web-common_4_0.xsd + - web-fragment_4_0.xsd + +may be obtained from: +http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/javaee/index.html diff --git a/dynamic-config/config-apollo/pom.xml b/dynamic-config/config-apollo/pom.xml new file mode 100644 index 00000000000..7cc03d182dd --- /dev/null +++ b/dynamic-config/config-apollo/pom.xml @@ -0,0 +1,44 @@ + + + + + + + dynamic-config + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + config-apollo + Java Chassis::Dynamic Config::Apollo + + + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-vertx + + + org.springframework + spring-web + + + diff --git a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloClient.java b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloClient.java new file mode 100644 index 00000000000..61c6a9c5c7d --- /dev/null +++ b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloClient.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.config.apollo.ApolloDynamicPropertiesSource.UpdateHandler; +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.web.client.RestTemplate; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.annotations.VisibleForTesting; + +import io.netty.handler.codec.http.HttpResponseStatus; + +public class ApolloClient { + + private static final Logger LOGGER = LoggerFactory.getLogger(ApolloClient.class); + + private static final Map originalConfigMap = new ConcurrentHashMap<>(); + + private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(1); + + private final ApolloConfig apolloConfig; + + private final UpdateHandler updateHandler; + + private static RestTemplate rest = new RestTemplate(); + + public ApolloClient(UpdateHandler updateHandler, ApolloConfig apolloConfig) { + this.updateHandler = updateHandler; + this.apolloConfig = apolloConfig; + } + + @VisibleForTesting + static Map getOriginalConfigMap() { + return originalConfigMap; + } + + @VisibleForTesting + static void setRest(RestTemplate rest) { + ApolloClient.rest = rest; + } + + public void refreshApolloConfig() { + EXECUTOR.scheduleWithFixedDelay(new ConfigRefresh(apolloConfig.getServerUri()), + apolloConfig.getFirstRefreshInterval(), + apolloConfig.getRefreshInterval(), TimeUnit.SECONDS); + } + + class ConfigRefresh implements Runnable { + private final String serviceUri; + + ConfigRefresh(String serviceUris) { + this.serviceUri = serviceUris; + } + + @Override + public void run() { + try { + refreshConfig(); + } catch (Throwable e) { + LOGGER.error("client refresh thread exception ", e); + } + } + + @SuppressWarnings("unchecked") + void refreshConfig() { + HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Type", "application/json;charset=UTF-8"); + 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()) { + try { + Map body = JsonUtils.OBJ_MAPPER.readValue(exchange.getBody(), + new TypeReference>() { + }); + refreshConfigItems((Map) body.get("configurations")); + } catch (IOException e) { + LOGGER.error("JsonObject parse config center response error: ", e); + } + } else { + LOGGER.error("fetch configuration failed, error code:{} for {}", + exchange.getStatusCode().value(), + exchange.getBody()); + } + } + + private String composeAPI() { + String api = serviceUri + "/openapi/v1/envs/" + apolloConfig.getEnv() + + "/apps/" + apolloConfig.getServiceName() + + "/clusters/" + apolloConfig.getServerClusters() + + "/namespaces/" + apolloConfig.getNamespace() + + "/releases/latest"; + return api; + } + + 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-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/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/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/resources/microservice.yaml b/dynamic-config/config-apollo/src/test/resources/microservice.yaml new file mode 100644 index 00000000000..4932794e5d7 --- /dev/null +++ b/dynamic-config/config-apollo/src/test/resources/microservice.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. +## --------------------------------------------------------------------------- + +APPLICATION_ID: apollotest +service_description: + name: apollo-test + version: 1.0.1 + +apollo: + config: + serverUri: http://127.0.0.1:8070 + serviceName: apollo-test + env: DEV + clusters: test-cluster + namespace: application + token: xxx + refreshInterval: 30 + firstRefreshInterval: 0 + +servicecomb: + service: + registry: + address: http://127.0.0.1:30100 + rest: + address: 0.0.0.0:8080 + highway: + address: 0.0.0.0:7070 diff --git a/dynamic-config/config-cc/pom.xml b/dynamic-config/config-cc/pom.xml new file mode 100644 index 00000000000..0e8f6addcc6 --- /dev/null +++ b/dynamic-config/config-cc/pom.xml @@ -0,0 +1,64 @@ + + + + + dynamic-config + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + config-cc + Java Chassis::Foundations::Config CC + + + + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-ssl + + + org.apache.servicecomb + foundation-vertx + + + io.vertx + vertx-codegen + provided + + + org.apache.servicecomb + java-chassis-core + + + org.apache.servicecomb + foundation-test-scaffolding + test + + + org.apache.servicecomb + config-center-client + + + + 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/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.core.bootup.BootUpInformationCollector b/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector new file mode 100644 index 00000000000..9b7c91cf3d9 --- /dev/null +++ b/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector @@ -0,0 +1,17 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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.ConfigCenterInformationCollector 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/cc/ConfigCenterAddressManagerTest.java b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterAddressManagerTest.java new file mode 100644 index 00000000000..50d39bc19db --- /dev/null +++ b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterAddressManagerTest.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.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; + +import com.google.common.eventbus.EventBus; + +class ConfigCenterAddressManagerTest { + private static final List addresses = new ArrayList<>(); + + private static ConfigCenterAddressManager addressManager1; + + private static ConfigCenterAddressManager addressManager2; + + @Test + 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(), "", ""); + 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); + + List addresses = addressManager1.getAddresses(); + Assertions.assertEquals(2, addresses.size()); + Assertions.assertEquals("http://127.0.0.1:30103/v3/project", addresses.get(0)); + + Assertions.assertEquals("https://127.0.0.2:30103/v3/project", addressManager1.address()); + Assertions.assertEquals("http://127.0.0.1:30103/v3/project", addressManager1.address()); + Assertions.assertEquals("https://127.0.0.2:30103/v3/default", addressManager2.address()); + } + + @Test + public void onRefreshEndpointEvent() { + List addressAZ = new ArrayList<>(); + addressAZ.add("http://127.0.0.3:30100"); + List addressRG = new ArrayList<>(); + addressRG.add("http://127.0.0.4:30100"); + Map> zoneAndRegion = new HashMap<>(); + zoneAndRegion.put("sameZone", addressAZ); + zoneAndRegion.put("sameRegion", addressRG); + addressManager1 = new ConfigCenterAddressManager("project", addresses, new EventBus(), "", ""); + RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseConfigCenter"); + addressManager1.refreshEndpoint(event, "CseConfigCenter"); + + List availableZone = addressManager1.getAvailableZone(); + Assertions.assertEquals("http://127.0.0.3:30100/v3/project", availableZone.get(0)); + + List availableRegion = addressManager1.getAvailableRegion(); + Assertions.assertEquals("http://127.0.0.4:30100/v3/project", availableRegion.get(0)); + + Assertions.assertEquals("http://127.0.0.3:30100/v3/project", addressManager1.address()); + } +} diff --git a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterConfigurationSourceImplTest.java b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterConfigurationSourceImplTest.java new file mode 100644 index 00000000000..3298d95bb19 --- /dev/null +++ b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterConfigurationSourceImplTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.foundation.common.event.EventManager; +import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class ConfigCenterConfigurationSourceImplTest { + @Test + 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(), "", ""); + Field addressManagerField = addressManager.getClass().getSuperclass().getDeclaredField("index"); + addressManagerField.setAccessible(true); + addressManagerField.set(addressManager, 0); + Assertions.assertNotNull(addressManager); + + String address = addressManager.address(); + Assertions.assertEquals("http://127.0.0.2:30103/v3/test", address); + address = addressManager.address(); + Assertions.assertEquals("http://127.0.0.1:30103/v3/test", address); + + 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); + } + + @Test + void onRefreshEndpointEventTest() { + List addresses = new ArrayList<>(); + addresses.add("http://127.0.0.1:30103"); + List addressAZ = new ArrayList<>(); + addressAZ.add("rest://127.0.0.1:30100?sslEnabled=true"); + Map> zoneAndRegion = new HashMap<>(); + zoneAndRegion.put("sameZone", addressAZ); + zoneAndRegion.put("sameRegion", new ArrayList<>()); + RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseConfigCenter"); + ConfigCenterAddressManager addressManager = new ConfigCenterAddressManager("test", addresses, + EventManager.getEventBus(), "", ""); + addressManager.onRefreshEndpointEvent(event); + + List availableAZ = addressManager.getAvailableZone(); + Assertions.assertEquals("https://127.0.0.1:30100/v3/test", availableAZ.get(0)); + } +} diff --git a/dynamic-config/config-cc/src/test/resources/microservice.yaml b/dynamic-config/config-cc/src/test/resources/microservice.yaml new file mode 100644 index 00000000000..e0d5d7dab52 --- /dev/null +++ b/dynamic-config/config-cc/src/test/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +host.name: 172.16.8.7 +trace: + handler: + enabled: false + sampler: + percent: 0.5 + metric: + service: + enable: false +validate: + parameter: + enabled: true + returnValue: + enabled: true + apiInvoke: + enabled: true +shutDownHandler: + enabled: true + timeLimit: 30000 +eureka: + instance: + preferIpAddress: true + leaseRenewalIntervalInSeconds: 3 + leaseExpirationDurationInSeconds: 5 + client: + serviceUrl: + defaultZone: http://172.16.8.8:30100/ +servicecomb: + config: + client: + serviceName: testDemo + serverUri: https://172.16.8.7:30103,https://${host.name}:30103 + tenantName: csetest + refreshMode: 1 + refresh_interval: 10000 + service: + registry: + autodiscovery: true +service_description: + name: testDemo + environment: testing 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 new file mode 100644 index 00000000000..4bf61b5801f --- /dev/null +++ b/dynamic-config/config-kie/pom.xml @@ -0,0 +1,65 @@ + + + + + + + dynamic-config + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + config-kie + Java Chassis::Dynamic Config::Kie + + + + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-ssl + + + org.apache.servicecomb + foundation-vertx + + + io.vertx + vertx-codegen + provided + + + org.apache.servicecomb + java-chassis-core + + + org.apache.servicecomb + foundation-test-scaffolding + test + + + org.apache.servicecomb + config-kie-client + + + 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 new file mode 100644 index 00000000000..79e40786f8f --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfig.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Collections; +import java.util.List; + +import org.apache.servicecomb.foundation.vertx.VertxConst; +import org.springframework.core.env.Environment; + +public class KieConfig { + public static final String SSL_TAG = "kie.consumer"; + + private static final String SERVER_URL_KEY = "servicecomb.kie.serverUri"; + + private static final String REFRESH_INTERVAL = "servicecomb.kie.refreshInterval"; + + private static final String FIRST_REFRESH_INTERVAL = "servicecomb.kie.firstRefreshInterval"; + + private static final String DOMAIN_NAME = "servicecomb.kie.domainName"; + + private static final String ENABLE_LONG_POLLING = "servicecomb.kie.enableLongPolling"; + + private static final String POLLING_WAIT_TIME = "servicecomb.kie.pollingWaitTime"; + + private static final String FIRST_PULL_REQUIRED = "servicecomb.kie.firstPullRequired"; + + private static final String CUSTOM_LABEL = "servicecomb.kie.customLabel"; + + private static final String CUSTOM_LABEL_VALUE = "servicecomb.kie.customLabelValue"; + + private static final String ENABLE_APP_CONFIG = "servicecomb.kie.enableAppConfig"; + + private static final String ENABLE_SERVICE_CONFIG = "servicecomb.kie.enableServiceConfig"; + + private static final String ENABLE_VERSION_CONFIG = "servicecomb.kie.enableVersionConfig"; + + private static final String ENABLE_CUSTOM_CONFIG = "servicecomb.kie.enableCustomConfig"; + + public static final String FILE_SOURCE = "servicecomb.config.client.fileSource"; + + private static final int DEFAULT_REFRESH_INTERVAL = 15000; + + private static final int DEFAULT_POLLING_WAIT_TIME = 10; + + private static final int DEFAULT_FIRST_REFRESH_INTERVAL = 0; + + private static final boolean DEFAULT_ENABLE_LONG_POLLING = true; + + private static final String CUSTOM_LABEL_DEFAULT = "public"; + + private static final String CUSTOM_LABEL_VALUE_DEFAULT = ""; + + private static final String CLIENT_CONNECT_TIMEOUT = "servicecomb.kie.client.timeout.connect"; + + private static final String CLIENT_REQUEST_TIMEOUT = "servicecomb.kie.client.timeout.request"; + + private static final String CLIENT_SOCKET_TIMEOUT = "servicecomb.kie.client.timeout.socket"; + + private final Environment environment; + + public KieConfig(Environment environment) { + this.environment = environment; + } + + @SuppressWarnings("unchecked") + public List getFileSources() { + return environment.getProperty(FILE_SOURCE, List.class, Collections.emptyList()); + } + + public String getDomainName() { + return environment.getProperty(DOMAIN_NAME, "default"); + } + + public String getServerUri() { + return environment.getProperty(SERVER_URL_KEY); + } + + 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); + } + + public boolean enableAppConfig() { + return environment.getProperty(ENABLE_APP_CONFIG, boolean.class, true); + } + + public boolean enableServiceConfig() { + return environment.getProperty(ENABLE_SERVICE_CONFIG, boolean.class, true); + } + + public boolean enableVersionConfig() { + return environment.getProperty(ENABLE_VERSION_CONFIG, boolean.class, true); + } + + public boolean enableCustomConfig() { + return environment.getProperty(ENABLE_CUSTOM_CONFIG, boolean.class, true); + } + + public boolean enableLongPolling() { + return environment.getProperty(ENABLE_LONG_POLLING, boolean.class, DEFAULT_ENABLE_LONG_POLLING); + } + + public int getPollingWaitTime() { + return environment.getProperty(POLLING_WAIT_TIME, int.class, DEFAULT_POLLING_WAIT_TIME); + } + + public boolean firstPullRequired() { + return environment.getProperty(FIRST_PULL_REQUIRED, boolean.class, false); + } + + public String getCustomLabel() { + return environment.getProperty(CUSTOM_LABEL, CUSTOM_LABEL_DEFAULT); + } + + public String getCustomLabelValue() { + return environment.getProperty(CUSTOM_LABEL_VALUE, CUSTOM_LABEL_VALUE_DEFAULT); + } + + 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 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/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 new file mode 100644 index 00000000000..8be7ec00c1a --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/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.kie; + +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-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 new file mode 100644 index 00000000000..d689aaec2e1 --- /dev/null +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/collect/KieClientInformationCollector.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.kie.collect; + +import org.apache.servicecomb.config.kie.KieConfig; +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 KieClientInformationCollector implements BootUpInformationCollector, EnvironmentAware { + private KieConfig kieConfig; + + @Override + public void setEnvironment(@NonNull Environment environment) { + this.kieConfig = new KieConfig(environment); + } + + @Override + public String collect() { + return "Kie Center: " + kieConfig.getServerUri(); + } + + @Override + public int getOrder() { + return 2; + } +} 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.core.bootup.BootUpInformationCollector b/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector new file mode 100644 index 00000000000..4ff2984a1b2 --- /dev/null +++ b/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector @@ -0,0 +1,17 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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.KieClientInformationCollector 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/resources/microservice.yaml b/dynamic-config/config-kie/src/test/resources/microservice.yaml new file mode 100644 index 00000000000..4928e22a60b --- /dev/null +++ b/dynamic-config/config-kie/src/test/resources/microservice.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. +## --------------------------------------------------------------------------- + +host.name: 172.16.8.7 +trace: + handler: + enabled: false + sampler: + percent: 0.5 + metric: + service: + enable: false +validate: + parameter: + enabled: true + returnValue: + enabled: true + apiInvoke: + enabled: true +shutDownHandler: + enabled: true + timeLimit: 30000 +eureka: + instance: + preferIpAddress: true + leaseRenewalIntervalInSeconds: 3 + leaseExpirationDurationInSeconds: 5 + client: + serviceUrl: + defaultZone: http://172.16.8.8:30100/ +servicecomb: + kie: + serverUri: https://172.16.8.7:30110 + service: + registry: + autodiscovery: true + refreshInterval: 3000 + firstRefreshInterval: 0 + +service_description: + name: testDemo + environment: testing diff --git a/dynamic-config/config-nacos/README.md b/dynamic-config/config-nacos/README.md new file mode 100644 index 00000000000..e09d8db761d --- /dev/null +++ b/dynamic-config/config-nacos/README.md @@ -0,0 +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. diff --git a/dynamic-config/config-nacos/pom.xml b/dynamic-config/config-nacos/pom.xml new file mode 100644 index 00000000000..395603f9fd6 --- /dev/null +++ b/dynamic-config/config-nacos/pom.xml @@ -0,0 +1,45 @@ + + + + + + + dynamic-config + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + config-nacos + Java Chassis::Dynamic Config::Nacos + + + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-vertx + + + + com.alibaba.nacos + nacos-client + + + 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/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/test/resources/microservice.yaml b/dynamic-config/config-nacos/src/test/resources/microservice.yaml new file mode 100644 index 00000000000..1b9666a84d4 --- /dev/null +++ b/dynamic-config/config-nacos/src/test/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +APPLICATION_ID: nacostest +service_description: + name: nacos-test + version: 1.0.1 + +servicecomb: + service: + registry: + address: http://127.0.0.1:30100 + nacos: + serverAddr: 127.0.0.1:8848 + dataId: example + group: DEFAULT_GROUP + rest: + address: 0.0.0.0:8080 + highway: + address: 0.0.0.0:7070 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 new file mode 100644 index 00000000000..d5777cd654c --- /dev/null +++ b/dynamic-config/pom.xml @@ -0,0 +1,44 @@ + + + + + + + org.apache.servicecomb + java-chassis-parent + 3.4.0-SNAPSHOT + ../parents/default + + 4.0.0 + + dynamic-config + Java Chassis::Dynamic Config + pom + + config-apollo + 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 new file mode 100644 index 00000000000..0a75f8e33c6 --- /dev/null +++ b/edge/edge-core/pom.xml @@ -0,0 +1,70 @@ + + + + + 4.0.0 + + org.apache.servicecomb + edge + 3.4.0-SNAPSHOT + + edge-core + Java Chassis::Edge::Core + + + org.apache.servicecomb + transport-rest-vertx + + + org.apache.servicecomb + handler-loadbalance + + + 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 + test + + + org.apache.servicecomb + swagger-generator-jaxrs + test + + + org.mockito + mockito-inline + test + + + 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 new file mode 100644 index 00000000000..c29c066b800 --- /dev/null +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/AbstractEdgeDispatcher.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.edge.core; + +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; +import org.apache.servicecomb.transport.rest.vertx.AbstractVertxHttpDispatcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.RoutingContext; + +public abstract class AbstractEdgeDispatcher extends AbstractVertxHttpDispatcher { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractEdgeDispatcher.class); + + protected void onFailure(RoutingContext context) { + LOGGER.error("edge server failed.", context.failure()); + HttpServerResponse response = context.response(); + if (response.closed() || response.ended()) { + return; + } + + if (context.failure() instanceof InvocationException) { + InvocationException exception = (InvocationException) context.failure(); + response.setStatusCode(exception.getStatusCode()); + response.setStatusMessage(exception.getReasonPhrase()); + if (null == exception.getErrorData()) { + response.end(); + return; + } + + String responseBody; + try { + responseBody = RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(exception.getErrorData()); + response.putHeader("Content-Type", MediaType.APPLICATION_JSON); + } catch (JsonProcessingException e) { + responseBody = exception.getErrorData().toString(); + response.putHeader("Content-Type", MediaType.TEXT_PLAIN); + } + response.end(responseBody); + } else { + response.setStatusCode(Status.BAD_GATEWAY.getStatusCode()); + response.setStatusMessage(Status.BAD_GATEWAY.getReasonPhrase()); + response.end(); + } + } +} 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 new file mode 100644 index 00000000000..0f7e59bb0d2 --- /dev/null +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CommonHttpEdgeDispatcher.java @@ -0,0 +1,249 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; +import org.apache.servicecomb.loadbalance.ExtensionsManager; +import org.apache.servicecomb.loadbalance.LoadBalanceFilter; +import org.apache.servicecomb.loadbalance.LoadBalancer; +import org.apache.servicecomb.loadbalance.RuleExt; +import org.apache.servicecomb.loadbalance.ServiceCombServer; +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.google.common.eventbus.Subscribe; + +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.RequestOptions; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; + +/** + * This dispatcher forwards requests to any http servers, includes java-chassis providers and other, + * provided the server is registered to service center. + * + * This dispatcher using loadbalance handler to choose the target server. So any functions + * provided by loadbalancer handler is available, excluding retrying. + */ +public class CommonHttpEdgeDispatcher extends AbstractEdgeDispatcher { + private static final Logger LOG = LoggerFactory.getLogger(CommonHttpEdgeDispatcher.class); + + private static final String KEY_ENABLED = "servicecomb.http.dispatcher.edge.http.enabled"; + + private static final String KEY_ORDER = "servicecomb.http.dispatcher.edge.http.order"; + + private static final String KEY_PATTERN = "servicecomb.http.dispatcher.edge.http.pattern"; + + private static final String PATTERN_ANY = "/(.*)"; + + private static final String KEY_MAPPING_PREFIX = "servicecomb.http.dispatcher.edge.http.mappings"; + + private final Map loadBalancerMap = new ConcurrentHashMapEx<>(); + + private Map configurations = new HashMap<>(); + + 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(); + } + } + + // 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 LegacyPropertyFactory.getIntProperty(KEY_ORDER, 40_000); + } + + @Override + public boolean enabled() { + return environment.getProperty(KEY_ENABLED, boolean.class, false); + } + + @Override + public void init(Router router) { + String pattern = environment.getProperty(KEY_PATTERN, PATTERN_ANY); + router.routeWithRegex(pattern).failureHandler(this::onFailure).handler(this::onRequest); + } + + private void loadConfigurations() { + 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) { + URLMappedConfigurationItem configurationItem = findConfigurationItem(context.request().uri()); + if (configurationItem == null) { + context.next(); + return; + } + + String uri = Utils.findActualPath(context.request().uri(), configurationItem.getPrefixSegmentCount()); + + Invocation invocation = new Invocation() { + @Override + public String getConfigTransportName() { + return "rest"; + } + + @Override + public String getMicroserviceName() { + return configurationItem.getMicroserviceName(); + } + }; + + LoadBalancer loadBalancer = getOrCreateLoadBalancer(invocation, configurationItem.getMicroserviceName() + ); + ServiceCombServer server = loadBalancer.chooseServer(invocation); + if (server == null) { + LOG.warn("no available server for service {}", configurationItem.getMicroserviceName()); + serverNotReadyResponse(context); + return; + } + + URIEndpointObject endpointObject = new URIEndpointObject(server.getEndpoint().getEndpoint()); + + RequestOptions requestOptions = new RequestOptions(); + requestOptions.setHost(endpointObject.getHostOrIp()) + .setPort(endpointObject.getPort()) + .setSsl(endpointObject.isSslEnabled()) + .setMethod(context.request().method()) + .setURI(uri); + + HttpClient httpClient; + if (endpointObject.isHttp2Enabled()) { + httpClient = HttpClients.getClient(Http2TransportHttpClientOptionsSPI.CLIENT_NAME, false).getHttpClient(); + } else { + httpClient = HttpClients.getClient(HttpTransportHttpClientOptionsSPI.CLIENT_NAME, false).getHttpClient(); + } + + context.request().pause(); + + httpClient + .request(requestOptions).compose(httpClientRequest -> { + context.request().headers() + .forEach((header) -> httpClientRequest.headers().set(header.getKey(), header.getValue())); + + context.request().resume(); + context.request().handler(httpClientRequest::write); + context.request().endHandler((v) -> httpClientRequest.end()); + + return httpClientRequest.response().compose(httpClientResponse -> { + context.response().setStatusCode(httpClientResponse.statusCode()); + httpClientResponse.headers() + .forEach((header) -> context.response().headers().set(header.getKey(), header.getValue())); + httpClientResponse.handler(this.responseHandler(context)); + httpClientResponse.endHandler((v) -> context.response().end()); + return Future.succeededFuture(); + }); + }).onFailure(failure -> { + LOG.warn("send request to target {}:{} failed, cause {}", endpointObject.getHostOrIp(), endpointObject.getPort(), + failure.getMessage()); + serverNotReadyResponse(context); + }); + } + + private void serverNotReadyResponse(RoutingContext context) { + context.response().setStatusCode(503); + context.response().setStatusMessage("service not ready"); + context.response().end(); + } + + protected Handler responseHandler(RoutingContext routingContext) { + return data -> routingContext.response().write(data); + } + + protected LoadBalancer getOrCreateLoadBalancer(Invocation invocation, String microserviceName) { + DiscoveryContext context = new DiscoveryContext(); + context.setInputParameters(invocation); + 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 = getExtensionsManager().createLoadBalancerRule(microserviceName); + return new LoadBalancer(rule, microserviceName); + } + + private URLMappedConfigurationItem findConfigurationItem(String path) { + for (URLMappedConfigurationItem item : configurations.values()) { + if (item.getPattern().matcher(path).matches()) { + return item; + } + } + return null; + } +} 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 new file mode 100644 index 00000000000..56108a8cf1a --- /dev/null +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +import com.google.common.annotations.VisibleForTesting; + +import io.vertx.codegen.annotations.Nullable; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; + +/** + * Provide an easy mapping dispatcher that starts with a common prefix pattern. + */ +public class DefaultEdgeDispatcher extends AbstractEdgeDispatcher { + + private static final String KEY_ENABLED = "servicecomb.http.dispatcher.edge.default.enabled"; + + private static final String KEY_ORDER = "servicecomb.http.dispatcher.edge.default.order"; + + private static final String KEY_PREFIX = "servicecomb.http.dispatcher.edge.default.prefix"; + + private static final String KEY_PREFIX_SEGMENT_COUNT = "servicecomb.http.dispatcher.edge.default.prefixSegmentCount"; + + public static final String MICROSERVICE_NAME = "param0"; + + private int prefixSegmentCount; + + private Environment environment; + + // though this is an SPI, but add as beans. + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public int getOrder() { + // can not use environment, add as beans is later than instantiate SPI + return LegacyPropertyFactory.getIntProperty(KEY_ORDER, 20_000); + } + + @Override + public boolean enabled() { + return environment.getProperty(KEY_ENABLED, boolean.class, false); + } + + @Override + public void init(Router router) { + 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()); + router.routeWithRegex(regex).failureHandler(this::onFailure).handler(this::onRequest); + } + + @VisibleForTesting + String generateRouteRegex(String prefix, boolean withVersion) { + String version = withVersion ? "/([^\\\\/]+)" : ""; + return String.format("/%s/([^\\\\/]+)%s/(.*)", prefix, version); + } + + protected void onRequest(RoutingContext context) { + String microserviceName = extractMicroserviceName(context); + String path = Utils.findActualPath(context.request().path(), prefixSegmentCount); + + requestByFilter(context, microserviceName, path); + } + + @Nullable + private String extractMicroserviceName(RoutingContext context) { + return context.pathParam(MICROSERVICE_NAME); + } + + 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, path); + new RestProducerInvocationFlow(creator, requestEx, responseEx) + .run(); + } +} 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 new file mode 100644 index 00000000000..92594e6157a --- /dev/null +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderFilter.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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.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.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 extends AbstractFilter implements EdgeFilter { + public static final String NAME = "edge-add-headers"; + + 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(",")); + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean enabledForTransport(String transport) { + return CoreConst.RESTFUL.equals(transport); + } + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1995; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + if (publicHeaders.isEmpty()) { + return nextNode.onFilter(invocation); + } + + 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 new file mode 100644 index 00000000000..144dc2c6224 --- /dev/null +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeBootListener.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.edge.core; + +import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.core.executor.ExecutorManager; +import org.apache.servicecomb.transport.rest.vertx.TransportConfig; + +public class EdgeBootListener implements BootListener { + @Override + public void onBootEvent(BootEvent event) { + if (!EventType.BEFORE_PRODUCER_PROVIDER.equals(event.getEventType())) { + return; + } + + TransportConfig.setRestServerVerticle(EdgeRestServerVerticle.class); + 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 new file mode 100644 index 00000000000..6fa0457c7fc --- /dev/null +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeCoreConfiguration.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.edge.core; + +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 scbEdgeBootListener() { + return new EdgeBootListener(); + } + + @Bean + 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 new file mode 100644 index 00000000000..309548a5b38 --- /dev/null +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeInvocationCreator.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.edge.core; + +import java.util.concurrent.CompletableFuture; + +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; +import org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig; +import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; +import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; + +import io.vertx.core.Vertx; +import io.vertx.ext.web.RoutingContext; + +public class EdgeInvocationCreator extends RestVertxProducerInvocationCreator { + public static final String EDGE_INVOCATION_CONTEXT = "edgeInvocationContext"; + + protected final String microserviceName; + + protected final String path; + + protected MicroserviceReferenceConfig microserviceReferenceConfig; + + public EdgeInvocationCreator(RoutingContext routingContext, + HttpServletRequestEx requestEx, HttpServletResponseEx 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.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, requestEx.getMethod()); + } + + @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 + invocation.addLocalContext(EDGE_INVOCATION_CONTEXT, Vertx.currentContext()); + + return invocation; + } +} diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeRestServerVerticle.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeRestServerVerticle.java new file mode 100644 index 00000000000..5135bf5d4fb --- /dev/null +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeRestServerVerticle.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.edge.core; + +import org.apache.servicecomb.transport.rest.vertx.RestServerVerticle; + +public class EdgeRestServerVerticle extends RestServerVerticle { + @Override + public void start() throws Exception { + super.start(); + } +} 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 new file mode 100644 index 00000000000..d4e456c098f --- /dev/null +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.eventbus.Subscribe; + +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.PlatformHandler; + +/** + * Provide a URL mapping based dispatcher. Users configure witch URL patterns dispatch to a target service. + */ +public class URLMappedEdgeDispatcher extends AbstractEdgeDispatcher { + public static final String CONFIGURATION_ITEM = "URLMappedConfigurationItem"; + + private static final String PATTERN_ANY = "/(.*)"; + + private static final String KEY_ORDER = "servicecomb.http.dispatcher.edge.url.order"; + + private static final String KEY_ENABLED = "servicecomb.http.dispatcher.edge.url.enabled"; + + private static final String KEY_PATTERN = "servicecomb.http.dispatcher.edge.url.pattern"; + + private static final String KEY_MAPPING_PREFIX = "servicecomb.http.dispatcher.edge.url.mappings"; + + 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(); + } + } + + @VisibleForTesting + public Map getConfigurations() { + return configurations; + } + + @Override + public int getOrder() { + return LegacyPropertyFactory.getIntProperty(KEY_ORDER, 30_000); + } + + @Override + public boolean enabled() { + 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 = environment.getProperty(KEY_PATTERN, PATTERN_ANY); + router.routeWithRegex(pattern).failureHandler(this::onFailure) + .handler((PlatformHandler) URLMappedEdgeDispatcher.this::preCheck) + .handler(createBodyHandler()) + .handler(this::onRequest); + } + + private void loadConfigurations() { + 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) { + URLMappedConfigurationItem configurationItem = findConfigurationItem(context.request().path()); + if (configurationItem == null) { + // by pass body handler flag + context.put(RestBodyHandler.BYPASS_BODY_HANDLER, Boolean.TRUE); + context.next(); + return; + } + context.put(CONFIGURATION_ITEM, configurationItem); + context.next(); + } + + protected void onRequest(RoutingContext context) { + Boolean bypass = context.get(RestBodyHandler.BYPASS_BODY_HANDLER); + if (Boolean.TRUE.equals(bypass)) { + // clear flag + context.put(RestBodyHandler.BYPASS_BODY_HANDLER, Boolean.FALSE); + context.next(); + return; + } + + URLMappedConfigurationItem configurationItem = context.get(CONFIGURATION_ITEM); + + String path = Utils.findActualPath(context.request().path(), configurationItem.getPrefixSegmentCount()); + + requestByFilter(context, configurationItem, path); + } + + protected void requestByFilter(RoutingContext context, URLMappedConfigurationItem configurationItem, String path) { + HttpServletRequestEx requestEx = new VertxServerRequestToHttpServletRequest(context); + HttpServletResponseEx responseEx = new VertxServerResponseToHttpServletResponse(context.response()); + InvocationCreator creator = new EdgeInvocationCreator(context, requestEx, responseEx, + configurationItem.getMicroserviceName(), path); + new RestProducerInvocationFlow(creator, requestEx, responseEx) + .run(); + } + + private URLMappedConfigurationItem findConfigurationItem(String path) { + for (URLMappedConfigurationItem item : configurations.values()) { + if (item.getPattern().matcher(path).matches()) { + return item; + } + } + return null; + } +} 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.transport.rest.vertx.VertxHttpDispatcher b/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher new file mode 100644 index 00000000000..6a672cefffd --- /dev/null +++ b/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.apache.servicecomb.edge.core.DefaultEdgeDispatcher +org.apache.servicecomb.edge.core.URLMappedEdgeDispatcher +org.apache.servicecomb.edge.core.CommonHttpEdgeDispatcher 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/TestAbstractEdgeDispatcher.java b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestAbstractEdgeDispatcher.java new file mode 100644 index 00000000000..bbeceff2292 --- /dev/null +++ b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestAbstractEdgeDispatcher.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.edge.core; + +import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestAbstractEdgeDispatcher { + static class AbstractEdgeDispatcherForTest extends AbstractEdgeDispatcher { + @Override + public int getOrder() { + return 0; + } + + @Override + public void init(Router router) { + } + } + + @Test + public void onFailure() { + RoutingContext context = Mockito.mock(RoutingContext.class); + + HttpServerResponse response = Mockito.mock(HttpServerResponse.class); + + Mockito.when(context.response()).thenReturn(response); + Mockito.when(context.failure()).thenReturn(new RuntimeExceptionWithoutStackTrace("failed")); + + AbstractEdgeDispatcherForTest dispatcher = new AbstractEdgeDispatcherForTest(); + dispatcher.onFailure(context); + + Mockito.verify(response).setStatusMessage("Bad Gateway"); + Mockito.verify(response).setStatusCode(502); + + Mockito.when(context.failure()).thenReturn(new InvocationException(401, "unauthorized", "unauthorized")); + + dispatcher = new AbstractEdgeDispatcherForTest(); + dispatcher.onFailure(context); + Mockito.verify(response).setStatusMessage("unauthorized"); + Mockito.verify(response).setStatusCode(401); + } +} 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 new file mode 100644 index 00000000000..046adfaa3a2 --- /dev/null +++ b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Map; + +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() { + } + + @Test + public void testConfigurations() { + 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); + + RoutingContext context = Mockito.mock(RoutingContext.class); + Mockito.when(context.get(RestBodyHandler.BYPASS_BODY_HANDLER)).thenReturn(Boolean.TRUE); + dispatcher.onRequest(context); + + 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"); + Assertions.assertEquals(item.getMicroserviceName(), "serviceName"); + Assertions.assertEquals(item.getPrefixSegmentCount(), 2); + Assertions.assertEquals(item.getStringPattern(), "/a/b/c/.*"); + Assertions.assertEquals(item.getVersionRule(), "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", + "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"); + Assertions.assertEquals(item.getMicroserviceName(), "serviceName"); + Assertions.assertEquals(item.getPrefixSegmentCount(), 2); + Assertions.assertEquals(item.getStringPattern(), "/a/b/c/.*"); + Assertions.assertEquals(item.getVersionRule(), "2.0.0+"); + } +} 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 new file mode 100644 index 00000000000..38e8fa837d5 --- /dev/null +++ b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestUtils.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.edge.core; + +import org.apache.servicecomb.common.rest.route.Utils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestUtils { + @Test + public void testUtils() { + Assertions.assertEquals("/a/b/c", Utils.findActualPath("/a/b/c", -1)); + Assertions.assertEquals("/a/b/c", Utils.findActualPath("/a/b/c", 0)); + Assertions.assertEquals("/b/c", Utils.findActualPath("/a/b/c", 1)); + Assertions.assertEquals("/c", Utils.findActualPath("/a/b/c", 2)); + Assertions.assertEquals("", Utils.findActualPath("/a/b/c", 3)); + Assertions.assertEquals("", Utils.findActualPath("/a/b/c", 100)); + } +} diff --git a/edge/pom.xml b/edge/pom.xml new file mode 100644 index 00000000000..e11f4e088b6 --- /dev/null +++ b/edge/pom.xml @@ -0,0 +1,34 @@ + + + + + 4.0.0 + + org.apache.servicecomb + java-chassis-parent + 3.4.0-SNAPSHOT + ../parents/default + + edge + Java Chassis::Edge + pom + + edge-core + + diff --git a/etc/checkstyle-eclipse.xml b/etc/checkstyle-eclipse.xml deleted file mode 100644 index 40b0744a6e0..00000000000 --- a/etc/checkstyle-eclipse.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/etc/checkstyle-suppressions.xml b/etc/checkstyle-suppressions.xml deleted file mode 100644 index bd00455ccb1..00000000000 --- a/etc/checkstyle-suppressions.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/etc/checkstyle.xml b/etc/checkstyle.xml deleted file mode 100644 index 7c4bcd5e206..00000000000 --- a/etc/checkstyle.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/etc/code-templates.xml b/etc/code-templates.xml index df52b11487d..dcb9150a408 100644 --- a/etc/code-templates.xml +++ b/etc/code-templates.xml @@ -1,12 +1,13 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/foundations/foundation-common/pom.xml b/foundations/foundation-common/pom.xml index 06ac15638c7..2f5a18878b4 100644 --- a/foundations/foundation-common/pom.xml +++ b/foundations/foundation-common/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - 0.3.0-SNAPSHOT - foundations - - foundation-common + 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 + 3.4.0-SNAPSHOT + foundations + + foundation-common + Java Chassis::Foundations::Common + + + + com.sun.activation + jakarta.activation + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-annotations + + + org.springframework + spring-context + + + org.slf4j + slf4j-api + + + org.apache.httpcomponents + httpclient + + + commons-logging + commons-logging + + + + + jakarta.ws.rs + jakarta.ws.rs-api + + + commons-io + commons-io + + + com.google.guava + guava + + + com.google.guava + failureaccess + + + + jakarta.servlet + jakarta.servlet-api + + + + org.apache.commons + commons-lang3 + + + + org.apache.servicecomb + foundation-spi + + + + org.apache.servicecomb + foundation-test-scaffolding + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + io.vertx + vertx-core + - - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - - - com.fasterxml.jackson.core - jackson-databind - - - com.fasterxml.jackson.core - jackson-annotations - - - org.springframework - spring-context - - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - - - org.apache.httpcomponents - httpclient - - - javax.ws.rs - javax.ws.rs-api - - - commons-io - commons-io - - + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.jmockit + jmockit + test + + diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/auth/AuthHeaderProvider.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/auth/AuthHeaderProvider.java deleted file mode 100644 index f2e3ec043ef..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/auth/AuthHeaderProvider.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.auth; - -import java.util.Map; - -public interface AuthHeaderProvider { - Map authHeaders(); -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/CommonThread.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/CommonThread.java deleted file mode 100644 index b4747a5a3f0..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/CommonThread.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common; - -public class CommonThread extends Thread { - protected volatile boolean shutdown; - - public CommonThread() { - super(); - } - - public CommonThread(String name, long stackSize) { - super(null, null, name, stackSize); - } - - public boolean isShutdown() { - return shutdown; - } - - public boolean isRunning() { - return !shutdown; - } - - public void shutdown() { - shutdown = true; - interrupt(); - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/NamedThreadFactory.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/NamedThreadFactory.java deleted file mode 100644 index 2d5232f2b34..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/NamedThreadFactory.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common; - -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * 已命名线程工厂. - * 获取新的名字有固定前缀的线程工厂. - */ -public class NamedThreadFactory implements ThreadFactory { - - private final AtomicInteger threadNumber = new AtomicInteger(); - - private String prefix; - - public NamedThreadFactory() { - this("Thread"); - } - - public NamedThreadFactory(String prefix) { - this.prefix = prefix; - } - - /** - * 获取新的名字以prefix为前缀的线程 - */ - @Override - public Thread newThread(Runnable r) { - return new Thread(r, prefix + "-" + threadNumber.getAndIncrement()); - } - - public String getPrefix() { - return prefix; - } - - public void setPrefix(String prefix) { - this.prefix = prefix; - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/RegisterManager.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/RegisterManager.java deleted file mode 100644 index c2f2c8835e3..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/RegisterManager.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common; - -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RegisterManager { - private static final Logger LOGGER = LoggerFactory.getLogger(RegisterManager.class); - - private String name; - - private String registerErrorFmt = "Not allow regsiter repeat data, name=%s, key=%s"; - - private Map objMap = new ConcurrentHashMap<>(); - - private Object lockObj = new Object(); - - public RegisterManager(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public String getRegisterErrorFmt() { - return registerErrorFmt; - } - - public void setRegisterErrorFmt(String registerErrorFmt) { - this.registerErrorFmt = registerErrorFmt; - } - - public void register(KEY key, VALUE value) { - synchronized (lockObj) { - if (objMap.get(key) != null) { - // 不允许重复注册 - String msg = String.format(registerErrorFmt, name, key); - LOGGER.error(msg); - - // 禁止启动 - throw new Error(msg); - } - objMap.put(key, value); - } - } - - public VALUE findValue(KEY key) { - return objMap.get(key); - } - - public VALUE ensureFindValue(KEY key) { - VALUE value = objMap.get(key); - if (value == null) { - String msg = String.format("Can not find value, name=%s, key=%s", name, key); - throw new Error(msg); - } - - return value; - } - - public Collection keys() { - return objMap.keySet(); - } - - public Collection values() { - return objMap.values(); - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/base/DescriptiveRunnable.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/base/DescriptiveRunnable.java deleted file mode 100644 index 479f05b81fa..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/base/DescriptiveRunnable.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.base; - -public interface DescriptiveRunnable extends Runnable { - - String description(); -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/base/RetryableRunnable.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/base/RetryableRunnable.java deleted file mode 100644 index 72686e28c95..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/base/RetryableRunnable.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.base; - -import java.util.concurrent.TimeUnit; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RetryableRunnable implements Runnable { - private static final Logger LOGGER = LoggerFactory.getLogger(RetryableRunnable.class); - - private final DescriptiveRunnable runnable; - - private final int sleepInMs; - - public RetryableRunnable(DescriptiveRunnable runnable, int sleepInMs) { - this.runnable = runnable; - this.sleepInMs = sleepInMs; - } - - @Override - public void run() { - boolean success; - do { - try { - LOGGER.info("Running [{}] task", runnable.description()); - runnable.run(); - success = true; - } catch (Throwable e) { - success = false; - LOGGER.error("Failed to run [{}] task", runnable.description(), e); - sleep(sleepInMs); - } - } while (!success && !Thread.currentThread().isInterrupted()); - - LOGGER.info("Task [{}] completed", runnable.description()); - } - - private void sleep(int timeout) { - try { - TimeUnit.MILLISECONDS.sleep(timeout); - } catch (InterruptedException ignored) { - Thread.currentThread().interrupt(); - } - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/base/ServiceCombConstants.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/base/ServiceCombConstants.java deleted file mode 100644 index 1ace49717c6..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/base/ServiceCombConstants.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.base; - -public class ServiceCombConstants { - - public static final String CONFIG_APPLICATION_ID = "APPLICATION_ID"; - - public static final String CONFIG_SERVICE = "service_description"; - - public static final String CONFIG_SERVICE_NAME = CONFIG_SERVICE + ".name"; - - public static final String CONFIG_SERVICE_VERSION = CONFIG_SERVICE + ".version"; - - public static final String CONFIG_SERVICE_ROLE = CONFIG_SERVICE + ".role"; - - public static final String CONFIG_SERVICE_DESCRIPTION = CONFIG_SERVICE + ".description"; - - public static final String DEFAULT_SERVICE_NAME = "anonymous-service"; - - public static final String CONFIG_TRACING_COLLECTOR_ADDRESS = "servicecomb.tracing.collector.address"; - - public static final String CONFIG_TRACING_COLLECTOR_PATH = "/api/v1/spans"; - - public static final String DEFAULT_TRACING_COLLECTOR_ADDRESS = "http://127.0.0.1:9411"; - - public static final String CONFIG_SERVICECOMB_PREFIX = "servicecomb."; - - public static final String CONFIG_CSE_PREFIX = "cse."; -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/ConfigLoader.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/ConfigLoader.java deleted file mode 100644 index df37b2fc36a..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/ConfigLoader.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.config; - -public interface ConfigLoader { - - T load() throws Exception; -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/ConfigMgr.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/ConfigMgr.java deleted file mode 100644 index d34a7eecf7a..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/ConfigMgr.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.config; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.springframework.core.io.Resource; - -import io.servicecomb.foundation.common.config.impl.IncConfigs; -import io.servicecomb.foundation.common.config.impl.IncConfigs.IncConfig; -import io.servicecomb.foundation.common.config.impl.PropertiesLoader; -import io.servicecomb.foundation.common.config.impl.XmlLoader; -import io.servicecomb.foundation.common.config.impl.XmlLoaderUtils; - -public class ConfigMgr { - public static final ConfigMgr INSTANCE = new ConfigMgr(); - - private Map configMap = new ConcurrentHashMap<>(); - - private Map configLoaderMap; - - /** - * 扫描所有的*.inc.config.xml - * 根据配置初始化所有的loader - */ - public void init() throws Exception { - List resArr = - PaaSResourceUtils.getSortedResources("classpath*:config/config.inc.xml", ".inc.xml"); - IncConfigs incConfigs = new IncConfigs(); - incConfigs.setPropertiesList(new ArrayList<>()); - incConfigs.setXmlList(new ArrayList<>()); - for (Resource resource : resArr) { - IncConfigs tmp = XmlLoaderUtils.load(resource, IncConfigs.class); - if (tmp.getPropertiesList() != null) { - incConfigs.getPropertiesList().addAll(tmp.getPropertiesList()); - } - if (tmp.getXmlList() != null) { - incConfigs.getXmlList().addAll(tmp.getXmlList()); - } - } - - configLoaderMap = new HashMap<>(); - for (IncConfig incConfig : incConfigs.getPropertiesList()) { - PropertiesLoader loader = (PropertiesLoader) configLoaderMap.get(incConfig.getId()); - if (loader != null) { - loader.getLocationPatternList().addAll(incConfig.getPathList()); - continue; - } - - configLoaderMap.put(incConfig.getId(), new PropertiesLoader(incConfig.getPathList())); - } - - for (IncConfig incConfig : incConfigs.getXmlList()) { - XmlLoader loader = (XmlLoader) configLoaderMap.get(incConfig.getId()); - if (loader != null) { - loader.getLocationPatternList().addAll(incConfig.getPathList()); - continue; - } - configLoaderMap.put(incConfig.getId(), new XmlLoader(incConfig.getPathList())); - } - } - - @SuppressWarnings("unchecked") - public T getConfig(String configId) throws Exception { - T config = (T) configMap.get(configId); - if (config != null) { - return config; - } - - if (configLoaderMap == null) { - init(); - } - - ConfigLoader loader = configLoaderMap.get(configId); - if (loader == null) { - throw new RuntimeException("can not find config for " + configId); - } - - config = (T) loader.load(); - configMap.put(configId, config); - return config; - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/PaaSPropertyPlaceholderConfigurer.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/PaaSPropertyPlaceholderConfigurer.java deleted file mode 100644 index a91622d7517..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/PaaSPropertyPlaceholderConfigurer.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.config; - -import java.io.IOException; -import java.util.Properties; - -public class PaaSPropertyPlaceholderConfigurer - extends org.springframework.beans.factory.config.PropertyPlaceholderConfigurer { - - // 以逗号分隔 - private String configId; - - public void setConfigId(String configId) { - this.configId = configId; - } - - @Override - protected void loadProperties(Properties props) throws IOException { - for (String id : configId.split(",")) { - try { - Properties config = ConfigMgr.INSTANCE.getConfig(id); - props.putAll(config); - } catch (Exception e) { - throw new IOException(e); - } - } - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/PaaSResourceUtils.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/PaaSResourceUtils.java deleted file mode 100644 index 748a33433b3..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/PaaSResourceUtils.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.config; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.Properties; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.Unmarshaller; - -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.core.io.support.ResourcePatternResolver; -import org.springframework.util.StringUtils; -import org.w3c.dom.Document; - -import io.servicecomb.foundation.common.config.impl.PropertiesLoader; -import io.servicecomb.foundation.common.utils.JsonUtils; - -public class PaaSResourceUtils extends org.springframework.util.ResourceUtils { - public static final String PROPERTIES_SUFFIX = ".properties"; - - public static final String XML_SUFFIX = ".xml"; - - private static ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); - - /** - * 失败,则返回空数组 - */ - public static Resource[] getResources(String locationPattern) { - try { - return resourcePatternResolver.getResources(locationPattern); - } catch (IOException e) { - return new Resource[0]; - } - } - - /** - * 失败,则返回空列表 - */ - public static List getResources(String... locationPatterns) { - List ret = new ArrayList<>(); - for (String locationPattern : locationPatterns) { - Resource[] resArr = getResources(locationPattern); - ret.addAll(Arrays.asList(resArr)); - } - - return ret; - } - - /** - * 文件同名时,jar的优先级比较低 - * jar: xxx.xml - * jar: xxx.model.xml - * file:xxx.xml - * 调用者保证,所有res的后缀都是suffix - * file文件应该只有一个,因为放在目录中的配置文件,应该是最终的部署定制文件 - * 此时,还分多个,是不合适的 - */ - public static void sortResources(List resList, String suffix) { - resList.sort(new Comparator() { - @Override - public int compare(Resource o1, Resource o2) { - try { - // jar的优先级比较低 - if (isJarURL(o1.getURL()) && isFileURL(o2.getURL())) { - return -1; - } - - // 干掉后缀,再排序 - String name1 = o1.getFilename(); - String name2 = o2.getFilename(); - - //Resource.getFilename接口会返回null,当路径的文件名不存在时 - //配置文件一定存在,并且Resource有合法的文件名,name1和name2不可能为空, - //这里做判断是为了处理CodeDEX报的null引用 - if (StringUtils.isEmpty(name1) || StringUtils.isEmpty(name2)) { - throw new IOException( - String.format("Resource %s or %s is not a file", o1.getURI(), o2.getURI())); - } - - name1 = name1.substring(0, name1.length() - suffix.length()); - name2 = name2.substring(0, name2.length() - suffix.length()); - - return name1.compareTo(name2); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }); - } - - public static void sortProperties(List resList) { - sortResources(resList, PROPERTIES_SUFFIX); - } - - public static void sortXmls(List resList) { - sortResources(resList, XML_SUFFIX); - } - - public static List getSortedResources(String locationPattern, String suffix) { - if (StringUtils.isEmpty(locationPattern)) { - throw new RuntimeException("Resource path must not be null or empty"); - } - - if (!locationPattern.endsWith(suffix)) { - throw new RuntimeException("Resource path must ends with " + suffix); - } - - String prefix = locationPattern.substring(0, locationPattern.length() - suffix.length()); - - List resList = PaaSResourceUtils.getResources(locationPattern, prefix + ".*" + suffix); - sortResources(resList, suffix); - return resList; - } - - public static List getSortedPorperties(String locationPattern) { - return getSortedResources(locationPattern, PROPERTIES_SUFFIX); - } - - public static Properties loadMergedProperties(String locationPattern) throws Exception { - PropertiesLoader loader = new PropertiesLoader(Arrays.asList(locationPattern)); - return loader.load(); - } - - public static List getSortedXmls(String locationPattern) { - return getSortedResources(locationPattern, XML_SUFFIX); - } - - @SuppressWarnings("unchecked") - public static T loadConfigAs(String configId, Class clazz) throws Exception { - Object config = ConfigMgr.INSTANCE.getConfig(configId); - if (Properties.class.isInstance(config)) { - return (T) JsonUtils.convertValue(config, clazz); - } - - if (Document.class.isInstance(config)) { - JAXBContext jaxbContext = JAXBContext.newInstance(clazz); - Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); - return (T) jaxbUnmarshaller.unmarshal((Document) config, clazz).getValue(); - } - - throw new Exception("not support"); - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/AbstractLoader.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/AbstractLoader.java deleted file mode 100644 index da8aba93bbe..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/AbstractLoader.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.config.impl; - -import java.util.List; - -import io.servicecomb.foundation.common.config.ConfigLoader; - -public abstract class AbstractLoader implements ConfigLoader { - protected List locationPatternList; - - public AbstractLoader(List locationPatternList) { - this.locationPatternList = locationPatternList; - } - - public List getLocationPatternList() { - return locationPatternList; - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/IdXmlLoader.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/IdXmlLoader.java deleted file mode 100644 index e7a462f1ce5..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/IdXmlLoader.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.config.impl; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.util.StringUtils; -import org.w3c.dom.Element; - -public class IdXmlLoader extends XmlLoader { - private Map idMap = new HashMap<>(); - - public IdXmlLoader(List locationPatternList) { - super(locationPatternList); - } - - public IdXmlLoader(List locationPatternList, String suffix) { - super(locationPatternList, suffix); - } - - @Override - protected Element findAndSetExist(Element ele) { - String id = ele.getAttribute("id"); - if (StringUtils.isEmpty(id)) { - throw new RuntimeException("id not allow be empty"); - } - - Element existEle = idMap.get(id); - if (existEle == null) { - idMap.put(id, ele); - return null; - } - - return existEle; - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/IncConfigs.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/IncConfigs.java deleted file mode 100644 index eb5690086b6..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/IncConfigs.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.config.impl; - -import java.util.List; - -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; - -@JacksonXmlRootElement(localName = "configs") -public class IncConfigs { - - public static class IncConfig { - @JacksonXmlProperty(isAttribute = true) - private String id; - - @JacksonXmlProperty(isAttribute = true) - private String loader; - - @JacksonXmlProperty(localName = "path") - @JacksonXmlElementWrapper(useWrapping = false) - private List pathList; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getLoader() { - return loader; - } - - public void setLoader(String loader) { - this.loader = loader; - } - - public List getPathList() { - return pathList; - } - - public void setPathList(List pathList) { - this.pathList = pathList; - } - } - - @JacksonXmlProperty(localName = "properties") - @JacksonXmlElementWrapper(useWrapping = false) - private List propertiesList; - - @JacksonXmlProperty(localName = "xml") - @JacksonXmlElementWrapper(useWrapping = false) - private List xmlList; - - public List getPropertiesList() { - return propertiesList; - } - - public void setPropertiesList(List propertiesList) { - this.propertiesList = propertiesList; - } - - public List getXmlList() { - return xmlList; - } - - public void setXmlList(List xmlList) { - this.xmlList = xmlList; - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/PropertiesLoader.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/PropertiesLoader.java deleted file mode 100644 index 68a99a1a86e..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/PropertiesLoader.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.config.impl; - -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; - -import org.springframework.core.io.Resource; - -import io.servicecomb.foundation.common.config.PaaSResourceUtils; - -public class PropertiesLoader extends AbstractLoader { - private List foundResList = new ArrayList<>(); - - public PropertiesLoader(List locationPatternList) { - super(locationPatternList); - } - - public List getFoundResList() { - return foundResList; - } - - @SuppressWarnings("unchecked") - @Override - public T load() throws Exception { - Properties props = new Properties(); - for (String locationPattern : locationPatternList) { - List resList = PaaSResourceUtils.getSortedPorperties(locationPattern); - foundResList.addAll(resList); - PaaSPropertiesLoaderUtils.fillAllProperties(props, resList); - } - - return (T) props; - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/XmlLoader.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/XmlLoader.java deleted file mode 100644 index 0c3d1ed6b10..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/XmlLoader.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.config.impl; - -import java.util.List; - -import org.springframework.core.io.Resource; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import io.servicecomb.foundation.common.config.PaaSResourceUtils; - -/** - * 无逻辑append xml - */ -public class XmlLoader extends AbstractLoader { - - private String suffix; - - public XmlLoader(List locationPatternList) { - this(locationPatternList, PaaSResourceUtils.XML_SUFFIX); - } - - public XmlLoader(List locationPatternList, String suffix) { - super(locationPatternList); - this.suffix = suffix; - } - - @SuppressWarnings("unchecked") - @Override - public T load() throws Exception { - Document doc = XmlLoaderUtils.newDoc(); - Element root = null; - for (String locationPattern : locationPatternList) { - List resList = PaaSResourceUtils.getSortedResources(locationPattern, suffix); - for (Resource res : resList) { - Document tmpDoc = XmlLoaderUtils.load(res); - Element tmpRoot = tmpDoc.getDocumentElement(); - - if (root == null) { - root = (Element) doc.importNode(tmpRoot, false); - doc.appendChild(root); - } - - NodeList nodeList = tmpRoot.getChildNodes(); - for (int idx = 0; idx < nodeList.getLength(); idx++) { - Node child = nodeList.item(idx); - - if (!Element.class.isInstance(child)) { - continue; - } - - Element clone = (Element) doc.importNode(child, true); - Element exist = findAndSetExist((Element) clone); - if (exist == null) { - root.appendChild(clone); - continue; - } - - // merge attr and children - XmlLoaderUtils.mergeElement(clone, exist); - } - } - } - return (T) doc; - } - - protected Element findAndSetExist(Element ele) { - return null; - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/exceptions/ServiceCombException.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/exceptions/ServiceCombException.java deleted file mode 100644 index 0696cc9cb3a..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/exceptions/ServiceCombException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.exceptions; - -public class ServiceCombException extends RuntimeException { - private static final long serialVersionUID = -1085233183289520695L; - - public ServiceCombException(String cause, Throwable throwable) { - super(cause, throwable); - } - - public ServiceCombException(String cause) { - super(cause); - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/net/IpPort.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/net/IpPort.java deleted file mode 100644 index bdfe2b86752..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/net/IpPort.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.net; - -import java.net.InetSocketAddress; - -public class IpPort { - private String hostOrIp; - - private int port; - - private volatile InetSocketAddress socketAddress; - - private final Object lock = new Object(); - - public IpPort() { - - } - - public IpPort(String hostOrIp, int port) { - this.hostOrIp = hostOrIp; - this.port = port; - } - - public String getHostOrIp() { - return hostOrIp; - } - - public void setHostOrIp(String hostOrIp) { - this.hostOrIp = hostOrIp; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public InetSocketAddress getSocketAddress() { - if (socketAddress == null) { - synchronized (lock) { - if (socketAddress == null) { - InetSocketAddress tmpSocketAddress = new InetSocketAddress(hostOrIp, port); - socketAddress = tmpSocketAddress; - } - } - } - - return socketAddress; - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/net/NetUtils.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/net/NetUtils.java deleted file mode 100644 index 6f872188022..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/net/NetUtils.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.net; - -import java.io.IOException; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.ServerSocket; -import java.net.SocketException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public final class NetUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(NetUtils.class); - - // one interface can bind to multiple address - // we only save one ip for each interface name. - // eg: - // 1. eth0 -> ip1 ip2 - // last data is eth0 -> ip2 - // 2. eth0 -> ip1 - // eth0:0 -> ip2 - // eth0:1 -> ip3 - // on interface name conflict, all data saved - - // key is network interface name - private static Map allInterfaceAddresses = new HashMap<>(); - - private static String hostName; - - private static String hostAddress; - - static { - try { - doGetIpv4AddressFromNetworkInterface(); - // getLocalHost will throw exception in some docker image and sometimes will do a hostname lookup and time consuming - hostName = InetAddress.getLocalHost().getHostName(); - hostAddress = InetAddress.getLocalHost().getHostAddress(); - - LOGGER.info( - "add host name from localhost:" + hostName + ",host address:" + hostAddress); - } catch (Exception e) { - LOGGER.error("got exception when trying to get addresses:", e); - if (allInterfaceAddresses.size() >= 1) { - InetAddress entry = allInterfaceAddresses.entrySet().iterator().next().getValue(); - // get host name will do a reverse name lookup and is time consuming - hostName = entry.getHostName(); - hostAddress = entry.getHostAddress(); - LOGGER.info( - "add host name from interfaces:" + hostName + ",host address:" + hostAddress); - } - } - } - - private NetUtils() { - } - - /** - * docker环境中,有时无法通过InetAddress.getLocalHost()获取 ,会报unknown host Exception, system error - * 此时,通过遍历网卡接口的方式规避,出来的数据不一定对 - */ - private static void doGetIpv4AddressFromNetworkInterface() throws SocketException { - Enumeration iterNetwork = NetworkInterface.getNetworkInterfaces(); - - while (iterNetwork.hasMoreElements()) { - NetworkInterface network = iterNetwork.nextElement(); - - if (!network.isUp() || network.isLoopback() || network.isVirtual()) { - continue; - } - - Enumeration iterAddress = network.getInetAddresses(); - while (iterAddress.hasMoreElements()) { - InetAddress address = iterAddress.nextElement(); - - if (address.isAnyLocalAddress() || address.isLoopbackAddress() || address.isMulticastAddress() - || Inet6Address.class.isInstance(address)) { - continue; - } - - if (Inet4Address.class.isInstance(address)) { - LOGGER.info( - "add network interface name:" + network.getName() + ",host address:" + address.getHostAddress()); - allInterfaceAddresses.put(network.getName(), address); - } - } - } - - return; - } - - /** - * address ip:port格式 - */ - public static IpPort parseIpPort(String address) { - if (address == null) { - return null; - } - - int idx = address.indexOf(':'); - if (idx == -1) { - return null; - } - String hostOrIp = address.substring(0, idx); - int port = Integer.parseInt(address.substring(idx + 1)); - - return new IpPort(hostOrIp, port); - } - - public static IpPort parseIpPortFromURI(String uriAddress) { - if (uriAddress == null) { - return null; - } - - try { - URI uri = new URI(uriAddress); - String authority = uri.getAuthority(); - return parseIpPort(authority); - } catch (URISyntaxException e) { - return null; - } - } - - /** - * 对于配置为0.0.0.0的地址,let it go - * schema, e.g. http - * adddress, e.g 0.0.0.0:8080 - * return 实际监听的地址 - */ - public static String getRealListenAddress(String schema, String address) { - if (address == null) { - return address; - } - try { - URI originalURI = new URI(schema + "://" + address); - IpPort ipPort = NetUtils.parseIpPort(originalURI.getAuthority()); - if (ipPort == null) { - LOGGER.warn("address {} not valid.", address); - return null; - } - return originalURI.toString(); - } catch (URISyntaxException e) { - LOGGER.warn("address {} not valid.", address); - return null; - } - } - - public static String getHostName() { - return hostName; - } - - public static String getHostAddress() { - return hostAddress; - } - - public static InetAddress getInterfaceAddress(String interfaceName) { - return allInterfaceAddresses.get(interfaceName); - } - - public static InetAddress ensureGetInterfaceAddress(String interfaceName) { - InetAddress address = allInterfaceAddresses.get(interfaceName); - if (address == null) { - throw new IllegalArgumentException("Can not find address for interface name: " + interfaceName); - } - return address; - } - - public static boolean canTcpListen(InetAddress address, int port) { - try (ServerSocket ss = new ServerSocket(port, 0, address)) { - return true; - } catch (IOException e) { - return false; - } - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/net/URIEndpointObject.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/net/URIEndpointObject.java deleted file mode 100644 index 0daddf881a3..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/net/URIEndpointObject.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.net; - -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.apache.http.NameValuePair; -import org.apache.http.client.utils.URLEncodedUtils; - -/** - * tranport公共的Endpoint Object,当transport使用URI表示的时候,可以转化为这个对象。 - */ -public class URIEndpointObject extends IpPort { - private static final String SSL_ENABLED_KEY = "sslEnabled"; - - private boolean sslEnabled; - - private Map> querys; - - public URIEndpointObject(String endpoint) { - URI uri = URI.create(endpoint); - setHostOrIp(uri.getHost()); - if (uri.getPort() < 0) { - // do not use default port - throw new IllegalArgumentException("port not specified."); - } - setPort(uri.getPort()); - querys = splitQuery(uri); - sslEnabled = Boolean.parseBoolean(getFirst(SSL_ENABLED_KEY)); - } - - public static Map> splitQuery(URI uri) { - final Map> queryPairs = new LinkedHashMap>(); - List pairs = URLEncodedUtils.parse(uri, StandardCharsets.UTF_8.name()); - for (NameValuePair pair : pairs) { - List list = queryPairs.computeIfAbsent(pair.getName(), name -> { - return new ArrayList<>(); - }); - list.add(pair.getValue()); - } - return queryPairs; - } - - public boolean isSslEnabled() { - return sslEnabled; - } - - public List getQuery(String key) { - return querys.get(key); - } - - public String getFirst(String key) { - List values = querys.get(key); - // it's impossible that values is not null and size is 0 - if (values == null) { - return null; - } - - return values.get(0); - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/spring/PaasNamespaceHandler.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/spring/PaasNamespaceHandler.java deleted file mode 100644 index c43653a2615..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/spring/PaasNamespaceHandler.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.spring; - -import java.util.Map.Entry; -import java.util.Properties; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.xml.BeanDefinitionParser; -import org.springframework.beans.factory.xml.NamespaceHandlerSupport; - -import io.servicecomb.foundation.common.config.PaaSResourceUtils; -import io.servicecomb.foundation.common.utils.FortifyUtils; - -//根据各个jar提供的信息来注册parser -public class PaasNamespaceHandler extends NamespaceHandlerSupport { - private static final Logger LOGGER = LoggerFactory.getLogger(PaasNamespaceHandler.class); - - private static final String NAMESPACE_RES = "classpath*:META-INF/spring/namespace.properties"; - - // @Override - public void init() { - Properties properties = null; - - try { - properties = PaaSResourceUtils.loadMergedProperties(NAMESPACE_RES); - } catch (Exception e) { - LOGGER.error("Failed to load namespace handler define, {}, {}", - NAMESPACE_RES, - FortifyUtils.getErrorInfo(e)); - return; - } - - for (Entry entry : properties.entrySet()) { - String className = entry.getValue().toString(); - try { - Class clazz = Class.forName(className); - Object instance = clazz.newInstance(); - registerBeanDefinitionParser(entry.getKey().toString(), - (BeanDefinitionParser) instance); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { - // 类找不到,说明没部署相应的jar包,这不一定是错误 - // 可能是业务就选择不部署相应的jar包 - // 所以只是打印个info日志 - - LOGGER.info("Failed to create BeanDefinitionParser instance of {}", className); - } - } - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/BeanUtils.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/BeanUtils.java deleted file mode 100644 index 89c63fef3dd..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/BeanUtils.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.utils; - -import org.springframework.aop.TargetClassAware; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -public final class BeanUtils { - public static final String DEFAULT_BEAN_RESOURCE = "classpath*:META-INF/spring/*.bean.xml"; - - private static ApplicationContext context; - - private BeanUtils() { - } - - public static void init() { - init(DEFAULT_BEAN_RESOURCE); - } - - public static void init(String... configLocations) { - context = new ClassPathXmlApplicationContext(configLocations); - } - - public static ApplicationContext getContext() { - return context; - } - - public static void setContext(ApplicationContext applicationContext) { - context = applicationContext; - } - - /** - * 不应该在业务流程中频繁调用,因为内部必然会加一个锁做互斥,会影响并发度 - */ - @SuppressWarnings("unchecked") - public static T getBean(String name) { - return (T) context.getBean(name); - } - - public static Class getImplClassFromBean(Object bean) { - if (TargetClassAware.class.isInstance(bean)) { - return ((TargetClassAware) bean).getTargetClass(); - } - - return bean.getClass(); - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/HttpUtils.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/HttpUtils.java deleted file mode 100644 index baed48cbf63..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/HttpUtils.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.utils; - -import java.net.HttpCookie; -import java.util.List; - -public final class HttpUtils { - private HttpUtils() { - } - - public static String getCookieParamValue(String cookieName, List cookieLines) { - for (String cookieLine : cookieLines) { - String value = getCookieParamValue(cookieName, cookieLine); - if (value != null) { - return value; - } - } - - return null; - } - - public static String getCookieParamValue(String cookieName, String cookieLine) { - List httpCookieList = HttpCookie.parse(cookieLine); - for (HttpCookie httpCookie : httpCookieList) { - if (cookieName.equals(httpCookie.getName())) { - return httpCookie.getValue(); - } - } - return null; - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/JsonUtils.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/JsonUtils.java deleted file mode 100644 index 9dc21957c78..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/JsonUtils.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.utils; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerationException; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -public final class JsonUtils { - public static final ObjectMapper OBJ_MAPPER; - - static { - //如果待转换的字符串比较随机,这个缓存很容易就填满,会导致同步清空缓存,对性能有影响 - JsonFactory jsonFactory = new JsonFactory(); - jsonFactory.configure(JsonFactory.Feature.INTERN_FIELD_NAMES, false); - OBJ_MAPPER = new ObjectMapper(jsonFactory); - } - - // static - // { - // //设置反序列化时忽略json字符串中存在而Java对象实际没有的属性 - // om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - // } - - private JsonUtils() { - } - - public static T readValue(byte[] src, - Class valueType) throws JsonParseException, JsonMappingException, IOException { - return OBJ_MAPPER.readValue(src, valueType); - } - - public static T readValue(InputStream is, - Class valueType) throws JsonParseException, JsonMappingException, IOException { - return OBJ_MAPPER.readValue(is, valueType); - } - - public static T readValue(InputStream is, - JavaType valueType) throws JsonParseException, JsonMappingException, IOException { - return OBJ_MAPPER.readValue(is, valueType); - } - - public static byte[] writeValueAsBytes(Object value) throws JsonProcessingException { - return OBJ_MAPPER.writeValueAsBytes(value); - } - - public static String writeValueAsString(Object value) throws JsonProcessingException { - return OBJ_MAPPER.writeValueAsString(value); - } - - public static T convertValue(Object fromValue, Class toValueType) { - return OBJ_MAPPER.convertValue(fromValue, toValueType); - } - - public static void writeValue(OutputStream out, - Object value) throws JsonGenerationException, JsonMappingException, IOException { - OBJ_MAPPER.writeValue(out, value); - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/Log4jUtils.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/Log4jUtils.java deleted file mode 100644 index b29c698c95b..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/Log4jUtils.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.utils; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.Map.Entry; -import java.util.Properties; -import java.util.stream.Collectors; - -import org.apache.commons.io.FileUtils; -import org.apache.log4j.PropertyConfigurator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.io.Resource; - -import io.servicecomb.foundation.common.config.impl.PropertiesLoader; - -public final class Log4jUtils { - private static final String MERGED_FILE = "merged.log4j.properties"; - - // spring boot的包装中会重复调用init,需要规避一下 - private static boolean inited = false; - - private static final Object LOCK = new Object(); - - private Log4jUtils() { - } - - public static void init() throws Exception { - init(Arrays.asList("classpath*:config/base/log4j.properties", "classpath*:config/log4j.properties")); - } - - public static void init(String locationPattern) throws Exception { - init(Arrays.asList(locationPattern)); - } - - public static void init(List locationPatterns) throws Exception { - if (inited) { - return; - } - - synchronized (LOCK) { - if (inited) { - return; - } - - PropertiesLoader loader = new PropertiesLoader(locationPatterns); - Properties properties = loader.load(); - if (properties.isEmpty()) { - throw new Exception("can not find resource " + locationPatterns); - } - - PropertyConfigurator.configure(properties); - inited = true; - - // 如果最高优先级的文件是在磁盘上,且有写权限,则将merge的结果输出到该目录,方便维护时观察生效的参数 - outputFile(loader.getFoundResList(), properties); - } - } - - private static void outputFile(List resList, - Properties properties) throws IOException, URISyntaxException { - //不可以作为class的变量初始化,因为在outputFile前一句log机制才初始化完成的 - //must create org.slf4j.impl.Log4jLoggerAdapter by LoggerExtFactory - //in order to redefine Log4jLoggerAdapter before other class load Log4jLoggerAdapter - Logger log = LoggerFactory.getLogger(Log4jUtils.class); - - String content = genFileContext(resList, properties); - //不打印配置信息,有密钥等敏感信息 - //log.info("Merged log4j:\n{}", content); - - Resource res = resList.get(resList.size() - 1); - // 不能直接使用res.getFile,因为jar里面的资源,getFile会抛异常 - File file = new File(res.getURL().getPath()); - if (!file.getParentFile().canWrite()) { - log.error("Can not output {},because can not write to directory of file {}", - MERGED_FILE, - res.getURL().getPath()); - return; - } - - File mergedfile = new File(res.getFile().getParentFile(), MERGED_FILE); - FileUtils.writeStringToFile(mergedfile, content); - log.info("Write merged log4j config file to {}", mergedfile.getAbsolutePath()); - } - - private static String genFileContext(List resList, Properties properties) throws IOException { - List> entryList = properties.entrySet() - .stream() - .sorted(new Comparator>() { - @Override - public int compare(Entry o1, Entry o2) { - return o1.getKey().toString().compareTo(o2.getKey().toString()); - } - }) - .collect(Collectors.toList()); - - StringBuilder sb = new StringBuilder(); - for (Resource res : resList) { - sb.append("#").append(res.getURL().getPath()).append("\n"); - } - for (Entry entry : entryList) { - sb.append(entry.getKey()).append("=").append(entry.getValue()).append("\n"); - } - return sb.toString(); - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/ReflectUtils.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/ReflectUtils.java deleted file mode 100644 index 24a8b57c224..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/ReflectUtils.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.utils; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - -import org.springframework.util.ReflectionUtils; - -public final class ReflectUtils { - private static final Field MODIFIERS_FIELD = - ReflectionUtils.findField(Field.class, "modifiers"); - - static { - MODIFIERS_FIELD.setAccessible(true); - } - - private ReflectUtils() { - - } - - public static void setField(Object instance, String fieldName, Object value) { - setField(instance.getClass(), instance, fieldName, value); - } - - public static void setField(Class cls, Object instance, String fieldName, Object value) { - Field field = ReflectionUtils.findField(cls, fieldName); - try { - if ((field.getModifiers() & Modifier.FINAL) != 0) { - MODIFIERS_FIELD.setInt(field, field.getModifiers() & ~Modifier.FINAL); - } - field.setAccessible(true); - field.set(instance, value); - } catch (Exception e) { - throw new Error(e); - } - } - - // 根据方法名,忽略参数查找method,调用此函数的前提是没有重载 - public static Method findMethod(Class cls, String methodName) { - for (Method method : cls.getMethods()) { - if (method.getName().equals(methodName)) { - return method; - } - } - - return null; - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/RollingFileAppenderExt.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/RollingFileAppenderExt.java deleted file mode 100644 index d4be8779f7d..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/RollingFileAppenderExt.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.utils; - -import java.io.File; -import java.io.IOException; - -import org.apache.log4j.RollingFileAppender; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.spi.LoggingEvent; - -/** - * 扩展增加2个功能:1. 设置文件权限;2. 文件删除后重建。 - */ -public class RollingFileAppenderExt extends RollingFileAppender { - private File currentFile; - - private boolean append; - - private String logPermission; - - public String getLogPermission() { - return logPermission; - } - - public void setLogPermission(String logPermission) { - this.logPermission = logPermission; - } - - @Override - public void setFile(String file) { - super.setFile(file); - } - - @Override - public void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) throws IOException { - this.append = append; - currentFile = new File(fileName); - createCurrentFile(); - super.setFile(fileName, append, bufferedIO, bufferSize); - } - - @Override - protected void subAppend(LoggingEvent event) { - // create a new file when file deleted - if (!currentFile.exists()) { - try { - setFile(fileName, append, bufferedIO, bufferSize); - } catch (IOException e) { - LogLog.error("", e); - } - } - super.subAppend(event); - } - - protected void createCurrentFile() throws IOException { - if (!currentFile.exists()) { - File parent = currentFile.getParentFile(); - if (parent != null && !parent.exists()) { - parent.mkdirs(); - } - currentFile.createNewFile(); - } - FilePerm.setFilePerm(currentFile, logPermission); - } -} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/SPIServiceUtils.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/SPIServiceUtils.java deleted file mode 100644 index 4ba9e80f63e..00000000000 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/SPIServiceUtils.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.utils; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.ServiceLoader; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.ReflectionUtils; - -/** - * SPI Service utils - * - * - */ -public final class SPIServiceUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(SPIServiceUtils.class); - - private SPIServiceUtils() { - - } - - /** - * get target service.if target services are array,only random access to a service. - */ - public static T getTargetService(Class serviceType) { - ServiceLoader loader = ServiceLoader.load(serviceType); - Iterator targetServices = loader.iterator(); - while (targetServices.hasNext()) { - T service = targetServices.next(); - LOGGER.info("get the SPI service success, the extend service is: {}", service.getClass()); - return service; - } - LOGGER.info("Can not get the SPI service, the interface type is: {}", serviceType.toString()); - return null; - } - - public static List getAllService(Class serviceType) { - List list = new ArrayList<>(); - ServiceLoader.load(serviceType).forEach(service -> { - list.add(service); - }); - return list; - } - - public static T getPriorityHighestService(Class serviceType) { - String methodName = "getOrder"; - Method getOrder = ReflectionUtils.findMethod(serviceType, methodName); - if (getOrder == null) { - throw new Error(String.format("method %s not exists in class %s", methodName, serviceType.getName())); - } - - int order = Integer.MAX_VALUE; - T highestService = null; - for (T service : getAllService(serviceType)) { - int serviceOrder = (int) ReflectionUtils.invokeMethod(getOrder, service); - if (serviceOrder <= order) { - order = serviceOrder; - highestService = service; - } - } - return highestService; - } -} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/Cipher.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/Cipher.java new file mode 100644 index 00000000000..3912ce38bda --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/Cipher.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.foundation.auth; + +public interface Cipher { + String name(); + + char[] decrypt(char[] encrypted); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/DefaultCipher.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/DefaultCipher.java new file mode 100644 index 00000000000..62cb24df37a --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/DefaultCipher.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.foundation.auth; + +public final class DefaultCipher implements Cipher { + public static final String CIPHER_NAME = "default"; + + private static final DefaultCipher INSTANCE = new DefaultCipher(); + + public static DefaultCipher getInstance() { + return INSTANCE; + } + + private DefaultCipher() { + } + + @Override + public String name() { + return CIPHER_NAME; + } + + @Override + public char[] decrypt(char[] encrypted) { + return encrypted; + } +} 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 new file mode 100644 index 00000000000..7faea74a3b5 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/FoundationCommonAuthConfiguration.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.foundation.auth; + +import org.springframework.context.annotation.Configuration; + +@Configuration +public class FoundationCommonAuthConfiguration { + public ShaAKSKCipher scbShaAKSKCipher() { + return new ShaAKSKCipher(); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/ShaAKSKCipher.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/ShaAKSKCipher.java new file mode 100644 index 00000000000..ee857e64464 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/ShaAKSKCipher.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.foundation.auth; + +public class ShaAKSKCipher implements Cipher { + public static final String CIPHER_NAME = "ShaAKSKCipher"; + + @Override + public String name() { + return CIPHER_NAME; + } + + @Override + public char[] decrypt(char[] encrypted) { + return encrypted; + } +} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/AbstractObjectManager.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/AbstractObjectManager.java similarity index 75% rename from foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/AbstractObjectManager.java rename to foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/AbstractObjectManager.java index 88651ab4dd5..3b19ca7b369 100644 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/AbstractObjectManager.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/AbstractObjectManager.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +15,7 @@ * limitations under the License. */ -package io.servicecomb.foundation.common; +package org.apache.servicecomb.foundation.common; import java.util.Collection; import java.util.Map; diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/CommonThread.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/CommonThread.java new file mode 100644 index 00000000000..7a4e97e522d --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/CommonThread.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.foundation.common; + +public class CommonThread extends Thread { + protected volatile boolean shutdown; + + public CommonThread() { + super(); + } + + public CommonThread(String name, long stackSize) { + super(null, null, name, stackSize); + } + + public boolean isShutdown() { + return shutdown; + } + + public boolean isRunning() { + return !shutdown; + } + + public void shutdown() { + shutdown = true; + interrupt(); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/DynamicObject.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/DynamicObject.java new file mode 100644 index 00000000000..0bdf703315f --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/DynamicObject.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.foundation.common; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; + +public class DynamicObject { + protected Map dynamic = new HashMap<>(); + + @JsonAnyGetter + public Map getDynamic() { + return dynamic; + } + + @SuppressWarnings("unchecked") + public T getDynamic(String key) { + return (T) dynamic.get(key); + } + + public void setDynamic(Map dynamic) { + this.dynamic = dynamic; + } + + @JsonAnySetter + public DynamicObject putDynamic(String key, Object value) { + this.dynamic.put(key, value); + return this; + } +} 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 new file mode 100644 index 00000000000..9e9752c6a30 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/Holder.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.foundation.common; + +// do not use javax.xml.ws.Holder, use this one. Because JDK 11 above do not have jakarta.ws.Holder +public final class Holder { + + /** + * The value contained in the holder. + */ + public T value; + + /** + * Creates a new holder with a null value. + */ + public Holder() { + } + + /** + * Create a new holder with the specified value. + * + * @param value The value to be stored in the holder. + */ + public Holder(T value) { + this.value = value; + } +} 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 new file mode 100644 index 00000000000..3b79c9c8e1a --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/NamedThreadFactory.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.foundation.common; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 已命名线程工厂. + * 获取新的名字有固定前缀的线程工厂. + */ +public class NamedThreadFactory implements ThreadFactory { + + private final AtomicInteger threadNumber = new AtomicInteger(); + + private String prefix; + + public NamedThreadFactory() { + this("Thread"); + } + + public NamedThreadFactory(String prefix) { + this.prefix = prefix; + } + + /** + * 获取新的名字以prefix为前缀的线程 + */ + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, prefix + "-" + threadNumber.getAndIncrement()); + thread.setDaemon(true); + return thread; + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } +} 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/RegisterManager.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/RegisterManager.java new file mode 100644 index 00000000000..80cf768a22d --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/RegisterManager.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; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RegisterManager { + private static final Logger LOGGER = LoggerFactory.getLogger(RegisterManager.class); + + private final String name; + + private String registerErrorFmt = "Not allow register repeat data, name=%s, key=%s"; + + private final Map objMap = new ConcurrentHashMap<>(); + + private final Object lockObj = new Object(); + + protected Map getObjMap() { + return objMap; + } + + public RegisterManager(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public String getRegisterErrorFmt() { + return registerErrorFmt; + } + + public void setRegisterErrorFmt(String registerErrorFmt) { + this.registerErrorFmt = registerErrorFmt; + } + + public void register(KEY key, VALUE value) { + synchronized (lockObj) { + if (objMap.get(key) != null) { + // 不允许重复注册 + String msg = String.format(registerErrorFmt, name, key); + LOGGER.error(msg); + + // 禁止启动 + throw new Error(msg); + } + objMap.put(key, value); + } + } + + public VALUE findValue(KEY key) { + return objMap.get(key); + } + + public VALUE ensureFindValue(KEY key) { + VALUE value = objMap.get(key); + if (value == null) { + String msg = String.format("Can not find value, name=%s, key=%s", name, key); + throw new Error(msg); + } + + return value; + } + + public Collection keys() { + return objMap.keySet(); + } + + public Collection values() { + return objMap.values(); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/VendorExtensions.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/VendorExtensions.java new file mode 100644 index 00000000000..270a883d0f0 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/VendorExtensions.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.foundation.common; + +import java.util.Map; +import java.util.function.Function; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; + +public class VendorExtensions { + private final Map store = new ConcurrentHashMapEx<>(); + + public Map getStore() { + return store; + } + + @SuppressWarnings("unchecked") + public V get(Object key) { + return (V) store.get(key); + } + + public void put(Object key, Object value) { + store.put(key, value); + } + + @SuppressWarnings("unchecked") + public V computeIfAbsent(Object key, Function mappingFunction) { + return (V) store.computeIfAbsent(key, mappingFunction); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/Version.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/Version.java new file mode 100644 index 00000000000..e09ad62e0ef --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/Version.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.foundation.common; + +import java.util.Objects; + +import org.apache.commons.lang3.ArrayUtils; + +// short version is enough +public class Version implements Comparable { + private static final String[] ZERO = new String[] {"0", "0", "0", "0"}; + + private final short major; + + private final short minor; + + private final short patch; + + private final short build; + + private final String version; + + private final long numberVersion; + + // 1 + // 1.0 + // 1.0.0 + // 1.0.0.0 + public Version(String version) { + Objects.requireNonNull(version); + + String[] versions = version.split("\\.", -1); + if (versions.length > 4) { + throw new IllegalStateException(String.format("Invalid version \"%s\".", version)); + } + + if (versions.length < 4) { + versions = ArrayUtils.addAll(versions, ZERO); + } + this.major = parseNumber("major", version, versions[0]); + this.minor = parseNumber("minor", version, versions[1]); + this.patch = parseNumber("patch", version, versions[2]); + this.build = parseNumber("build", version, versions[3]); + + this.version = combineStringVersion(); + this.numberVersion = combineVersion(); + } + + private short parseNumber(String name, String allVersion, String version) { + short value = 0; + try { + value = Short.parseShort(version); + } catch (NumberFormatException e) { + throw new IllegalStateException( + String.format("Invalid %s \"%s\", version \"%s\".", name, version, allVersion), e); + } + + if (value < 0) { + throw new IllegalStateException( + String.format("%s \"%s\" can not be negative, version \"%s\".", name, version, allVersion)); + } + + return value; + } + + public Version(short major, short minor, short patch, short build) { + this.major = major; + this.minor = minor; + this.patch = patch; + this.build = build; + this.version = combineStringVersion(); + this.numberVersion = combineVersion(); + } + + private String combineStringVersion() { + return major + "." + minor + "." + patch + "." + build; + } + + // 1.0.0 equals 1.0.0.0 + private long combineVersion() { + return (long) major << 48 | (long) minor << 32 | (long) patch << 16 | (long) build; + } + + public short getMajor() { + return major; + } + + public short getMinor() { + return minor; + } + + public short getPatch() { + return patch; + } + + public short getBuild() { + return build; + } + + public String getVersion() { + return version; + } + + @Override + public String toString() { + return version; + } + + @Override + public int hashCode() { + return version.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Version)) { + return false; + } + + return numberVersion == ((Version) other).numberVersion; + } + + @Override + public int compareTo(Version other) { + return Long.compare(numberVersion, other.numberVersion); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/DescriptiveRunnable.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/DescriptiveRunnable.java new file mode 100644 index 00000000000..825cdc70b8f --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/DescriptiveRunnable.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.foundation.common.base; + +public interface DescriptiveRunnable extends Runnable { + + String description(); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/DynamicEnum.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/DynamicEnum.java new file mode 100644 index 00000000000..982e913fefe --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/DynamicEnum.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.foundation.common.base; + +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonValue; + +public abstract class DynamicEnum { + private final T value; + + @JsonIgnore + private boolean dynamic = false; + + public DynamicEnum(T value) { + this.value = value; + } + + @JsonValue + public T getValue() { + return value; + } + + @JsonIgnore + public boolean isStatic() { + return !dynamic; + } + + public boolean isDynamic() { + return dynamic; + } + + public DynamicEnum setDynamic(boolean dynamic) { + this.dynamic = dynamic; + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DynamicEnum that = (DynamicEnum) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/DynamicEnumCache.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/DynamicEnumCache.java new file mode 100644 index 00000000000..74d27cdbc3a --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/DynamicEnumCache.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.common.base; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.ParameterizedType; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DynamicEnumCache> { + private static final Logger LOGGER = LoggerFactory.getLogger(DynamicEnumCache.class); + + private final Class cls; + + private final Map values; + + /** + * + * @param cls + */ + private final Constructor constructor; + + public DynamicEnumCache(Class cls) { + try { + this.cls = cls; + this.constructor = initFactory(); + this.values = initValues(); + } catch (Exception e) { + throw new IllegalStateException("failed to init dynamic enum, class=" + cls.getName(), e); + } + } + + private Constructor initFactory() throws NoSuchMethodException { + ParameterizedType superClass = (ParameterizedType) cls.getGenericSuperclass(); + Class argument = (Class) superClass.getActualTypeArguments()[0]; + return cls.getConstructor(argument); + } + + @SuppressWarnings("unchecked") + private Map initValues() { + Map values = new LinkedHashMap<>(); + EnumUtils.findEnumFields(cls) + .map(field -> (T) EnumUtils.readEnum(field)) + .forEach(oneEnum -> values.put(oneEnum.getValue(), oneEnum)); + return Collections.unmodifiableMap(values); + } + + public T fromValue(Object value) { + if (value == null) { + return null; + } + + T instance = values.get(value); + if (instance != null) { + return instance; + } + + try { + // do not cache unknown value to avoid attach + // if need to cache unknown value, should avoid cache too many values + instance = constructor.newInstance(value); + instance.setDynamic(true); + return instance; + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + LOGGER.error("failed to create enum, class={}, value={}.", cls.getName(), value); + return null; + } + } +} 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 new file mode 100644 index 00000000000..b25bcfa1504 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/EnumUtils.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.foundation.common.base; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.stream.Stream; + +public interface EnumUtils { + @SuppressWarnings("unchecked") + static T readEnum(Field enumField) { + try { + return (T) enumField.get(null); + } catch (IllegalAccessException e) { + throw new IllegalStateException("failed to read enum, field=" + enumField, e); + } + } + + static boolean isEnumField(Class cls, Field field) { + return Modifier.isStatic(field.getModifiers()) && cls.equals(field.getType()); + } + + static Stream findEnumFields(Class cls) { + return Arrays.stream(cls.getFields()) + .filter(field -> isEnumField(cls, field)); + } + + 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 new file mode 100644 index 00000000000..aedae7c1a86 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/ServiceCombConstants.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.foundation.common.base; + +public interface ServiceCombConstants { + String CONFIG_TRACING_COLLECTOR_ADDRESS = "servicecomb.tracing.collector.address"; + + String CONFIG_TRACING_COLLECTOR_API_V1 = "v1"; + + String CONFIG_TRACING_COLLECTOR_API_V2 = "v2"; + + String CONFIG_TRACING_COLLECTOR_API_VERSION = "servicecomb.tracing.collector.apiVersion"; + + String CONFIG_TRACING_COLLECTOR_PATH = "/api/{0}/spans"; + + String DEFAULT_TRACING_COLLECTOR_ADDRESS = "http://127.0.0.1:9411"; + + String CONFIG_KEY_SPLITTER = "_"; + + String CONFIG_FRAMEWORK_DEFAULT_NAME = "servicecomb-java-chassis"; + + String CONFIG_DEFAULT_REGISTER_BY = "SDK"; + + String DEVELOPMENT_SERVICECOMB_ENV = "development"; + + String SERVICE_MAPPING = "SERVICE_MAPPING"; + + String VERSION_MAPPING = "VERSION_MAPPING"; + + String APP_MAPPING = "APP_MAPPING"; +} 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 new file mode 100644 index 00000000000..b30e13862b4 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/cache/VersionedCache.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cache; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; + +@SuppressWarnings("unchecked") +public class VersionedCache { + protected static final AtomicInteger VERSION = new AtomicInteger(); + + interface IsEmpty { + boolean isEmpty(); + } + + protected volatile int cacheVersion; + + // an optional name + protected String name; + + protected Object data; + + protected IsEmpty isEmpty = this::isCommonEmpty; + + private boolean isCommonEmpty() { + return data == null; + } + + private boolean isMapEmpty() { + return ((Map) data).isEmpty(); + } + + private boolean isCollectionEmpty() { + return ((Collection) data).isEmpty(); + } + + private boolean isArrayEmpty() { + return ((Object[]) data).length == 0; + } + + public int cacheVersion() { + return cacheVersion; + } + + public T autoCacheVersion() { + this.cacheVersion = VERSION.incrementAndGet(); + return (T) this; + } + + public T cacheVersion(int cacheVersion) { + this.cacheVersion = cacheVersion; + return (T) this; + } + + public String name() { + return name; + } + + public T name(String name) { + this.name = name; + return (T) this; + } + + public T subName(VersionedCache parent, String subName) { + Objects.requireNonNull(parent.name()); + Objects.requireNonNull(subName); + + this.name = parent.name + "/" + subName; + return (T) this; + } + + + public T data() { + return (T) data; + } + + // caller maker sure data is a map + public Map mapData() { + return (Map) data; + } + + // caller maker sure data is a collection + public Collection collectionData() { + return (Collection) data; + } + + //caller maker sure data is a array + public T[] arrayData() { + return (T[]) data; + } + + public T data(Object data) { + this.data = data; + + if (data == null) { + isEmpty = this::isCommonEmpty; + } else if (Map.class.isInstance(data)) { + isEmpty = this::isMapEmpty; + } else if (Collection.class.isInstance(data)) { + isEmpty = this::isCollectionEmpty; + } else if (data.getClass().isArray()) { + isEmpty = this::isArrayEmpty; + } else { + isEmpty = this::isCommonEmpty; + } + return (T) this; + } + + // only newData is new than this, means expired. + public boolean isExpired(VersionedCache newCache) { + // cacheVersion maybe overflow, so must use decrease to determine + return newCache.cacheVersion - cacheVersion > 0; + } + + public boolean isSameVersion(VersionedCache newCache) { + return newCache.cacheVersion == cacheVersion; + } + + public boolean isEmpty() { + return isEmpty.isEmpty(); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/concurrency/SuppressedRunnableWrapper.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/concurrency/SuppressedRunnableWrapper.java new file mode 100644 index 00000000000..edd3ba2f845 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/concurrency/SuppressedRunnableWrapper.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.foundation.common.concurrency; + +import java.util.function.Consumer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A wrapper for {@link Runnable}. + * All the exceptions thrown from {@link Runnable#run()} are caught and handled. + */ +public class SuppressedRunnableWrapper implements Runnable { + + private static final Logger LOGGER = LoggerFactory.getLogger(SuppressedRunnableWrapper.class); + + private final Runnable target; + + private final Consumer errorHandler; + + public SuppressedRunnableWrapper(Runnable runnable) { + this(runnable, null); + } + + public SuppressedRunnableWrapper(Runnable runnable, Consumer errorHandler) { + this.target = runnable; + this.errorHandler = errorHandler; + } + + @Override + public void run() { + try { + target.run(); + } catch (Throwable e) { + handleError(e); + } + } + + private void handleError(Throwable e) { + if (null == errorHandler) { + LOGGER.error("task {} execute error!", target, e); + return; + } + errorHandler.accept(e); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/concurrent/ConcurrentHashMapEx.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/concurrent/ConcurrentHashMapEx.java new file mode 100644 index 00000000000..ac6e1ba44e8 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/concurrent/ConcurrentHashMapEx.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.foundation.common.concurrent; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +public class ConcurrentHashMapEx extends ConcurrentHashMap { + private static final long serialVersionUID = -7753722464102569902L; + + public ConcurrentHashMapEx() { + super(); + } + + public ConcurrentHashMapEx(int initialCapacity) { + super(initialCapacity); + } + + public ConcurrentHashMapEx(Map m) { + super(m); + } + + public ConcurrentHashMapEx(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + } + + public ConcurrentHashMapEx(int initialCapacity, + float loadFactor, int concurrencyLevel) { + super(initialCapacity, loadFactor, concurrencyLevel); + } + + // ConcurrentHashMap.computeIfAbsent always do "synchronized" operation + // so we wrap it to improve performance + @Override + public V computeIfAbsent(K key, Function mappingFunction) { + V value = get(key); + if (value != null) { + return value; + } + + return super.computeIfAbsent(key, mappingFunction); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/ConfigLoader.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/ConfigLoader.java new file mode 100644 index 00000000000..69826094ee8 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/ConfigLoader.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.foundation.common.config; + +public interface ConfigLoader { + + T load() throws Exception; +} 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 new file mode 100644 index 00000000000..fe11a3fe763 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/PaaSResourceUtils.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.config; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.common.config.impl.PropertiesLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; + + +public class PaaSResourceUtils extends org.springframework.util.ResourceUtils { + public static final String PROPERTIES_SUFFIX = ".properties"; + + private static final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); + + /** + * 失败,则返回空数组 + */ + public static Resource[] getResources(String locationPattern) { + try { + return resourcePatternResolver.getResources(locationPattern); + } catch (IOException e) { + return new Resource[0]; + } + } + + /** + * 失败,则返回空列表 + */ + public static List getResources(String... locationPatterns) { + List ret = new ArrayList<>(); + for (String locationPattern : locationPatterns) { + Resource[] resArr = getResources(locationPattern); + ret.addAll(Arrays.asList(resArr)); + } + + return ret; + } + + /** + * 文件同名时,jar的优先级比较低 + * jar: xxx.xml + * jar: xxx.model.xml + * file:xxx.xml + * 调用者保证,所有res的后缀都是suffix + * file文件应该只有一个,因为放在目录中的配置文件,应该是最终的部署定制文件 + * 此时,还分多个,是不合适的 + */ + public static void sortResources(List resList, String suffix) { + resList.sort((o1, o2) -> { + try { + // jar的优先级比较低 + if (isJarURL(o1.getURL()) && isFileURL(o2.getURL())) { + return -1; + } + + // 干掉后缀,再排序 + String name1 = o1.getFilename(); + String name2 = o2.getFilename(); + + //Resource.getFilename接口会返回null,当路径的文件名不存在时 + //配置文件一定存在,并且Resource有合法的文件名,name1和name2不可能为空, + //这里做判断是为了处理CodeDEX报的null引用 + if (StringUtils.isEmpty(name1) || StringUtils.isEmpty(name2)) { + throw new IOException( + String.format("Resource %s or %s is not a file", o1.getURI(), o2.getURI())); + } + + name1 = name1.substring(0, name1.length() - suffix.length()); + name2 = name2.substring(0, name2.length() - suffix.length()); + + return name1.compareTo(name2); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + + public static void sortProperties(List resList) { + sortResources(resList, PROPERTIES_SUFFIX); + } + + public static List getSortedResources(String locationPattern, String suffix) { + if (StringUtils.isEmpty(locationPattern)) { + throw new RuntimeException("Resource path must not be null or empty"); + } + + if (!locationPattern.endsWith(suffix)) { + throw new RuntimeException("Resource path must ends with " + suffix); + } + + String prefix = locationPattern.substring(0, locationPattern.length() - suffix.length()); + + List resList = PaaSResourceUtils.getResources(locationPattern, prefix + ".*" + suffix); + sortResources(resList, suffix); + return resList; + } + + public static List getSortedProperties(String locationPattern) { + return getSortedResources(locationPattern, PROPERTIES_SUFFIX); + } + + public static Properties loadMergedProperties(String locationPattern) throws Exception { + PropertiesLoader loader = new PropertiesLoader(Arrays.asList(locationPattern)); + return loader.load(); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/AbstractLoader.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/AbstractLoader.java new file mode 100644 index 00000000000..c534f4e23a3 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/AbstractLoader.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.foundation.common.config.impl; + +import java.util.List; + +import org.apache.servicecomb.foundation.common.config.ConfigLoader; + +public abstract class AbstractLoader implements ConfigLoader { + protected List locationPatternList; + + public AbstractLoader(List locationPatternList) { + this.locationPatternList = locationPatternList; + } + + public List getLocationPatternList() { + return locationPatternList; + } +} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/PaaSPropertiesLoaderUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PaaSPropertiesLoaderUtils.java similarity index 75% rename from foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/PaaSPropertiesLoaderUtils.java rename to foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PaaSPropertiesLoaderUtils.java index bd6c90a75ee..a12eca5ae3a 100644 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/PaaSPropertiesLoaderUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PaaSPropertiesLoaderUtils.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,16 +15,16 @@ * limitations under the License. */ -package io.servicecomb.foundation.common.config.impl; +package org.apache.servicecomb.foundation.common.config.impl; import java.io.IOException; import java.util.List; import java.util.Properties; +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.common.config.PaaSResourceUtils; import org.springframework.core.io.Resource; -import org.springframework.util.StringUtils; -import io.servicecomb.foundation.common.config.PaaSResourceUtils; public class PaaSPropertiesLoaderUtils extends org.springframework.core.io.support.PropertiesLoaderUtils { 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 new file mode 100644 index 00000000000..ae4500e8aef --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PropertiesLoader.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.common.config.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.apache.servicecomb.foundation.common.config.PaaSResourceUtils; +import org.springframework.core.io.Resource; + +public class PropertiesLoader extends AbstractLoader { + private final List foundResList = new ArrayList<>(); + + public PropertiesLoader(List locationPatternList) { + super(locationPatternList); + } + + public List getFoundResList() { + return foundResList; + } + + @SuppressWarnings("unchecked") + @Override + public T load() throws Exception { + Properties props = new Properties(); + for (String locationPattern : locationPatternList) { + List resList = PaaSResourceUtils.getSortedProperties(locationPattern); + foundResList.addAll(resList); + PaaSPropertiesLoaderUtils.fillAllProperties(props, resList); + } + + return (T) props; + } +} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/XmlLoaderUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/XmlLoaderUtils.java similarity index 77% rename from foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/XmlLoaderUtils.java rename to foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/XmlLoaderUtils.java index 42f6ae4a124..8d2a4b98c1e 100644 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/config/impl/XmlLoaderUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/XmlLoaderUtils.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +15,7 @@ * limitations under the License. */ -package io.servicecomb.foundation.common.config.impl; +package org.apache.servicecomb.foundation.common.config.impl; import java.io.IOException; import java.net.URL; @@ -23,6 +24,7 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.apache.servicecomb.foundation.common.utils.FortifyUtils; import org.springframework.core.io.Resource; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -32,13 +34,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import io.servicecomb.foundation.common.utils.FortifyUtils; - public final class XmlLoaderUtils { private XmlLoaderUtils() { } - private static ObjectMapper xmlMapper = new XmlMapper(); + private static final ObjectMapper xmlMapper = new XmlMapper(); @SuppressWarnings("unchecked") public static T load(Resource res, Class cls) throws Exception { diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/encrypt/Encryption.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/encrypt/Encryption.java new file mode 100644 index 00000000000..99be3c47b4e --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/encrypt/Encryption.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.foundation.common.encrypt; + +/** + * Interface for users to encode/decode confidential data + */ +public interface Encryption { + default int getOrder() { + return 0; + } + + /** + * decode confidential data + * @param encrypted encrypted data + * @param tags extra information used to do something + * @return plain data + */ + char[] decode(char[] encrypted, String tags); + + /** + * + * @param plain plain data + * @param tags extra information used to do something + * @return encrypted data + */ + char[] encode(char[] plain, String tags); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/encrypt/Encryptions.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/encrypt/Encryptions.java new file mode 100644 index 00000000000..8039532e29f --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/encrypt/Encryptions.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.foundation.common.encrypt; + +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; + +import com.google.common.annotations.VisibleForTesting; + +public class Encryptions { + private static Encryption encryption = SPIServiceUtils.getPriorityHighestService(Encryption.class); + + @VisibleForTesting + static void setEncryption(Encryption encryption) { + Encryptions.encryption = encryption; + } + + @VisibleForTesting + static Encryption getEncryption() { + return encryption; + } + + public static String decode(String encrypted, String tags) { + if (encrypted == null) { + return null; + } + char[] result = decode(encrypted.toCharArray(), tags); + if (result == null) { + return null; + } + return new String(result); + } + + public static char[] decode(char[] encrypted, String tags) { + return encryption.decode(encrypted, tags); + } + + public static String encode(String plain, String tags) { + if (plain == null) { + return null; + } + char[] result = encode(plain.toCharArray(), tags); + if (result == null) { + return null; + } + return new String(result); + } + + public static char[] encode(char[] plain, String tags) { + return encryption.encode(plain, tags); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/encrypt/NoEncryption.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/encrypt/NoEncryption.java new file mode 100644 index 00000000000..f3f23c27061 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/encrypt/NoEncryption.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.foundation.common.encrypt; + +public class NoEncryption implements Encryption { + @Override + public int getOrder() { + return 100; + } + + @Override + public char[] decode(char[] encrypted, String tags) { + return encrypted; + } + + @Override + public char[] encode(char[] plain, String tags) { + return plain; + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/AlarmEvent.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/AlarmEvent.java new file mode 100644 index 00000000000..3539764406d --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/AlarmEvent.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.foundation.common.event; + +public class AlarmEvent { + + Type type; + + public AlarmEvent(Type type) { + this.type = type; + } + + public Type getType() { + return this.type; + } + + public enum Type { + OPEN, + CLOSE + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EnableExceptionPropagation.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EnableExceptionPropagation.java new file mode 100644 index 00000000000..744ebb94a26 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EnableExceptionPropagation.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.foundation.common.event; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * do not suppress exception + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface EnableExceptionPropagation { +} 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 new file mode 100644 index 00000000000..50b474050d9 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.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.foundation.common.event; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.google.common.eventbus.EventBus; + +/** + * EventManager for chassis events + * + */ +@Configuration +public class EventManager { + public static EventBus eventBus = new SimpleEventBus(); + + public static EventBus getEventBus() { + return eventBus; + } + + /** + * Registering listener. + */ + public static void register(Object listener) { + eventBus.register(listener); + } + + /** + * post event. + */ + public static void post(Object event) { + eventBus.post(event); + } + + /** + * Unregistering listener. + */ + public static void unregister(Object listener) { + eventBus.unregister(listener); + } + + @Bean + public EventBus scbEventBus() { + return eventBus; + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/SimpleEventBus.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/SimpleEventBus.java new file mode 100644 index 00000000000..49602a2de58 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/SimpleEventBus.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.event; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.reflect.MethodUtils; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; + +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + +/** + * for register/unregister rarely + */ +public class SimpleEventBus extends EventBus { + private final Map> subscribersMap = new ConcurrentHashMapEx<>(); + + // key is event class + private Map, List> subscribersCache = new ConcurrentHashMapEx<>(); + + private List collectSubscribers(Object instance) { + List subscribers = new ArrayList<>(); + Method[] methods = MethodUtils.getMethodsWithAnnotation(instance.getClass(), Subscribe.class, true, true); + for (Method method : methods) { + SimpleSubscriber subscriber = new SimpleSubscriber(instance, method); + subscribers.add(subscriber); + } + return subscribers; + } + + @Override + public void register(Object instance) { + subscribersMap.computeIfAbsent(instance, this::collectSubscribers); + // even ignored cause of duplicate register + // still reset cache + // this makes logic simpler + subscribersCache = new ConcurrentHashMapEx<>(); + } + + @Override + public void unregister(Object instance) { + if (subscribersMap.remove(instance) != null) { + subscribersCache = new ConcurrentHashMapEx<>(); + } + } + + @Override + public void post(Object event) { + // cache always reset after register/unregister + // so cache always match latest subscribersMap at last + // the worst scenes is invoke collectSubscriberForEvent multiple times, no problem + List subscribers = subscribersCache + .computeIfAbsent(event.getClass(), this::collectSubscriberForEvent); + for (SimpleSubscriber subscriber : subscribers) { + subscriber.dispatchEvent(event); + } + } + + /** + * subscribersMap almost stable
+ * so we not care for performance of collectSubscriberForEvent + * @param eventClass + */ + private List collectSubscriberForEvent(Class eventClass) { + List subscribersForEvent = new ArrayList<>(); + for (List subscribers : subscribersMap.values()) { + for (SimpleSubscriber subscriber : subscribers) { + if (subscriber.getMethod().getParameterTypes()[0].isAssignableFrom(eventClass)) { + subscribersForEvent.add(subscriber); + } + } + } + + subscribersForEvent.sort(Comparator.comparingInt(SimpleSubscriber::getOrder)); + return subscribersForEvent; + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/SimpleSubscriber.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/SimpleSubscriber.java new file mode 100644 index 00000000000..990ca304dcf --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/SimpleSubscriber.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.foundation.common.event; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.function.Consumer; + +import org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.AllowConcurrentEvents; + +public class SimpleSubscriber { + private static final Logger LOGGER = LoggerFactory.getLogger(SimpleSubscriber.class); + + private final Object instance; + + private final Method method; + + private int order; + + private final boolean enableExceptionPropagation; + + // generated from method + private Consumer lambda; + + private Consumer dispatcher; + + public SimpleSubscriber(Object instance, Method method) { + this.instance = instance; + this.method = method; + + enableExceptionPropagation = method.getAnnotation(EnableExceptionPropagation.class) != null; + SubscriberOrder subscriberOrder = method.getAnnotation(SubscriberOrder.class); + if (subscriberOrder != null) { + order = subscriberOrder.value(); + } + + try { + lambda = LambdaMetafactoryUtils.createLambda(instance, method, Consumer.class); + } catch (Throwable throwable) { + // because enhance LambdaMetafactoryUtils to support ALL_MODES by reflect + // never run into this branch. + // otherwise create a listener instance of anonymous class will run into this branch + LOGGER.warn("Failed to create lambda for method: {}, fallback to reflect.", method, throwable); + + checkAccess(method); + lambda = event -> { + try { + method.invoke(instance, event); + } catch (Throwable e) { + LOGGER.warn("Failed to call event listener {}.", method.getName()); + throw new IllegalStateException(e); + } + }; + } + + dispatcher = this::syncDispatch; + if (method.getAnnotation(AllowConcurrentEvents.class) != null) { + dispatcher = this::concurrentDispatch; + } + } + + private static void checkAccess(Method method) { + // This check is not accurate. Most of time package visible and protected access can be ignored, so simply do this. + if (!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) { + throw new IllegalStateException( + String.format( + "The event handler must be a public accessible method. NOTICE: " + + "this is change from 2.0 and using higher version of JDK. " + + "Declaring class is %s, method is %s", + method.getDeclaringClass().getName(), + method.getName())); + } + } + + public Object getInstance() { + return instance; + } + + public Method getMethod() { + return method; + } + + public int getOrder() { + return order; + } + + public void dispatchEvent(Object event) { + try { + dispatcher.accept(event); + } catch (Throwable e) { + if (enableExceptionPropagation) { + throw e; + } + LOGGER.error("Event process should not throw exception when @EnableExceptionPropagation not set. ", e); + } + } + + private void syncDispatch(Object event) { + synchronized (this) { + lambda.accept(event); + } + } + + private void concurrentDispatch(Object event) { + lambda.accept(event); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/SubscriberOrder.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/SubscriberOrder.java new file mode 100644 index 00000000000..2232ac5dd99 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/SubscriberOrder.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.foundation.common.event; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * order of subscriber
+ * lower values will run first + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface SubscriberOrder { + int value() default 0; +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/exceptions/ServiceCombException.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/exceptions/ServiceCombException.java new file mode 100644 index 00000000000..27fd731bc3d --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/exceptions/ServiceCombException.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.foundation.common.exceptions; + +public class ServiceCombException extends RuntimeException { + private static final long serialVersionUID = -1085233183289520695L; + + public ServiceCombException(String cause, Throwable throwable) { + super(cause, throwable); + } + + public ServiceCombException(String cause) { + super(cause); + } +} 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 new file mode 100644 index 00000000000..45806dcc53f --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatus.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.foundation.common.http; + +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) { + return Status.Family.SUCCESSFUL.equals(Status.Family.familyOf(code)); + } + + public static boolean isSuccess(StatusType status) { + return Status.Family.SUCCESSFUL.equals(status.getFamily()); + } + + private final int statusCode; + + private final String reason; + + public HttpStatus(final int statusCode, final String reasonPhrase) { + this.statusCode = statusCode; + this.reason = reasonPhrase; + } + + @Override + public int getStatusCode() { + return statusCode; + } + + @Override + public Family getFamily() { + return Family.familyOf(statusCode); + } + + @Override + public String getReasonPhrase() { + return reason; + } +} 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 new file mode 100644 index 00000000000..8a995f74389 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatusManager.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.foundation.common.http; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.StatusType; + +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; + +public class HttpStatusManager { + private final Map statusMap = new ConcurrentHashMap<>(); + + public HttpStatusManager() { + for (Status status : Status.values()) { + statusMap.put(status.getStatusCode(), status); + } + + SPIServiceUtils.getAllService(StatusType.class).forEach(this::addStatusType); + } + + public void addStatusType(StatusType status) { + if (statusMap.containsKey(status.getStatusCode())) { + throw new IllegalStateException("repeated status code: " + status.getStatusCode()); + } + + statusMap.put(status.getStatusCode(), status); + } + + public StatusType getOrCreateByStatusCode(int code) { + StatusType statusType = statusMap.get(code); + if (statusType != null) { + return statusType; + } + + statusType = new HttpStatus(code, ""); + addStatusType(statusType); + + return statusType; + } +} 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 new file mode 100644 index 00000000000..3c4693c9eab --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpStatusUtils.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.http; + +import jakarta.ws.rs.core.Response.StatusType; + +public final class HttpStatusUtils { + private static final HttpStatusManager MGR = new HttpStatusManager(); + + private HttpStatusUtils() { + } + + public static StatusType getOrCreateByStatusCode(int code) { + return MGR.getOrCreateByStatusCode(code); + } +} 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 new file mode 100644 index 00000000000..626cb136030 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/http/HttpUtils.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.http; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; + + +import com.google.common.net.UrlEscapers; +import org.apache.commons.lang3.StringUtils; + +public final class HttpUtils { + private HttpUtils() { + } + + /** + * paramName is not case sensitive + * @param headerValue example: attachment;filename=a.txt + * + */ + public static String parseParamFromHeaderValue(String headerValue, String paramName) { + if (StringUtils.isEmpty(headerValue)) { + return null; + } + + for (String value : headerValue.split(";")) { + int idx = value.indexOf('='); + if (idx == -1) { + continue; + } + if (paramName.equalsIgnoreCase(value.substring(0, idx).trim())) { + return value.substring(idx + 1).replaceAll("\"", "").trim(); + } + } + return null; + } + + /** + *
+   *          foo://example.com:8042/over/there?name=ferret#nose
+   *          \_/   \______________/\_________/ \_________/ \__/
+   *           |           |            |            |        |
+   *        scheme     authority       path        query   fragment
+   *           |   _____________________|__
+   *          / \ /                        \
+   *          urn:example:animal:ferret:nose
+   * 
+ *

the URI syntax components above is referred from RFC3986. + * This method is used to encode the entire path part(e.g. /over/there in the example).

+ * In order to keep the structure of path, slash '/' will not be encoded. If you want to encode '/' into {@code %2F}, + * please consider the {@link #encodePathParam(String)} + * + * + * @param path the entire url path + * @return the encoded url path + */ + public static String uriEncodePath(String path) { + try { + URI uri = new URI(null, null, path, null); + return uri.toASCIIString(); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(String.format("uriEncode failed, path=\"%s\".", path), e); + } + } + + /** + * Encode path params. For example, if the path of an operation is {@code /over/there/{pathParam}/tail}, this method + * should be used to encoded {@code {pathParam}}. In order to keep the path structure, the slash '/' will be encoded + * into {@code %2F} to avoid path matching problem. + * + * @see UrlEscapers#urlPathSegmentEscaper() + * + * @param pathParam the path param to be encoded + * @return the encoded path param + */ + public static String encodePathParam(String pathParam) { + return UrlEscapers.urlPathSegmentEscaper().escape(pathParam); + } + + public static String uriDecodePath(String path) { + if (path == null) { + return null; + } + + try { + return new URI(path).getPath(); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(String.format("uriDecode failed, path=\"%s\".", path), e); + } + } + + /** + * only used by SDK to download from serviceComb producer
+ * no need to check rtf6266's "filename*" rule. + */ + public static String parseFileNameFromHeaderValue(String headerValue) { + String fileName = parseParamFromHeaderValue(headerValue, "filename"); + fileName = StringUtils.isEmpty(fileName) ? "default" : fileName; + fileName = uriDecodePath(fileName); + return new File(fileName).getName(); + } + + /** + * Parse the character encoding from the specified content type header. + * If the content type is null, or there is no explicit character encoding, + * null is returned. + * + * @param contentType a content type header + */ + public static String getCharsetFromContentType(String contentType) { + if (contentType == null) { + return null; + } + int start = contentType.indexOf("charset="); + if (start < 0) { + return null; + } + String encoding = contentType.substring(start + 8); + int end = encoding.indexOf(';'); + if (end >= 0) { + encoding = encoding.substring(0, end); + } + encoding = encoding.trim(); + if ((encoding.length() > 2) && (encoding.startsWith("\"")) + && (encoding.endsWith("\""))) { + encoding = encoding.substring(1, encoding.length() - 1); + } + return encoding.trim(); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/io/AsyncCloseable.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/io/AsyncCloseable.java new file mode 100644 index 00000000000..a33756586b9 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/io/AsyncCloseable.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.foundation.common.io; + +import java.util.concurrent.CompletableFuture; + +public interface AsyncCloseable { + CompletableFuture close(); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/log/AbstractMarker.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/log/AbstractMarker.java new file mode 100644 index 00000000000..15128c19bce --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/log/AbstractMarker.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.foundation.common.log; + +import java.util.Collections; +import java.util.Iterator; + +import org.slf4j.Marker; + +public abstract class AbstractMarker implements Marker { + private static final long serialVersionUID = -1L; + + @Override + public void add(Marker reference) { + + } + + @Override + public boolean remove(Marker reference) { + return false; + } + + @Deprecated + @Override + public boolean hasChildren() { + return false; + } + + @Override + public boolean hasReferences() { + return false; + } + + @SuppressWarnings("unchecked") + @Override + public Iterator iterator() { + return Collections.EMPTY_LIST.iterator(); + } + + @Override + public boolean contains(Marker other) { + return false; + } + + @Override + public boolean contains(String name) { + return false; + } + + @Override + public String toString() { + return getName(); + } +} 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 new file mode 100644 index 00000000000..66aae3ebca7 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/IpPort.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.net; + +import java.net.InetSocketAddress; + +import com.google.common.base.Objects; + +public class IpPort { + private volatile String hostOrIp; + + private volatile int port; + + private volatile InetSocketAddress socketAddress; + + private final Object lock = new Object(); + + public IpPort() { + + } + + public IpPort(String hostOrIp, int port) { + this.hostOrIp = hostOrIp; + this.port = port; + } + + public String getHostOrIp() { + return hostOrIp; + } + + public void setHostOrIp(String hostOrIp) { + this.hostOrIp = hostOrIp; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + @Override + public int hashCode() { + return Objects.hashCode(port, hostOrIp); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + IpPort ipPort = (IpPort) o; + + if (port != ipPort.port) { + return false; + } + return hostOrIp.equals(ipPort.hostOrIp); + } + + @Override + public String toString() { + return hostOrIp + ":" + port; + } + + public InetSocketAddress getSocketAddress() { + if (socketAddress == null) { + synchronized (lock) { + if (socketAddress == null) { + InetSocketAddress tmpSocketAddress = new InetSocketAddress(hostOrIp, port); + socketAddress = tmpSocketAddress; + } + } + } + + return 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 new file mode 100644 index 00000000000..fd8cc1f7a29 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java @@ -0,0 +1,346 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.net; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.ServerSocket; +import java.net.SocketException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; + +public final class NetUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(NetUtils.class); + + private static final String IPV4_KEY = "_v4"; + + private static final String IPV6_KEY = "_v6"; + + private static final String PREFERRED_INTERFACE = "eth"; + + // one interface can bind to multiple address + // we only save one ip for each interface name. + // eg: + // 1. eth0 -> ip1 ip2 + // last data is eth0 -> ip2 + // 2. eth0 -> ip1 + // eth0:0 -> ip2 + // eth0:1 -> ip3 + // on interface name conflict, all data saved + + // key is network interface name and type + private static Map allInterfaceAddresses = new HashMap<>(); + + private static String hostName; + + private static String hostAddress; + + private static String hostAddressIpv6; + + static { + doGetHostNameAndHostAddress(); + } + + private static void doGetHostNameAndHostAddress() { + try { + doGetAddressFromNetworkInterface(); + // getLocalHost will throw exception in some docker image and sometimes will do a hostname lookup and time consuming + InetAddress localHost = InetAddress.getLocalHost(); + hostName = localHost.getHostName(); + LOGGER.info("localhost hostName={}, hostAddress={}.", hostName, localHost.getHostAddress()); + + if (!isLocalAddress(localHost)) { + if (Inet6Address.class.isInstance(localHost)) { + hostAddressIpv6 = trimIpv6(localHost.getHostAddress()); + hostAddress = tryGetHostAddressFromNetworkInterface(false, localHost); + LOGGER.info("Host address info ipV4={}, ipV6={}.", hostAddress, hostAddressIpv6); + return; + } + hostAddress = localHost.getHostAddress(); + hostAddressIpv6 = trimIpv6(tryGetHostAddressFromNetworkInterface(true, localHost)); + LOGGER.info("Host address info ipV4={}, ipV6={}.", hostAddress, hostAddressIpv6); + return; + } + hostAddressIpv6 = trimIpv6(tryGetHostAddressFromNetworkInterface(true, localHost)); + hostAddress = tryGetHostAddressFromNetworkInterface(false, localHost); + LOGGER.info("Host address info ipV4={}, ipV6={}.", hostAddress, hostAddressIpv6); + } catch (Exception e) { + LOGGER.error("got exception when trying to get addresses:", e); + if (allInterfaceAddresses.size() >= 1) { + InetAddress entry = allInterfaceAddresses.entrySet().iterator().next().getValue(); + // get host name will do a reverse name lookup and is time consuming + hostName = entry.getHostName(); + hostAddress = entry.getHostAddress(); + LOGGER.info("add host name from interfaces:" + hostName + ",host address:" + hostAddress); + } + } + } + + private static String tryGetHostAddressFromNetworkInterface(boolean isIpv6, InetAddress localhost) { + InetAddress result = null; + for (Entry entry : allInterfaceAddresses.entrySet()) { + if (isIpv6 && entry.getKey().endsWith(IPV6_KEY)) { + result = entry.getValue(); + if (entry.getKey().startsWith(PREFERRED_INTERFACE)) { + return result.getHostAddress(); + } + } else if (!isIpv6 && entry.getKey().endsWith(IPV4_KEY)) { + result = entry.getValue(); + if (entry.getKey().startsWith(PREFERRED_INTERFACE)) { + return result.getHostAddress(); + } + } + } + + if (result == null) { + return localhost.getHostAddress(); + } + + return result.getHostAddress(); + } + + private NetUtils() { + } + + /** + * docker环境中,有时无法通过InetAddress.getLocalHost()获取 ,会报unknown host Exception, system error + * 此时,通过遍历网卡接口的方式规避,出来的数据不一定对 + */ + private static void doGetAddressFromNetworkInterface() throws SocketException { + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + + while (networkInterfaces.hasMoreElements()) { + NetworkInterface network = networkInterfaces.nextElement(); + + if (!network.isUp() || network.isLoopback() || network.isVirtual()) { + continue; + } + + Enumeration addresses = network.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress address = addresses.nextElement(); + + if (isLocalAddress(address)) { + continue; + } + + if (address instanceof Inet4Address) { + LOGGER.info("add ipv4 network interface:" + network.getName() + ",host address:" + address.getHostAddress()); + allInterfaceAddresses.put(network.getName() + IPV4_KEY, address); + } else if (address instanceof Inet6Address) { + LOGGER.info("add ipv6 network interface:" + network.getName() + ",host address:" + address.getHostAddress()); + allInterfaceAddresses.put(network.getName() + IPV6_KEY, address); + } + } + } + } + + private static String trimIpv6(String hostAddress) { + int index = hostAddress.indexOf("%"); + if (index >= 0) { + return hostAddress.substring(0, index); + } + return hostAddress; + } + + private static boolean isLocalAddress(InetAddress address) { + return address.isAnyLocalAddress() || address.isLoopbackAddress() || address.isMulticastAddress(); + } + + /** + * The format of address should be {@code IPv4:port} or {@code [IPv6]:port}, or {@code host:port}, + * or you will not get expected result. + * + * Note that the IPv6 address should be wrapped by square brackets. + * @return IpPort parsed from input param, or {@code null} if the param is null. + */ + public static IpPort parseIpPort(String address) { + if (address == null) { + return null; + } + + URI uri = URI.create("http://" + address); + return parseIpPort(uri, true); + } + + /** + * Parse a {@link URI} into an {@link IpPort}. + * + *

+ * A uri without port is allowed, in which case the port will be inferred from the scheme. {@code http} is 80, and + * {@code https} is 443. + *

+ *

+ * The host of the {@code uri} should not be null, or it will be treated as an illegal param, + * and an {@link IllegalArgumentException} will be thrown. + *

+ */ + public static IpPort parseIpPort(URI uri) { + return parseIpPort(uri, false); + } + + /** + * Parse a {@link URI} into an {@link IpPort} + * @param uri a uri representing {@link IpPort} + * @param ignorePortUndefined whether the port should be inferred from scheme, when the port part of {@code uri} is {@code -1}. + * If {@code true} the undefined port is ignored; + * otherwise a port will be inferred from scheme: {@code http} is 80, and {@code https} is 443. + */ + public static IpPort parseIpPort(URI uri, boolean ignorePortUndefined) { + if (null == uri.getHost()) { + // if the format of address is legal but the value is out of range, URI#create(String) will not throw exception + // but return a URI with null host. + throw new IllegalArgumentException("Illegal uri: [" + uri + "]"); + } + + IpPort ipPort = new IpPort(uri.getHost(), uri.getPort()); + if (-1 != ipPort.getPort() || ignorePortUndefined) { + return ipPort; + } + + if (uri.getScheme().equals("http")) { + ipPort.setPort(80); + } + if (uri.getScheme().equals("https")) { + ipPort.setPort(443); + } + + return ipPort; + } + + /** + * @param uriAddress the address containing IP and port info. + * @return IpPort parsed from input param, or {@code null} if the param is null. + */ + public static IpPort parseIpPortFromURI(String uriAddress) { + if (uriAddress == null) { + return null; + } + + try { + return parseIpPort(new URI(uriAddress)); + } catch (URISyntaxException e) { + return null; + } + } + + public static IpPort parseIpPort(String scheme, String authority) { + if (authority == null) { + return null; + } + return parseIpPort(URI.create(scheme + "://" + authority)); + } + + /** + * 对于配置为0.0.0.0的地址,let it go + * schema, e.g. http + * address, e.g 0.0.0.0:8080 + * return 实际监听的地址 + */ + public static String getRealListenAddress(String schema, String address) { + if (address == null) { + return null; + } + try { + URI originalURI = new URI(schema + "://" + address); + // validate original url + NetUtils.parseIpPort(originalURI); + + return originalURI.toString(); + } catch (URISyntaxException e) { + LOGGER.error("address {} is not valid.", address); + return null; + } + } + + public static String getHostName() { + //If failed to get host name ,micro-service will registry failed + //So I add retry mechanism + if (hostName == null) { + doGetHostNameAndHostAddress(); + } + return hostName; + } + + @VisibleForTesting + static void resetHostName() { + hostName = null; + } + + public static String getHostAddress() { + //If failed to get host address ,micro-service will registry failed + //So I add retry mechanism + if (hostAddress == null) { + doGetHostNameAndHostAddress(); + } + return hostAddress; + } + + public static String getIpv6HostAddress() { + //If failed to get host address ,micro-service will registry failed + //So I add retry mechanism + if (hostAddressIpv6 == null) { + doGetHostNameAndHostAddress(); + } + return hostAddressIpv6; + } + + public static InetAddress getInterfaceAddress(String interfaceName) { + return allInterfaceAddresses.get(interfaceName); + } + + public static InetAddress ensureGetInterfaceAddress(String interfaceName) { + InetAddress address = allInterfaceAddresses.get(interfaceName); + if (address == null) { + throw new IllegalArgumentException("Can not find address for interface name: " + interfaceName); + } + return address; + } + + @SuppressWarnings({"unused", "try"}) + public static boolean canTcpListen(InetAddress address, int port) { + try (ServerSocket ss = new ServerSocket(port, 0, address)) { + return true; + } catch (IOException e) { + return false; + } + } + + public static String humanReadableBytes(long bytes) { + int unit = 1024; + if (bytes < unit) { + return String.valueOf(bytes); + } + int exp = (int) (Math.log(bytes) / Math.log(unit)); + char pre = "KMGTPE".charAt(exp - 1); + return String.format("%.3f%c", bytes / Math.pow(unit, exp), pre); + } +} 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 new file mode 100644 index 00000000000..78b633c1183 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/URIEndpointObject.java @@ -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. + */ + +package org.apache.servicecomb.foundation.common.net; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; + +/** + * transport公共的Endpoint Object,当transport使用URI表示的时候,可以转化为这个对象。 + */ +public class URIEndpointObject extends IpPort { + private static final String SSL_ENABLED_KEY = "sslEnabled"; + + 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 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()); + 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; + } + } + + public static Map> splitQuery(URI uri) { + final Map> queryPairs = new LinkedHashMap<>(); + List pairs = URLEncodedUtils.parse(uri, StandardCharsets.UTF_8); + for (NameValuePair pair : pairs) { + List list = queryPairs.computeIfAbsent(pair.getName(), name -> new ArrayList<>()); + list.add(pair.getValue()); + } + return queryPairs; + } + + 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 queries.get(key); + } + + public String getFirst(String key) { + List values = queries.get(key); + // it's impossible that values is not null and size is 0 + if (values == null) { + return null; + } + + return values.get(0); + } +} 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 new file mode 100644 index 00000000000..0c849a5d83a --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/AbstractPart.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.foundation.common.part; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; + +import javax.activation.MimetypesFileTypeMap; + +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.MediaType; + +public class AbstractPart implements Part { + private static final MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap(); + + protected String name; + + private String submittedFileName; + + protected String contentType; + + @Override + public InputStream getInputStream() throws IOException { + throw new Error("not supported method"); + } + + @Override + public String getContentType() { + return contentType != null ? contentType : MediaType.APPLICATION_OCTET_STREAM; + } + + @SuppressWarnings("unchecked") + public T contentType(String contentType) { + this.contentType = contentType; + return (T) this; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getSubmittedFileName() { + return submittedFileName; + } + + public AbstractPart setSubmittedFileName(String submittedFileName) { + this.submittedFileName = submittedFileName; + updateContentType(); + return this; + } + + private void updateContentType() { + if (contentType != null || submittedFileName == null) { + return; + } + + contentType = mimetypesFileTypeMap.getContentType(submittedFileName); + } + + @Override + public long getSize() { + throw new Error("not supported method"); + } + + @Override + public void write(String fileName) throws IOException { + throw new Error("not supported method"); + } + + @Override + public void delete() throws IOException { + throw new Error("not supported method"); + } + + @Override + public String getHeader(String name) { + throw new Error("not supported method"); + } + + @Override + public Collection getHeaders(String name) { + throw new Error("not supported method"); + } + + @Override + public Collection getHeaderNames() { + throw new Error("not supported method"); + } +} 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 new file mode 100644 index 00000000000..1bd9ddfd952 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePart.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.part; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; + +import org.apache.commons.io.FileUtils; + +public class FilePart extends AbstractPart implements FilePartForSend { + private final File file; + + private boolean deleteAfterFinished; + + public FilePart(String name, String file) { + this(name, new File(file)); + } + + public FilePart(String name, File file) { + this.name = name; + this.file = file; + this.setSubmittedFileName(this.file.getName()); + } + + @Override + public InputStream getInputStream() throws IOException { + return Files.newInputStream(file.toPath()); + } + + @Override + public long getSize() { + return file.length(); + } + + @Override + public void write(String fileName) throws IOException { + FileUtils.copyFile(file, new File(fileName)); + } + + @Override + public void delete() throws IOException { + file.delete(); + } + + @Override + public boolean isDeleteAfterFinished() { + return deleteAfterFinished; + } + + public FilePart setDeleteAfterFinished(boolean deleteAfterFinished) { + this.deleteAfterFinished = deleteAfterFinished; + return this; + } + + @Override + public String getAbsolutePath() { + return file.getAbsolutePath(); + } +} 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 new file mode 100644 index 00000000000..b2252361997 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePartForSend.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.common.part; + +import jakarta.servlet.http.Part; + +public interface FilePartForSend extends Part { + boolean isDeleteAfterFinished(); + + String getAbsolutePath(); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/InputStreamPart.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/InputStreamPart.java new file mode 100644 index 00000000000..88efd60a21b --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/InputStreamPart.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.foundation.common.part; + +import java.io.InputStream; + +import org.springframework.core.io.InputStreamResource; + +public class InputStreamPart extends ResourcePart { + public InputStreamPart(String name, InputStream inputStream) { + super(name, new InputStreamResource(inputStream)); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/ResourcePart.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/ResourcePart.java new file mode 100644 index 00000000000..cb00e5dd00c --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/ResourcePart.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.foundation.common.part; + +import java.io.IOException; +import java.io.InputStream; + +import org.springframework.core.io.Resource; + +public class ResourcePart extends AbstractPart { + private final Resource resource; + + public ResourcePart(String name, Resource resource) { + this.name = name; + this.resource = resource; + this.setSubmittedFileName(resource.getFilename()); + } + + @Override + public InputStream getInputStream() throws IOException { + return resource.getInputStream(); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/spring/PaasNamespaceHandler.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/spring/PaasNamespaceHandler.java new file mode 100644 index 00000000000..f434818df84 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/spring/PaasNamespaceHandler.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.foundation.common.spring; + +import java.util.Map.Entry; +import java.util.Properties; + +import org.apache.servicecomb.foundation.common.config.PaaSResourceUtils; +import org.apache.servicecomb.foundation.common.utils.FortifyUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + +//根据各个jar提供的信息来注册parser +public class PaasNamespaceHandler extends NamespaceHandlerSupport { + private static final Logger LOGGER = LoggerFactory.getLogger(PaasNamespaceHandler.class); + + private static final String NAMESPACE_RES = "classpath*:META-INF/spring/namespace.properties"; + + // @Override + public void init() { + Properties properties = null; + + try { + properties = PaaSResourceUtils.loadMergedProperties(NAMESPACE_RES); + } catch (Exception e) { + LOGGER.error("Failed to load namespace handler define, {}, {}", + NAMESPACE_RES, + FortifyUtils.getErrorInfo(e)); + return; + } + + for (Entry entry : properties.entrySet()) { + String className = entry.getValue().toString(); + try { + Class clazz = Class.forName(className); + Object instance = clazz.getDeclaredConstructor().newInstance(); + registerBeanDefinitionParser(entry.getKey().toString(), + (BeanDefinitionParser) instance); + } catch (ReflectiveOperationException e) { + // 类找不到,说明没部署相应的jar包,这不一定是错误 + // 可能是业务就选择不部署相应的jar包 + // 所以只是打印个info日志 + + LOGGER.info("Failed to create BeanDefinitionParser instance of {}", className); + } + } + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/AbstractRestObjectMapper.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/AbstractRestObjectMapper.java new file mode 100644 index 00000000000..5069e6a3986 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/AbstractRestObjectMapper.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.foundation.common.utils; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public abstract class AbstractRestObjectMapper extends ObjectMapper { + private static final long serialVersionUID = 189026839992490564L; + + public AbstractRestObjectMapper() { + super(); + } + + public AbstractRestObjectMapper(RestObjectMapper src) { + super(src); + } + + public abstract String convertToString(Object value) throws Exception; +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/AsyncUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/AsyncUtils.java new file mode 100644 index 00000000000..25219038097 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/AsyncUtils.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.foundation.common.utils; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; + +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; + +public final class AsyncUtils { + private AsyncUtils() { + } + + public static CompletableFuture tryCatchSupplier(Supplier supplier) { + try { + T value = supplier.get(); + return CompletableFuture.completedFuture(value); + } catch (Throwable e) { + return completeExceptionally(e); + } + } + + public static CompletableFuture tryCatchSupplierFuture(Supplier> supplier) { + try { + return supplier.get(); + } catch (Throwable e) { + return completeExceptionally(e); + } + } + + public static CompletableFuture completeExceptionally(Throwable throwable) { + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(throwable); + return future; + } + + /** + * throws {@code exception} as RuntimeException. + * + * @param exception exception which will be rethrow + */ + public static RuntimeException rethrow(Throwable exception) { + if (exception instanceof RuntimeException) { + return (RuntimeException) exception; + } + return new ServiceCombException("Not declared exception", exception); + } + + public static T toSync(CompletableFuture future) { + try { + return future.get(); + } catch (ExecutionException executionException) { + throw rethrow(executionException.getCause()); + } catch (Throwable e) { + throw rethrow(e); + } + } +} 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 new file mode 100644 index 00000000000..bc6a648b361 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.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.common.utils; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import org.springframework.aop.framework.AopProxyUtils; +import org.springframework.context.ApplicationContext; + + +public final class BeanUtils { + private static ApplicationContext context; + + private BeanUtils() { + } + + public static ApplicationContext getContext() { + return context; + } + + public static void setContext(ApplicationContext applicationContext) { + context = applicationContext; + } + + /** + * 不应该在业务流程中频繁调用,因为内部必然会加一个锁做互斥,会影响并发度 + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) { + return (T) context.getBean(name); + } + + public static Map getBeansOfType(Class type) { + if (context == null) { + // for some test case + return Collections.emptyMap(); + } + return context.getBeansOfType(type); + } + + public static T getBean(Class type) { + if (context == null) { + // for some test case + return null; + } + return context.getBean(type); + } + + /** + * Get the implemented class of the given instance + * @param bean the instance to get implemented class from + * @return the implemented class (if the checked class is proxied, return the ultimate target class) + * @see org.springframework.aop.framework.AopProxyUtils#ultimateTargetClass + */ + public static Class getImplClassFromBean(Object bean) { + return AopProxyUtils.ultimateTargetClass(bean); + } + + public static void addBeans(Class cls, List exists) { + if (context == null) { + return; + } + + for (T instance : exists) { + context.getAutowireCapableBeanFactory().autowireBean(instance); + } + for (T bean : context.getBeansOfType(cls).values()) { + if (bean.enabled()) { + exists.add(bean); + } + } + exists.sort(Comparator.comparingInt(SPIOrder::getOrder)); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ClassLoaderScopeContext.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ClassLoaderScopeContext.java new file mode 100644 index 00000000000..c977b762fda --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ClassLoaderScopeContext.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.foundation.common.utils; + +import java.util.HashMap; +import java.util.Map; + +import com.google.common.annotations.VisibleForTesting; + +/** +* class loader scope property is used when users run java-chassis in an class loader separated environment. +* +* For examples, deploy two war's to web container, or deploy two bundles in an OSGI container. +* +* Now java chassis not testing this feature carefully, but we will support users doing so. +* +* users who using this feature can feed back your problems in issues. +* +*/ +public class ClassLoaderScopeContext { + + private static final Map CLASS_LOADER_SCOPE_CONTEXT = new HashMap<>(); + + public static void setClassLoaderScopeProperty(String key, String value) { + CLASS_LOADER_SCOPE_CONTEXT.put(key, value); + } + + public static String getClassLoaderScopeProperty(String key) { + return CLASS_LOADER_SCOPE_CONTEXT.get(key); + } + + @VisibleForTesting + public static void clearClassLoaderScopeProperty() { + CLASS_LOADER_SCOPE_CONTEXT.clear(); + } +} 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/ExceptionUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ExceptionUtils.java new file mode 100644 index 00000000000..acf0b452fb3 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ExceptionUtils.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.foundation.common.utils; + +public class ExceptionUtils { + private static final int DEPTH = 5; + + private ExceptionUtils() { + + } + + public static String getExceptionMessageWithoutTrace(Throwable e) { + StringBuilder result = new StringBuilder(); + appendExceptionInfo(e, result); + Throwable cause = e.getCause(); + int depth = DEPTH; + while (cause != null && depth-- > 0) { + result.append(";"); + appendExceptionInfo(cause, result); + cause = cause.getCause(); + } + return result.toString(); + } + + private static void appendExceptionInfo(Throwable e, StringBuilder result) { + result.append("cause:"); + result.append(e.getClass().getSimpleName()); + result.append(",message:"); + result.append(e.getMessage()); + } +} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/FilePerm.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/FilePerm.java similarity index 88% rename from foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/FilePerm.java rename to foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/FilePerm.java index d2ab4e6ea48..979841f1523 100644 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/FilePerm.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/FilePerm.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +15,7 @@ * limitations under the License. */ -package io.servicecomb.foundation.common.utils; +package org.apache.servicecomb.foundation.common.utils; import java.io.File; import java.io.IOException; @@ -25,6 +26,7 @@ import java.nio.file.attribute.PosixFileAttributes; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; +import java.util.Arrays; import java.util.EnumSet; import java.util.Set; @@ -82,7 +84,7 @@ private FilePerm() { */ public static final int FILE_PERM_MASK = 511; - private static AclEntryPermission[] permList = new AclEntryPermission[] { + private static final AclEntryPermission[] permList = new AclEntryPermission[] { AclEntryPermission.READ_DATA, AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_NAMED_ATTRS, @@ -127,9 +129,7 @@ public static Set getPosixPerm(int perm) { public static Set getDefaultAclPerm() { Set perms = EnumSet.noneOf(AclEntryPermission.class); - for (AclEntryPermission aclPerm : permList) { - perms.add(aclPerm); - } + perms.addAll(Arrays.asList(permList)); return perms; } diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/FortifyUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/FortifyUtils.java similarity index 80% rename from foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/FortifyUtils.java rename to foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/FortifyUtils.java index 3ae42daffcf..63234af5ab4 100644 --- a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/FortifyUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/FortifyUtils.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +15,7 @@ * limitations under the License. */ -package io.servicecomb.foundation.common.utils; +package org.apache.servicecomb.foundation.common.utils; import java.io.PrintWriter; import java.io.StringWriter; @@ -35,9 +36,9 @@ */ public final class FortifyUtils { - private static Method getMessageMethod; + private static final Method getMessageMethod; - private static Method printStackTraceMethod; + private static final Method printStackTraceMethod; static { try { @@ -82,7 +83,7 @@ public static String getErrorInfo(Throwable e) { } public static String getErrorInfo(Throwable e, boolean isPrintMsg) { - StringBuffer error = new StringBuffer(System.lineSeparator()); + StringBuilder error = new StringBuilder(System.lineSeparator()); error.append("Exception: ").append(e.getClass().getName()).append("; "); if (isPrintMsg) { 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 new file mode 100644 index 00000000000..d872236082e --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/GenericsUtils.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.foundation.common.utils; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +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. + */ + public static boolean isGenericResponseType(Type type) { + if (type instanceof ParameterizedType) { + return false; + } + if (type instanceof Class) { + return ((Class) type).getTypeParameters().length > 0; + } + return true; + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/IOUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/IOUtils.java new file mode 100644 index 00000000000..ce57bbb4f84 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/IOUtils.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.foundation.common.utils; + +import java.io.Closeable; + +import org.apache.commons.lang3.StringUtils; + +public class IOUtils { + @SuppressWarnings("deprecation") + public static void closeQuietly(final Closeable closeable) { + org.apache.commons.io.IOUtils.closeQuietly(closeable); + } + + public static String anonymousPath(String path) { + if (StringUtils.isEmpty(path)) { + return path; + } + StringBuilder result = new StringBuilder(); + char separator = path.contains("/") ? '/' : '\\'; + char[] tokens = path.toCharArray(); + boolean byPass = false; + boolean fileName = true; + for (int i = tokens.length - 1; i >= 0; i--) { + if (tokens[i] == separator) { + fileName = false; + byPass = false; + result.append(separator); + continue; + } + if (!byPass || fileName) { + result.append(tokens[i]); + byPass = true; + } + } + return result.reverse().toString(); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/JsonUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/JsonUtils.java new file mode 100644 index 00000000000..07269d6830f --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/JsonUtils.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.foundation.common.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.json.JsonWriteFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; + +public final class JsonUtils { + public static final ObjectMapper OBJ_MAPPER; + private static final ObjectMapper UNICODE_OBJ_MAPPER; + + static { + OBJ_MAPPER = new RestObjectMapper(); + + UNICODE_OBJ_MAPPER = OBJ_MAPPER.copy(); + UNICODE_OBJ_MAPPER.enable(JsonWriteFeature.ESCAPE_NON_ASCII.mappedFeature()); + } + + private JsonUtils() { + } + + public static T readValue(byte[] src, Class valueType) throws IOException { + return OBJ_MAPPER.readValue(src, valueType); + } + + public static T readValue(InputStream is, Class valueType) throws IOException { + return OBJ_MAPPER.readValue(is, valueType); + } + + public static T readValue(InputStream is, JavaType valueType) throws IOException { + return OBJ_MAPPER.readValue(is, valueType); + } + + public static byte[] writeValueAsBytes(Object value) throws JsonProcessingException { + return OBJ_MAPPER.writeValueAsBytes(value); + } + + public static String writeValueAsString(Object value) throws JsonProcessingException { + return OBJ_MAPPER.writeValueAsString(value); + } + + public static String writeUnicodeValueAsString(Object value) throws JsonProcessingException { + return UNICODE_OBJ_MAPPER.writeValueAsString(value); + } + + public static T convertValue(Object fromValue, Class toValueType) { + return OBJ_MAPPER.convertValue(fromValue, toValueType); + } + + public static T convertValue(Object fromValue, JavaType toValueType) { + return OBJ_MAPPER.convertValue(fromValue, toValueType); + } + + public static void writeValue(OutputStream out, Object value) throws IOException { + OBJ_MAPPER.writeValue(out, value); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/JvmUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/JvmUtils.java new file mode 100644 index 00000000000..6ed60ff0afc --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/JvmUtils.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.common.utils; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; + +public final class JvmUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(JvmUtils.class); + + // available for oracle jdk/ open jdk, and maybe others + @VisibleForTesting + static final String SUN_JAVA_COMMAND = "sun.java.command"; + + private JvmUtils() { + } + + /** + * + * @return main class or null, never throw exception. + * Note that this method does not ensure that the scbMainClass can be returned correctly in the some scene. + */ + public static Class findMainClassByStackTrace() { + String mainClass = null; + StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); + if (stackTrace != null && stackTrace.length > 0) { + for (StackTraceElement stackTraceElement : stackTrace) { + if ("main".equals(stackTraceElement.getMethodName())) { + mainClass = stackTraceElement.getClassName(); + break; + } + } + } + if (StringUtils.isEmpty(mainClass)) { + LOGGER.info("Can't found main class by stackTrace."); + return null; + } + try { + Class cls = Class.forName(mainClass); + LOGGER.info("Found main class \"{}\" by stackTrace.", mainClass); + return cls; + } catch (Throwable e) { + LOGGER.warn("\"{}\" is not a valid class.", mainClass, e); + return null; + } + } + + /** + * + * @return main class or null, never throw exception. + * Note that this method does not ensure that the scbMainClass can be returned correctly in the some scene,like "mvn spring-boot:run". + */ + public static Class findMainClass() { + //Get the mainClass from the call stack + String mainClass = null; + // 1.run with java -cp ...... + // command is main class and args + // 2.run with java -jar ...... + // command is jar file name and args + String command = System.getProperty(SUN_JAVA_COMMAND); + if (StringUtils.isNotEmpty(command)) { + String mainClassOrJar = command.trim().split(" ")[0]; + mainClass = readFromJar(mainClassOrJar); + } + if (StringUtils.isEmpty(mainClass)) { + LOGGER.info("Can't found main class by manifest."); + return null; + } + try { + Class cls = Class.forName(mainClass); + LOGGER.info("Found main class \"{}\".", mainClass); + return cls; + } catch (Throwable e) { + LOGGER.warn("\"{}\" is not a valid class.", mainClass, e); + return null; + } + } + + private static String readFromJar(String mainClassOrJar) { + if (!mainClassOrJar.endsWith(".jar")) { + return mainClassOrJar; + } + + String manifestUri = "jar:file:" + new File(mainClassOrJar).getAbsolutePath() + "!/" + JarFile.MANIFEST_NAME; + + try { + URL url = new URL(manifestUri); + try (InputStream inputStream = url.openStream()) { + Manifest manifest = new Manifest(inputStream); + String startClass = manifest.getMainAttributes().getValue("Start-Class"); + if (StringUtils.isNotEmpty(startClass)) { + return startClass; + } + return manifest.getMainAttributes().getValue("Main-Class"); + } + } catch (Throwable e) { + LOGGER.warn("Failed to read Main-Class from \"{}\".", manifestUri, e); + return null; + } + } + + /** + * find a property class loader to avoid null + */ + public static ClassLoader correctClassLoader(ClassLoader classLoader) { + ClassLoader targetClassLoader = classLoader; + if (targetClassLoader == null) { + targetClassLoader = Thread.currentThread().getContextClassLoader(); + } + if (targetClassLoader == null) { + targetClassLoader = JvmUtils.class.getClassLoader(); + } + return targetClassLoader; + } + + public static ClassLoader findClassLoader() { + return correctClassLoader(null); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairEntry.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairEntry.java new file mode 100644 index 00000000000..be31df199a4 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairEntry.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.foundation.common.utils; + +import java.security.PrivateKey; +import java.security.PublicKey; + +public final class KeyPairEntry { + + private final PrivateKey privateKey; + + private final PublicKey publicKey; + + private final String publicKeyEncoded; + + public KeyPairEntry(PrivateKey privateKey, PublicKey publicKey, String publicKeyEncoded) { + this.privateKey = privateKey; + this.publicKey = publicKey; + this.publicKeyEncoded = publicKeyEncoded; + } + + public PrivateKey getPrivateKey() { + return privateKey; + } + + public PublicKey getPublicKey() { + return publicKey; + } + + public String getPublicKeyEncoded() { + return publicKeyEncoded; + } +} 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 new file mode 100644 index 00000000000..91fed2ce1c8 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairUtils.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class KeyPairUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(KeyPairUtils.class); + + private static final String KEY_GENERATOR_ALGORITHM = LegacyPropertyFactory.getStringProperty( + "servicecomb.publicKey.accessControl.keyGeneratorAlgorithm", "RSA"); + + private static final String SIGN_ALG = LegacyPropertyFactory.getStringProperty( + "servicecomb.publicKey.accessControl.signAlgorithm", "SHA256withRSA"); + + private static final int KEY_SIZE = LegacyPropertyFactory.getIntProperty( + "servicecomb.publicKey.accessControl.keySize", 2048); + + private static final Base64.Encoder encoder = Base64.getEncoder(); + + private static final Base64.Decoder decoder = Base64.getDecoder(); + + private static KeyFactory kf = null; + + static { + + try { + kf = KeyFactory.getInstance(KEY_GENERATOR_ALGORITHM); + } catch (NoSuchAlgorithmException e) { + LOGGER.error("init keyfactory error"); + } + } + + public static KeyPairEntry generateALGKeyPair() { + try { + KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(KEY_GENERATOR_ALGORITHM); + keyGenerator.initialize(KEY_SIZE, new SecureRandom()); + KeyPair keyPair = keyGenerator.generateKeyPair(); + PublicKey pubKey = keyPair.getPublic(); + PrivateKey privKey = keyPair.getPrivate(); + return new KeyPairEntry(privKey, pubKey, encoder.encodeToString(pubKey.getEncoded())); + } catch (NoSuchAlgorithmException e) { + LOGGER.error("generate rsa keypair failed"); + throw new IllegalStateException("perhaps error occurred on jre"); + } + } + + /** + * if has performance problem ,change Signature to ThreadLocal instance + */ + public static String sign(String content, PrivateKey privateKey) + throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { + Signature signature = Signature.getInstance(SIGN_ALG); + signature.initSign(privateKey); + signature.update(content.getBytes(StandardCharsets.UTF_8)); + byte[] signByte = signature.sign(); + return encoder.encodeToString(signByte); + } + + /** + * + * if has performance problem ,change Signature to ThreadLocal instance + * @param publicKey public key after base64 encode + * @param sign 签名 + * @param content original content + * @return verify result + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws InvalidKeyException + * @throws SignatureException + */ + public static boolean verify(String publicKey, String sign, String content) + throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException { + if (null == kf) { + throw new NoSuchAlgorithmException(KEY_GENERATOR_ALGORITHM + " KeyFactory not available"); + } + byte[] bytes = decoder.decode(publicKey); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes); + PublicKey pubKey = kf.generatePublic(keySpec); + Signature signature = Signature.getInstance(SIGN_ALG); + signature.initVerify(pubKey); + signature.update(content.getBytes(StandardCharsets.UTF_8)); + return signature.verify(decoder.decode(sign)); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/LambdaMetafactoryUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/LambdaMetafactoryUtils.java new file mode 100644 index 00000000000..32ba59014e0 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/LambdaMetafactoryUtils.java @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.invoke.CallSite; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.utils.bean.BoolGetter; +import org.apache.servicecomb.foundation.common.utils.bean.BoolSetter; +import org.apache.servicecomb.foundation.common.utils.bean.ByteGetter; +import org.apache.servicecomb.foundation.common.utils.bean.ByteSetter; +import org.apache.servicecomb.foundation.common.utils.bean.CharGetter; +import org.apache.servicecomb.foundation.common.utils.bean.CharSetter; +import org.apache.servicecomb.foundation.common.utils.bean.DoubleGetter; +import org.apache.servicecomb.foundation.common.utils.bean.DoubleSetter; +import org.apache.servicecomb.foundation.common.utils.bean.FloatGetter; +import org.apache.servicecomb.foundation.common.utils.bean.FloatSetter; +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; +import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; +import org.apache.servicecomb.foundation.common.utils.bean.LongGetter; +import org.apache.servicecomb.foundation.common.utils.bean.LongSetter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.common.utils.bean.ShortGetter; +import org.apache.servicecomb.foundation.common.utils.bean.ShortSetter; + +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; + +public final class LambdaMetafactoryUtils { + private static final Lookup LOOKUP = MethodHandles.lookup(); + + private static final Map, Class> GETTER_MAP = new HashMap<>(); + + private static final Map, Class> SETTER_MAP = new HashMap<>(); + + static { + initGetterSetterMap(); + } + + private static void initGetterSetterMap() { + GETTER_MAP.put(boolean.class, BoolGetter.class); + GETTER_MAP.put(byte.class, ByteGetter.class); + GETTER_MAP.put(char.class, CharGetter.class); + GETTER_MAP.put(short.class, ShortGetter.class); + GETTER_MAP.put(int.class, IntGetter.class); + GETTER_MAP.put(long.class, LongGetter.class); + GETTER_MAP.put(float.class, FloatGetter.class); + GETTER_MAP.put(double.class, DoubleGetter.class); + + SETTER_MAP.put(boolean.class, BoolSetter.class); + SETTER_MAP.put(byte.class, ByteSetter.class); + SETTER_MAP.put(char.class, CharSetter.class); + SETTER_MAP.put(short.class, ShortSetter.class); + SETTER_MAP.put(int.class, IntSetter.class); + SETTER_MAP.put(long.class, LongSetter.class); + SETTER_MAP.put(float.class, FloatSetter.class); + SETTER_MAP.put(double.class, DoubleSetter.class); + } + + private LambdaMetafactoryUtils() { + } + + private static Method findAbstractMethod(Class functionalInterface) { + for (Method method : functionalInterface.getMethods()) { + if ((method.getModifiers() & Modifier.ABSTRACT) != 0) { + return method; + } + } + + return null; + } + + @SuppressWarnings("unchecked") + public static T createLambda(Object instance, Method instanceMethod, Class functionalIntfCls) { + try { + Method intfMethod = findAbstractMethod(functionalIntfCls); + MethodHandle methodHandle = LOOKUP.unreflect(instanceMethod); + + MethodType intfMethodType = MethodType.methodType(intfMethod.getReturnType(), intfMethod.getParameterTypes()); + MethodType instanceMethodType = MethodType + .methodType(instanceMethod.getReturnType(), instanceMethod.getParameterTypes()); + CallSite callSite = LambdaMetafactory.metafactory( + LOOKUP, + intfMethod.getName(), + MethodType.methodType(functionalIntfCls, instance.getClass()), + intfMethodType, + methodHandle, + instanceMethodType); + + return (T) callSite.getTarget().bindTo(instance).invoke(); + } catch (Throwable e) { + throw new IllegalStateException("Failed to create lambda from " + instanceMethod, e); + } + } + + @SuppressWarnings("unchecked") + public static T createLambda(Method instanceMethod, Class functionalIntfCls) { + if (Modifier.isNative(instanceMethod.getModifiers())) { + // fix "Failed to create lambda from public final native java.lang.Class java.lang.Object.getClass()" + return null; + } + try { + Method intfMethod = findAbstractMethod(functionalIntfCls); + MethodHandle methodHandle = LOOKUP.unreflect(instanceMethod); + + MethodType intfMethodType = MethodType.methodType(intfMethod.getReturnType(), intfMethod.getParameterTypes()); + + // the return type of fluent setter is object instead of void, but we can assume the return type is void. it doesn't matter + MethodType instanceMethodType = MethodType + .methodType(intfMethod.getReturnType(), methodHandle.type().parameterList()); + CallSite callSite = LambdaMetafactory.metafactory( + LOOKUP, + intfMethod.getName(), + MethodType.methodType(functionalIntfCls), + intfMethodType, + methodHandle, + instanceMethodType); + + return (T) callSite.getTarget().invoke(); + } catch (Throwable e) { + throw new IllegalStateException("Failed to create lambda from " + instanceMethod, e); + } + } + + public static T createGetter(Method getMethod) { + Class getterCls = GETTER_MAP.getOrDefault(getMethod.getReturnType(), Getter.class); + return createLambda(getMethod, getterCls); + } + + @SuppressWarnings("unchecked") + public static Getter createObjectGetter(Method getMethod) { + return createLambda(getMethod, Getter.class); + } + + public static Getter createObjectGetter(BeanPropertyDefinition propertyDefinition) { + if (propertyDefinition.hasGetter()) { + return createObjectGetter(propertyDefinition.getGetter().getAnnotated()); + } + + return createGetter(propertyDefinition.getField().getAnnotated()); + } + + // slower than reflect directly + @SuppressWarnings("unchecked") + public static Getter createGetter(Field field) { + checkAccess(field); + return instance -> { + try { + return (F) field.get(instance); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }; + } + + private static void checkAccess(Field field) { + // This check is not accurate. Most of time package visible and protected access can be ignored, so simply do this. + if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) { + throw new IllegalStateException( + String.format("Can not access field, a public field or accessor is required." + + "Declaring class is %s, field is %s", + field.getDeclaringClass().getName(), + field.getName())); + } + } + + public static T createSetter(Method setMethod) { + Class setterCls = SETTER_MAP.getOrDefault(setMethod.getParameterTypes()[0], Setter.class); + return createLambda(setMethod, setterCls); + } + + // just for avoid java 9~11 bug: https://bugs.openjdk.java.net/browse/JDK-8174983 + // otherwise can be replaced by: createLambda(setMethod, Setter.class) + @SuppressWarnings("unchecked") + public static Setter createObjectSetter(Method setMethod) { + Object setter = createSetter(setMethod); + if (setter instanceof BoolSetter) { + return (Instance, value) -> ((BoolSetter) setter).set(Instance, (boolean) value); + } + if (setter instanceof ByteSetter) { + return (Instance, value) -> ((ByteSetter) setter).set(Instance, (byte) value); + } + if (setter instanceof CharSetter) { + return (Instance, value) -> ((CharSetter) setter).set(Instance, (char) value); + } + if (setter instanceof DoubleSetter) { + return (Instance, value) -> ((DoubleSetter) setter).set(Instance, (double) value); + } + if (setter instanceof FloatSetter) { + return (Instance, value) -> ((FloatSetter) setter).set(Instance, (float) value); + } + if (setter instanceof IntSetter) { + return (Instance, value) -> ((IntSetter) setter).set(Instance, (int) value); + } + if (setter instanceof LongSetter) { + return (Instance, value) -> ((LongSetter) setter).set(Instance, (long) value); + } + if (setter instanceof ShortSetter) { + return (Instance, value) -> ((ShortSetter) setter).set(Instance, (short) value); + } + + return (Setter) setter; + } + + public static Setter createObjectSetter(BeanPropertyDefinition propertyDefinition) { + if (propertyDefinition.hasSetter()) { + return createObjectSetter(propertyDefinition.getSetter().getAnnotated()); + } + + return createSetter(propertyDefinition.getField().getAnnotated()); + } + + // slower than reflect directly + public static Setter createSetter(Field field) { + checkAccess(field); + return (instance, value) -> { + try { + field.set(instance, value); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }; + } +} 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/MimeTypesUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/MimeTypesUtils.java new file mode 100644 index 00000000000..25bf4cb4610 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/MimeTypesUtils.java @@ -0,0 +1,82 @@ +/* + * Copyright 2014 Red Hat, Inc. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * The Apache License v2.0 is available at + * http://www.opensource.org/licenses/apache2.0.php + * + * You may elect to redistribute this code under either of these licenses. + * + */ + +/* + * Forked from https://github.com/vert-x3/vertx-web/blob/927ed057ddc028eb09a168db621de3d72fd85ed4/vertx-web/src/main/java/io/vertx/ext/web/impl/Utils.java + * Because we uses getSortedAcceptableMimeTypes method which is removed by vertx. + */ + +package org.apache.servicecomb.foundation.common.utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.regex.Pattern; + +public class MimeTypesUtils { + private static final Pattern COMMA_SPLITTER = Pattern.compile(" *, *"); + + private static final Pattern SEMICOLON_SPLITTER = Pattern.compile(" *; *"); + + private static final Pattern EQUAL_SPLITTER = Pattern.compile(" *= *"); + + private static final Comparator ACCEPT_X_COMPARATOR = (o1, o2) -> Float.compare(getQuality(o2), getQuality(o1)); + + private static float getQuality(String s) { + if (s == null) { + return 0; + } + + String[] params = SEMICOLON_SPLITTER.split(s); + for (int i = 1; i < params.length; i++) { + String[] q = EQUAL_SPLITTER.split(params[1]); + if ("q".equals(q[0])) { + return Float.parseFloat(q[1]); + } + } + return 1; + } + + public static List getSortedAcceptableMimeTypes(String acceptHeader) { + // accept anything when accept is not present + if (acceptHeader == null) { + return Collections.emptyList(); + } + + // parse + String[] items = COMMA_SPLITTER.split(acceptHeader); + // sort on quality + Arrays.sort(items, ACCEPT_X_COMPARATOR); + + List list = new ArrayList<>(items.length); + + for (String item : items) { + // find any ; e.g.: "application/json;q=0.8" + int space = item.indexOf(';'); + + if (space != -1) { + list.add(item.substring(0, space)); + } else { + list.add(item); + } + } + + return list; + } +} 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 new file mode 100644 index 00000000000..61bbf442e11 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/PartUtils.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.foundation.common.utils; + +import java.io.File; +import java.io.InputStream; + +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() { + } + + public static Part getSinglePart(String name, Object value) { + if (value == null) { + return null; + } + + if (value instanceof Part) { + return (Part) value; + } + + if (value instanceof InputStream) { + return new InputStreamPart(name, (InputStream) value); + } + + if (value instanceof Resource) { + return new ResourcePart(name, (Resource) value); + } + + if (value instanceof File) { + return new FilePart(name, (File) value); + } + + if (value instanceof byte[]) { + return new ResourcePart(name, new ByteArrayResource((byte[]) value)); + } + + throw new IllegalStateException( + String.format("File input parameter of %s could be %s / %s / %s / byte[] or %s, but got %s.", + name, + Part.class.getName(), + InputStream.class.getName(), + Resource.class.getName(), + File.class.getName(), + value.getClass().getName())); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ReflectUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ReflectUtils.java new file mode 100644 index 00000000000..1b4d3f1ca1a --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ReflectUtils.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.utils; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import org.apache.commons.lang3.reflect.FieldUtils; +import org.springframework.util.ReflectionUtils; + +import com.google.common.reflect.TypeToken; + +public final class ReflectUtils { + private ReflectUtils() { + + } + + public static void setField(Object instance, String fieldName, Object value) { + setField(instance.getClass(), instance, fieldName, value); + } + + public static void setField(Class cls, Object instance, String fieldName, Object value) { + Field field = ReflectionUtils.findField(cls, fieldName); + try { + field.setAccessible(true); + field.set(instance, value); + } catch (Exception e) { + throw new Error(e); + } + } + + // 根据方法名,忽略参数查找method,调用此函数的前提是没有重载 + public static Method findMethod(Class cls, String methodName) { + for (Method method : cls.getMethods()) { + if (method.getName().equals(methodName)) { + return method; + } + } + + return null; + } + + @SuppressWarnings("unchecked") + public static Class getFieldArgument(Class genericCls, String fieldName) { + try { + Type generic = FieldUtils.getField(genericCls, fieldName).getGenericType(); + TypeToken token = TypeToken.of(genericCls).resolveType(generic); + Type fieldType = token.getType(); + Type argument = ((ParameterizedType) fieldType).getActualTypeArguments()[0]; + if (argument instanceof GenericArrayType) { + return (Class) TypeToken.of(argument).getRawType(); + } + + return (Class) argument; + } catch (Throwable e) { + throw new IllegalStateException("Failed to get generic argument.", e); + } + } + + @SuppressWarnings("unchecked") + public static T constructArrayType(Class cls) { + return (T) Array.newInstance(cls, 0).getClass(); + } + + public static Class getClassByName(String clsName) { + try { + return Class.forName(clsName); + } catch (ClassNotFoundException e) { + ClassLoader classLoader = JvmUtils.correctClassLoader(null); + try { + return classLoader.loadClass(clsName); + } catch (ClassNotFoundException e1) { + return null; + } + } + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ResourceUtil.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ResourceUtil.java new file mode 100644 index 00000000000..037900d9a7a --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ResourceUtil.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.common.utils; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Stream; + +public final class ResourceUtil { + + private ResourceUtil() { + } + + /** + * Search the specified location in classpath, and returns the resources with the specified suffix. + */ + public static List findResourcesBySuffix(String resourceLocation, String fileNameSuffix) + throws IOException, URISyntaxException { + return findResources(resourceLocation, path -> path.toString().endsWith(fileNameSuffix)); + } + + /** + * Search the specified location in classpath, all the resources found are collected and returned. + */ + public static List findResources(String resourceLocation) throws IOException, URISyntaxException { + return findResources(resourceLocation, p -> true); + } + + /** + * Search the specified location in classpath, which can be a directory or the exact file location, + * and returns a list of URIs pointing to the matched resources. + * + * @param resourceLocation in which location the resources are searched + * @param filter to pick out those matched resources + */ + public static List findResources(String resourceLocation, Predicate filter) + throws IOException, URISyntaxException { + ArrayList result = new ArrayList<>(); + + Enumeration dirURLs = JvmUtils.findClassLoader().getResources(resourceLocation); + while (dirURLs.hasMoreElements()) { + URL dirURL = dirURLs.nextElement(); + if ("file".equals(dirURL.getProtocol())) { + Path dirPath = Paths.get(dirURL.toURI()); + collectResourcesFromPath(dirPath, filter, result); + continue; + } + + try (FileSystem fileSystem = FileSystems.newFileSystem(dirURL.toURI(), Collections.emptyMap())) { + Path dirPath = fileSystem.getPath(resourceLocation); + if (Files.exists(dirPath)) { + // normal jar files like : xxx.jar!/resourceLocation + collectResourcesFromPath(dirPath, filter, result); + } else { + // spring boot fat jar files like : xxx.jar!/BOOT-INF/!classes/resourceLocation + dirPath = fileSystem.getPath("BOOT-INF", "classes", resourceLocation); + if (Files.exists(dirPath)) { + collectResourcesFromPath(dirPath, filter, result); + } + } + } + } + + return result; + } + + private static void collectResourcesFromPath(Path path, Predicate filter, Collection resources) + throws IOException { + try (Stream dirContentTraversalStream = Files.walk(path)) { + dirContentTraversalStream + .filter(filter) + .map(Path::toUri) + .forEach(resources::add); + } + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/RestObjectMapper.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/RestObjectMapper.java new file mode 100644 index 00000000000..edc6095878f --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/RestObjectMapper.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser.Feature; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import io.vertx.core.json.JsonObject; + +public class RestObjectMapper extends AbstractRestObjectMapper { + + private static class JsonObjectSerializer extends JsonSerializer { + @Override + public void serialize(JsonObject value, JsonGenerator jgen, SerializerProvider provider) throws IOException { + jgen.writeObject(value.getMap()); + } + } + + private static final long serialVersionUID = -8158869347066287575L; + + private static final JavaType STRING_JAVA_TYPE = TypeFactory.defaultInstance().constructType(String.class); + + public RestObjectMapper() { + super(); + getFactory().disable(Feature.AUTO_CLOSE_SOURCE); + // Enable features that can tolerance errors and not enable those make more constraints for compatible reasons. + // Developers can use validation api to do more checks. + disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + // no view annotations shouldn't be included in JSON + this._deserializationConfig = this._deserializationConfig.without(MapperFeature.DEFAULT_VIEW_INCLUSION); + this._serializationConfig = this._serializationConfig.without(MapperFeature.DEFAULT_VIEW_INCLUSION); + disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); + enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY); + + SimpleModule module = new SimpleModule(); + // custom types + module.addSerializer(JsonObject.class, new JsonObjectSerializer()); + registerModule(module); + registerModule(new JavaTimeModule()); + } + + public RestObjectMapper(RestObjectMapper src) { + super(src); + } + @Override + public RestObjectMapper copy() { + this._checkInvalidCopy(RestObjectMapper.class); + return new RestObjectMapper(this); + } + + @Override + public String convertToString(Object value) throws Exception { + return convertValue(value, STRING_JAVA_TYPE); + } + + @Override + @SuppressWarnings("unchecked") + public T convertValue(Object fromValue, JavaType toValueType) throws IllegalArgumentException { + // After jackson 2.10.*, will by pass the following check when convert value. But this is useful + // for java chassis applications and do not need to convert to keep performance. So add the check here.(conversion is + // not necessary and will cause some trouble in some user applications that depend on this) + if (fromValue == null) { + return null; + } else { + Class targetType = toValueType.getRawClass(); + if (targetType != Object.class + && !toValueType.hasGenericTypes() + && targetType.isAssignableFrom(fromValue.getClass())) { + return (T) fromValue; + } + } + + return super.convertValue(fromValue, toValueType); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/StringBuilderUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/StringBuilderUtils.java new file mode 100644 index 00000000000..08a723eb7ec --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/StringBuilderUtils.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.foundation.common.utils; + +public final class StringBuilderUtils { + private StringBuilderUtils() { + } + + public static void appendLine(StringBuilder sb, String fmt, Object... args) { + sb.append(String.format(fmt, args)).append("\n"); + } + + public static StringBuilder deleteLast(StringBuilder sb, int count) { + int min = Math.min(sb.length(), count); + sb.setLength(sb.length() - min); + return sb; + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/TimeUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/TimeUtils.java new file mode 100644 index 00000000000..476c3901c0b --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/TimeUtils.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.foundation.common.utils; + +import java.time.Clock; + +public final class TimeUtils { + private TimeUtils() { + } + + private static final Clock systemDefaultZoneClock = Clock.systemDefaultZone(); + + /** + * @return Singleton system default zone clock + */ + public static Clock getSystemDefaultZoneClock() { + return systemDefaultZoneClock; + } + + +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/TypesUtil.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/TypesUtil.java new file mode 100644 index 00000000000..5f72a1db26b --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/TypesUtil.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * common utils to convert java types. + */ +public class TypesUtil { + private static final Map, Class> PRIMITIVE_TO_WRAPPER = new HashMap<>(); + + static { + PRIMITIVE_TO_WRAPPER.put(byte.class, Byte.class); + PRIMITIVE_TO_WRAPPER.put(short.class, Short.class); + PRIMITIVE_TO_WRAPPER.put(int.class, Integer.class); + PRIMITIVE_TO_WRAPPER.put(long.class, Long.class); + PRIMITIVE_TO_WRAPPER.put(float.class, Float.class); + PRIMITIVE_TO_WRAPPER.put(double.class, Double.class); + PRIMITIVE_TO_WRAPPER.put(boolean.class, Boolean.class); + PRIMITIVE_TO_WRAPPER.put(char.class, Character.class); + } + + + private static final Map, Class> WRAPPER_TO_PRIMITIVE = new HashMap<>(); + + static { + WRAPPER_TO_PRIMITIVE.put(Byte.class, byte.class); + WRAPPER_TO_PRIMITIVE.put(Short.class, short.class); + WRAPPER_TO_PRIMITIVE.put(Integer.class, int.class); + WRAPPER_TO_PRIMITIVE.put(Long.class, long.class); + WRAPPER_TO_PRIMITIVE.put(Float.class, float.class); + WRAPPER_TO_PRIMITIVE.put(Double.class, double.class); + WRAPPER_TO_PRIMITIVE.put(Boolean.class, boolean.class); + WRAPPER_TO_PRIMITIVE.put(Character.class, char.class); + } + + public static final JavaType PRIMITIVE_BYTE = TypeFactory.defaultInstance().constructType(byte.class); + + public static final JavaType PRIMITIVE_SHORT = TypeFactory.defaultInstance().constructType(short.class); + + public static final JavaType PRIMITIVE_INT = TypeFactory.defaultInstance().constructType(int.class); + + public static final JavaType PRIMITIVE_LONG = TypeFactory.defaultInstance().constructType(long.class); + + public static final JavaType PRIMITIVE_FLOAT = TypeFactory.defaultInstance().constructType(float.class); + + public static final JavaType PRIMITIVE_DOUBLE = TypeFactory.defaultInstance().constructType(double.class); + + public static final JavaType PRIMITIVE_BOOLEAN = TypeFactory.defaultInstance().constructType(boolean.class); + + public static final JavaType PRIMITIVE_CHAR = TypeFactory.defaultInstance().constructType(char.class); + + public static final JavaType PRIMITIVE_WRAPPER_BYTE = TypeFactory.defaultInstance().constructType(Byte.class); + + public static final JavaType PRIMITIVE_WRAPPER_SHORT = TypeFactory.defaultInstance().constructType(Short.class); + + public static final JavaType PRIMITIVE_WRAPPER_INT = TypeFactory.defaultInstance().constructType(Integer.class); + + public static final JavaType PRIMITIVE_WRAPPER_LONG = TypeFactory.defaultInstance().constructType(Long.class); + + public static final JavaType PRIMITIVE_WRAPPER_FLOAT = TypeFactory.defaultInstance().constructType(Float.class); + + public static final JavaType PRIMITIVE_WRAPPER_DOUBLE = TypeFactory.defaultInstance().constructType(Double.class); + + public static final JavaType PRIMITIVE_WRAPPER_BOOLEAN = TypeFactory.defaultInstance().constructType(Boolean.class); + + public static final JavaType PRIMITIVE_WRAPPER_CHAR = TypeFactory.defaultInstance().constructType(Character.class); + + + private static final Map PRIMITIVE_TO_WRAPPER_JAVATYPE = new HashMap<>(); + + static { + PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_BYTE, PRIMITIVE_WRAPPER_BYTE); + PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_SHORT, PRIMITIVE_WRAPPER_SHORT); + PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_INT, PRIMITIVE_WRAPPER_INT); + PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_LONG, PRIMITIVE_WRAPPER_LONG); + PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_FLOAT, PRIMITIVE_WRAPPER_FLOAT); + PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_DOUBLE, PRIMITIVE_WRAPPER_DOUBLE); + PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_BOOLEAN, PRIMITIVE_WRAPPER_BOOLEAN); + PRIMITIVE_TO_WRAPPER_JAVATYPE.put(PRIMITIVE_CHAR, PRIMITIVE_WRAPPER_CHAR); + } + + private static final Map WRAPPER_TO_PRIMITIVE_JAVATYPE = new HashMap<>(); + + static { + WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_BYTE, PRIMITIVE_BYTE); + WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_SHORT, PRIMITIVE_SHORT); + WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_INT, PRIMITIVE_INT); + WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_LONG, PRIMITIVE_LONG); + WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_FLOAT, PRIMITIVE_FLOAT); + WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_DOUBLE, PRIMITIVE_DOUBLE); + WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_BOOLEAN, PRIMITIVE_BOOLEAN); + WRAPPER_TO_PRIMITIVE_JAVATYPE.put(PRIMITIVE_WRAPPER_CHAR, PRIMITIVE_CHAR); + } + + public static Class primitiveTypeToWrapper(Class primitiveType) { + return PRIMITIVE_TO_WRAPPER.get(primitiveType); + } + + public static Class wrapperTypeToPrimitive(Class wrapperType) { + return WRAPPER_TO_PRIMITIVE.get(wrapperType); + } + + public static JavaType primitiveJavaTypeToWrapper(JavaType primitiveType) { + return PRIMITIVE_TO_WRAPPER_JAVATYPE.get(primitiveType); + } + + public static JavaType wrapperJavaTypeToPrimitive(JavaType wrapperType) { + return WRAPPER_TO_PRIMITIVE_JAVATYPE.get(wrapperType); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ArrayGetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ArrayGetter.java new file mode 100644 index 00000000000..6bdda6ea66f --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ArrayGetter.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.foundation.common.utils.bean; + +public class ArrayGetter implements Getter { + private final int idx; + + public ArrayGetter(int idx) { + this.idx = idx; + } + + @Override + public T get(T[] instance) { + return instance[idx]; + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ArraySetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ArraySetter.java new file mode 100644 index 00000000000..4df87f8a273 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ArraySetter.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.foundation.common.utils.bean; + +public class ArraySetter implements Setter { + private final int idx; + + public ArraySetter(int idx) { + this.idx = idx; + } + + @Override + public void set(T[] instance, T value) { + instance[idx] = value; + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/BoolGetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/BoolGetter.java new file mode 100644 index 00000000000..11e0a3c753e --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/BoolGetter.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.foundation.common.utils.bean; + +public interface BoolGetter { + boolean get(T instance); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/BoolSetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/BoolSetter.java new file mode 100644 index 00000000000..8cbacb56ca7 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/BoolSetter.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.foundation.common.utils.bean; + +public interface BoolSetter { + void set(T instance, boolean value); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ByteGetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ByteGetter.java new file mode 100644 index 00000000000..c4bab67d346 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ByteGetter.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.foundation.common.utils.bean; + +public interface ByteGetter { + byte get(T instance); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ByteSetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ByteSetter.java new file mode 100644 index 00000000000..952d06b0723 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ByteSetter.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.foundation.common.utils.bean; + +public interface ByteSetter { + void set(T instance, byte value); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/CharGetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/CharGetter.java new file mode 100644 index 00000000000..2e09c988701 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/CharGetter.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.foundation.common.utils.bean; + +public interface CharGetter { + char get(T instance); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/CharSetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/CharSetter.java new file mode 100644 index 00000000000..e022fa34354 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/CharSetter.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.foundation.common.utils.bean; + +public interface CharSetter { + void set(T instance, char value); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/DoubleGetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/DoubleGetter.java new file mode 100644 index 00000000000..5349c4ccbb3 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/DoubleGetter.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.foundation.common.utils.bean; + +public interface DoubleGetter { + double get(T instance); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/DoubleSetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/DoubleSetter.java new file mode 100644 index 00000000000..c39a8024502 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/DoubleSetter.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.foundation.common.utils.bean; + +public interface DoubleSetter { + void set(T instance, double value); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/FloatGetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/FloatGetter.java new file mode 100644 index 00000000000..405d233a8bf --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/FloatGetter.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.foundation.common.utils.bean; + +public interface FloatGetter { + float get(T instance); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/FloatSetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/FloatSetter.java new file mode 100644 index 00000000000..7d4b75a34b9 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/FloatSetter.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.foundation.common.utils.bean; + +public interface FloatSetter { + void set(T instance, float value); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/Getter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/Getter.java new file mode 100644 index 00000000000..1fa0fb22947 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/Getter.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.foundation.common.utils.bean; + +public interface Getter { + F get(C instance); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/IntGetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/IntGetter.java new file mode 100644 index 00000000000..b7aaf2991e8 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/IntGetter.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.foundation.common.utils.bean; + +public interface IntGetter { + int get(T instance); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/IntSetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/IntSetter.java new file mode 100644 index 00000000000..0a0425e3ca1 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/IntSetter.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.foundation.common.utils.bean; + +public interface IntSetter { + void set(T instance, int value); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/LongGetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/LongGetter.java new file mode 100644 index 00000000000..8f4d68dd3e7 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/LongGetter.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.foundation.common.utils.bean; + +public interface LongGetter { + long get(T instance); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/LongSetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/LongSetter.java new file mode 100644 index 00000000000..a0ba4f9e723 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/LongSetter.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.foundation.common.utils.bean; + +public interface LongSetter { + void set(T instance, long value); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/MapGetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/MapGetter.java new file mode 100644 index 00000000000..84039d3595a --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/MapGetter.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.foundation.common.utils.bean; + +import java.util.Map; + +public class MapGetter implements Getter, V> { + private final K key; + + public MapGetter(K key) { + this.key = key; + } + + @Override + public V get(Map instance) { + return instance.get(key); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/MapSetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/MapSetter.java new file mode 100644 index 00000000000..872d00a63a4 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/MapSetter.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.foundation.common.utils.bean; + +import java.util.Map; + +public class MapSetter implements Setter, V> { + private final K key; + + public MapSetter(K key) { + this.key = key; + } + + @Override + public void set(Map instance, V value) { + instance.put(key, value); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/Setter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/Setter.java new file mode 100644 index 00000000000..bf79d2c8439 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/Setter.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.foundation.common.utils.bean; + +public interface Setter { + void set(C instance, F value); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ShortGetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ShortGetter.java new file mode 100644 index 00000000000..bea64f4e33e --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ShortGetter.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.foundation.common.utils.bean; + +public interface ShortGetter { + short get(T instance); +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ShortSetter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ShortSetter.java new file mode 100644 index 00000000000..db741cb047c --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/bean/ShortSetter.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.foundation.common.utils.bean; + +public interface ShortSetter { + void set(T instance, short value); +} 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 new file mode 100644 index 00000000000..5b4f80b9ddb --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/JavaxServletPartDeserializer.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.foundation.common.utils.json; + +import java.io.IOException; + +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 { + final Object currentValue = p.getEmbeddedObject(); + return (Part) currentValue; + } +} 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 new file mode 100644 index 00000000000..c964241b42c --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/JavaxServletPartSerializer.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.foundation.common.utils.json; + +import java.io.IOException; + +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; + + public JavaxServletPartSerializer() { + this(null); + } + + protected JavaxServletPartSerializer(Class t) { + super(t); + } + + @Override + public void serialize(Part value, JsonGenerator gen, SerializerProvider provider) throws IOException { + ObjectCodec preservedCodec = ((TokenBuffer) gen).asParser().getCodec(); + // set codec as null to avoid recursive dead loop + // JsonGenerator is instantiated for each serialization, so there should be no thread safe issue + gen.setCodec(null); + gen.writeObject(value); + gen.setCodec(preservedCodec); + } +} 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 new file mode 100644 index 00000000000..5c3b8a53bd6 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/json/PartModule.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.foundation.common.utils.json; + +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; + + public PartModule() { + super("javax-servlet-part"); + + addSerializer(Part.class, new JavaxServletPartSerializer()); + addDeserializer(Part.class, new JavaxServletPartDeserializer()); + } + + @Override + public Object getTypeId() { + return getModuleName(); + } + + @Override + public int getOrder() { + return Short.MAX_VALUE; + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/token/Keypair4Auth.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/token/Keypair4Auth.java new file mode 100644 index 00000000000..a3172419465 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/token/Keypair4Auth.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.foundation.token; + +import java.security.PrivateKey; +import java.security.PublicKey; + +/** + * 进程级别公私钥对 + * + */ +public class Keypair4Auth { + + private Keypair4Auth() { + } + + private PrivateKey privateKey; + + private PublicKey publicKey; + + private String publicKeyEncoded; + + + public PrivateKey getPrivateKey() { + return privateKey; + } + + + public void setPrivateKey(PrivateKey privateKey) { + this.privateKey = privateKey; + } + + + public PublicKey getPublicKey() { + return publicKey; + } + + + public void setPublicKey(PublicKey publicKey) { + this.publicKey = publicKey; + } + + + public String getPublicKeyEncoded() { + return publicKeyEncoded; + } + + + public void setPublicKeyEncoded(String publicKeyEncoded) { + this.publicKeyEncoded = publicKeyEncoded; + } + + public static Keypair4Auth INSTANCE = new Keypair4Auth(); +} diff --git a/foundations/foundation-common/src/main/resources/META-INF/mime.types b/foundations/foundation-common/src/main/resources/META-INF/mime.types new file mode 100644 index 00000000000..eec38ecd549 --- /dev/null +++ b/foundations/foundation-common/src/main/resources/META-INF/mime.types @@ -0,0 +1,156 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +application/octet-stream bin +application/astound asd,asn +application/fastman lcc +application/java-archive jar +application/java-serialized-object ser +application/java-vm class +application/mac-binhex40 hqx +application/x-stuffit sit +application/mbedlet mbd +application/msword doc,dot,wiz,rtf +application/oda oda +application/pdf pdf +application/postscript ai,eps,ps +application/studiom smp +application/timbuktu tbt +application/vnd.ms-excel xls,xlw,xla,xlc,xlm,xlt +application/vnd.ms-powerpoint ppt,pps,pot +application/vnd.ms-project mpp +application/winhlp hlp +application/x-javascript js +application/x-javascript;charset=UTF-8 jsu +application/x-java-jnlp-file jnlp +application/x-aim aim +application/x-asap asp +application/x-csh csh +application/x-dvi dvi +application/x-earthtime etc +application/x-envoy evy +application/x-gtar gtar +application/x-cpio cpio +application/x-hdf hdf +application/x-latex latex +application/x-javascript-config jsc +application/x-maker fm +application/x-mif mif,mi +application/x-mocha mocha,moc +application/x-msaccess mdb +application/x-mscardfile crd +application/x-msclip clp +application/x-msmediaview m13,m14 +application/x-msmetafile wmf +application/x-msmoney mny +application/x-mspublisher pub +application/x-msschedule scd +application/x-msterminal trm +application/x-mswrite wri +application/x-NET-Install ins +application/x-netcdf nc,cdf +application/x-ns-proxy-autoconfig proxy +application/x-salsa slc +application/x-sh sh +application/x-shar shar +application/x-sprite spr,sprite +application/x-tar tar +application/x-tcl tcl +application/x-perl pl +application/x-tex tex +application/x-texinfo texinfo,texi +application/x-timbuktu tbp +application/x-tkined tki,tkined +application/x-troff-man man +application/x-troff-me me +application/x-troff-ms ms +application/x-troff t,tr,roff +application/x-wais-source src +application/zip zip +application/pre-encrypted enc +application/x-pkcs7-crl crl +application/x-fortezza-ckl ckl +application/xml-dtd dtd + +audio/basic au,snd +audio/echospeech es,esl +audio/midi midi,mid +audio/x-aiff aif,aiff,aifc +audio/x-wav wav +audio/x-pn-realaudio ra,ram +audio/x-pac pac +audio/x-epac pae +audio/x-liveaudio lam + +drawing/x-dwf dwf + +image/fif fif +image/x-icon ico +image/gif gif +image/ief ief +image/ifs ifs +image/jpeg jpeg,jpg,jpe,jfif,pjpeg,pjp +image/png png +image/tiff tiff,tif +image/vnd dwg,svf +image/wavelet wi +image/bmp bmp +image/x-photo-cd pcd +image/x-cmu-raster ras +image/x-portable-anymap pnm +image/x-portable-bitmap pbm +image/x-portable-graymap pgm +image/x-portable-pixmap ppm +image/x-rgb rgb +image/x-xbitmap xbm +image/x-xpixmap xpm +image/x-xwindowdump xwd + +text/css css + +text/html htm,html +text/plain txt +text/richtext rtx +text/tab-separated-values tsv +text/x-setext etx +text/x-speech talk +text/xml xml +text/xul xul + +video/isivideo fvi +video/mpeg mpeg,mpg,mpe,mpv,vbs,mpegv +video/x-mpeg2 mpv2,mp2v +video/msvideo avi +video/quicktime qt,mov,moov +video/vivo viv,vivo +video/wavelet wv +video/x-sgi-movie movie + +x-world/x-svr svr +x-world/x-vrml wrl +x-world/x-vrt vrt + +x-conference/x-cooltalk ice + +magnus-internal/imagemap map +magnus-internal/parsed-html shtml +magnus-internal/cgi cgi,exe,bat + +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 new file mode 100644 index 00000000000..f954930aa71 --- /dev/null +++ b/foundations/foundation-common/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.common.encrypt.Encryption @@ -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.foundation.common.encrypt.NoEncryption diff --git a/foundations/foundation-common/src/main/resources/META-INF/spring/common.bean.xml b/foundations/foundation-common/src/main/resources/META-INF/spring/common.bean.xml deleted file mode 100644 index e56474e504e..00000000000 --- a/foundations/foundation-common/src/main/resources/META-INF/spring/common.bean.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - \ No newline at end of file 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/main/resources/config/base/log4j.properties b/foundations/foundation-common/src/main/resources/config/base/log4j.properties deleted file mode 100644 index f182dab3b42..00000000000 --- a/foundations/foundation-common/src/main/resources/config/base/log4j.properties +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -##log4j DEBUG < INFO < WARN < ERROR < FATAL -log4j.rootLogger=INFO,paas,stdout -paas.logs.dir=./ -paas.logs.file=cse.log - -#if not set runLogger level ,log4Utils.init will meet a null point error -log4j.logger.runLogger=INFO - -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%p] %m %l%n - -log4j.appender.paas=io.servicecomb.foundation.common.utils.RollingFileAppenderExt -log4j.appender.paas.file=${paas.logs.dir}${paas.logs.file} -log4j.appender.paas.MaxFileSize=20MB -log4j.appender.paas.MaxBackupIndex=10 -log4j.appender.paas.layout=org.apache.log4j.PatternLayout -log4j.appender.paas.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss,SSS/zzz}][%t][%p]%m %l%n -log4j.appender.paas.logPermission=rw------- diff --git a/foundations/foundation-common/src/main/resources/config/config.inc.xml b/foundations/foundation-common/src/main/resources/config/config.inc.xml deleted file mode 100644 index c2ea83ce8a9..00000000000 --- a/foundations/foundation-common/src/main/resources/config/config.inc.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - classpath*:config/config.properties - - \ No newline at end of file diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/base/RetryableRunnableTest.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/base/RetryableRunnableTest.java deleted file mode 100644 index 3183323c880..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/base/RetryableRunnableTest.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.base; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -import org.junit.Before; -import org.junit.Test; - -public class RetryableRunnableTest { - - private final Runnable blockedRunnable = mock(Runnable.class); - - private final DescriptiveRunnable runnable = mock(DescriptiveRunnable.class); - - private final RetryableRunnable retryableRunnable = new RetryableRunnable(runnable, 50); - - private final RuntimeException exception = new RuntimeException("oops"); - - @Before - public void setUp() throws Exception { - when(runnable.description()).thenReturn("some descriptive runnable"); - } - - @Test - public void runOnceWhenNoException() { - retryableRunnable.run(); - - verify(runnable).run(); - } - - @Test - public void retriesUnderlyingRunnableUntilSuccess() { - doThrow(exception).doThrow(exception).doNothing().when(runnable).run(); - - retryableRunnable.run(); - - verify(runnable, times(3)).run(); - } - - @Test - public void exitsWhenInterrupted() throws InterruptedException { - ExecutorService executorService = Executors.newSingleThreadExecutor(); - CountDownLatch countDownLatch = new CountDownLatch(1); - DescriptiveRunnable runnable = new DescriptiveRunnable() { - @Override - public String description() { - return "runnable"; - } - - @Override - public void run() { - if (countDownLatch.getCount() == 1) { - countDownLatch.countDown(); - } - throw new RuntimeException("oops"); - } - }; - - RetryableRunnable retryableRunnable = new RetryableRunnable(runnable, 50); - - Future retryable = executorService.submit(retryableRunnable); - executorService.submit(blockedRunnable); - - countDownLatch.await(); - - retryable.cancel(true); - - // this test have some pitfalls that can't shutdown the execution of retryableRunnable - assertThat(retryable.isCancelled(), is(true)); - // Just make sure the blockedRunnable is called in the slower box - Thread.sleep(100); - verify(blockedRunnable).run(); - executorService.shutdownNow(); - } -} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/config/BeanProp.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/config/BeanProp.java deleted file mode 100644 index ec507dcef58..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/config/BeanProp.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.config; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -@JsonIgnoreProperties(ignoreUnknown = true) -public class BeanProp { - @JsonProperty(value = "1.2") - private String test; - - public String getTest() { - return test; - } - - public void setTest(String test) { - this.test = test; - } -} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/config/TestConfig.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/config/TestConfig.java deleted file mode 100644 index f7bb7db2056..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/config/TestConfig.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.config; - -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; - -import org.junit.Assert; -import org.junit.Test; -import org.springframework.context.ApplicationContext; -import org.springframework.core.io.Resource; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; - -import io.servicecomb.foundation.common.config.impl.IdXmlLoader; -import io.servicecomb.foundation.common.config.impl.PaaSPropertiesLoaderUtils; -import io.servicecomb.foundation.common.config.impl.XmlLoader; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class TestConfig { - private static final int TEST_PROP_LIST_SIZE = 2; - - private static ApplicationContext context; - - @Test - public void loadMergedProperties() throws Exception { - Properties prop = ConfigMgr.INSTANCE.getConfig("pTest"); - Assert.assertEquals("0", prop.get("1")); - Assert.assertEquals("1", prop.get("1.1")); - Assert.assertEquals("2", prop.get("1.2")); - } - - @Test - public void testBean() throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - BeanProp bp = (BeanProp) BeanUtils.getBean("beanProp"); - Assert.assertEquals("2", bp.getTest()); - } - - @Test - public void testBeanContext() { - BeanUtils.setContext(context); - Assert.assertEquals(context, BeanUtils.getContext()); - } - - @Test - public void testConvertProperties() throws Exception { - BeanProp bp = PaaSResourceUtils.loadConfigAs("pTest", BeanProp.class); - Assert.assertEquals("2", bp.getTest()); - } - - @Test - public void testConvertXml() throws Exception { - Object ret = PaaSResourceUtils.loadConfigAs("xTest", BeanProp.class); - BeanProp bp = (BeanProp) ret; - Assert.assertEquals("test value", bp.getTest()); - } - - @Test - public void testXml() throws Exception { - List locationPatternList = new ArrayList<>(); - locationPatternList.add("classpath*:config/config.test.inc.xml"); - ConfigLoader loader = new XmlLoader(locationPatternList, ".inc.xml"); - Document doc = loader.load(); - - Element root = doc.getDocumentElement(); - Assert.assertEquals("configs", root.getNodeName()); - - { - NodeList propList = root.getElementsByTagName("properties"); - Assert.assertEquals(2, propList.getLength()); - - { - Element prop = (Element) propList.item(0); - Assert.assertEquals("pTest", prop.getAttributes().getNamedItem("id").getNodeValue()); - - NodeList pathList = prop.getElementsByTagName("path"); - Assert.assertEquals(1, pathList.getLength()); - Assert.assertEquals("classpath*:config/test.properties", pathList.item(0).getTextContent()); - } - - { - Element prop = (Element) propList.item(1); - Assert.assertEquals("pTest", prop.getAttributes().getNamedItem("id").getNodeValue()); - - NodeList pathList = prop.getElementsByTagName("path"); - Assert.assertEquals(1, pathList.getLength()); - Assert.assertEquals("classpath*:config/test.ext.properties", - pathList.item(0).getTextContent()); - } - } - - { - NodeList xmlList = root.getElementsByTagName("xml"); - Assert.assertEquals(2, xmlList.getLength()); - - { - Element xml = (Element) xmlList.item(0); - Assert.assertEquals("xTest", xml.getAttributes().getNamedItem("id").getNodeValue()); - - NodeList pathList = xml.getElementsByTagName("path"); - Assert.assertEquals(1, pathList.getLength()); - Assert.assertEquals("classpath*:config/test.xml", pathList.item(0).getTextContent()); - } - - { - Element xml = (Element) xmlList.item(1); - Assert.assertEquals("xTest", xml.getAttributes().getNamedItem("id").getNodeValue()); - - NodeList pathList = xml.getElementsByTagName("path"); - Assert.assertEquals(1, pathList.getLength()); - Assert.assertEquals("classpath*:config/test.ext.xml", pathList.item(0).getTextContent()); - } - } - } - - @Test - public void testIdXml() throws Exception { - List locationPatternList = new ArrayList<>(); - locationPatternList.add("classpath*:config/config.test.inc.xml"); - ConfigLoader loader = new IdXmlLoader(locationPatternList, ".inc.xml"); - Document doc = loader.load(); - - Element root = doc.getDocumentElement(); - Assert.assertEquals("configs", root.getNodeName()); - - NodeList propList = root.getElementsByTagName("properties"); - Assert.assertEquals(1, propList.getLength()); - - Element prop = (Element) propList.item(0); - Assert.assertEquals("pTest", prop.getAttributes().getNamedItem("id").getNodeValue()); - - NodeList pathListProp = prop.getElementsByTagName("path"); - Assert.assertEquals(TEST_PROP_LIST_SIZE, pathListProp.getLength()); - Assert.assertEquals("classpath*:config/test.properties", pathListProp.item(0).getTextContent()); - Assert.assertEquals("classpath*:config/test.ext.properties", pathListProp.item(1).getTextContent()); - - NodeList xmlList = root.getElementsByTagName("xml"); - Assert.assertEquals(1, xmlList.getLength()); - - Element xml = (Element) xmlList.item(0); - Assert.assertEquals("xTest", xml.getAttributes().getNamedItem("id").getNodeValue()); - - NodeList pathListXml = xml.getElementsByTagName("path"); - Assert.assertEquals(TEST_PROP_LIST_SIZE, pathListXml.getLength()); - Assert.assertEquals("classpath*:config/test.xml", pathListXml.item(0).getTextContent()); - Assert.assertEquals("classpath*:config/test.ext.xml", pathListXml.item(1).getTextContent()); - } - - @Test - public void testPaaSResourceUtils() throws Exception { - List oList = PaaSResourceUtils.getSortedXmls("test.xml"); - Assert.assertNotEquals(null, oList); - Assert.assertNotEquals(null, PaaSResourceUtils.loadMergedProperties("config/test.properties")); - PaaSResourceUtils.sortProperties(oList); - PaaSResourceUtils.sortXmls(oList); - Assert.assertNotEquals(null, PaaSPropertiesLoaderUtils.loadMergedProperties("config/test.properties")); - try { - PaaSPropertiesLoaderUtils.fillMergedProperties(new Properties(), ""); - } catch (Exception e) { - Assert.assertEquals(true, (e.getMessage()).contains("Resource path must not be null or empty")); - } - try { - PaaSPropertiesLoaderUtils.fillMergedProperties(new Properties(), "tes.kunle"); - } catch (Exception e) { - Assert.assertEquals(true, (e.getMessage()).contains("Resource path must ends with")); - } - } -} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/net/TestIpPort.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/net/TestIpPort.java deleted file mode 100644 index e64e249e10e..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/net/TestIpPort.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.net; - -import org.junit.Assert; -import org.junit.Test; - -public class TestIpPort { - @Test - public void testIpPort() { - IpPort inst1 = new IpPort(); - inst1.setHostOrIp("localhost"); - inst1.setPort(3333); - IpPort inst2 = new IpPort("localhost", 3333); - Assert.assertEquals(inst1.getHostOrIp(), inst2.getHostOrIp()); - Assert.assertEquals(inst1.getPort(), inst2.getPort()); - Assert.assertEquals(inst1.getSocketAddress().getHostName(), inst2.getSocketAddress().getHostName()); - } -} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/net/TestNetUtils.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/net/TestNetUtils.java deleted file mode 100644 index 8b14c04de1d..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/net/TestNetUtils.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.net; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import mockit.Deencapsulation; - -public class TestNetUtils { - @Test - public void testIpPort() { - IpPort oIPPort = new IpPort("10.145.154.45", 8080); - Assert.assertEquals("10.145.154.45", oIPPort.getHostOrIp()); - Assert.assertEquals(8080, oIPPort.getPort()); - oIPPort.setPort(9090); - Assert.assertEquals(9090, oIPPort.getPort()); - Assert.assertNotEquals(null, oIPPort.getSocketAddress()); - } - - @Test - public void testNetutils() { - Assert.assertEquals("127.0.0.1", NetUtils.parseIpPort("127.0.0.1:8080").getHostOrIp()); - Assert.assertEquals(8080, NetUtils.parseIpPort("127.0.0.1:8080").getPort()); - Assert.assertEquals(null, NetUtils.parseIpPort(null)); - Assert.assertEquals(null, NetUtils.parseIpPort("127.0.0.18080")); - Assert.assertEquals(NetUtils.parseIpPortFromURI(null), null); - Assert.assertEquals(NetUtils.parseIpPortFromURI("ss"), null); - Assert.assertEquals(NetUtils.parseIpPortFromURI("rest://127.0.0.1:8080").getHostOrIp(), "127.0.0.1"); - } - - @Test - public void testFullOperation() { - Assert.assertNotNull(NetUtils.getHostAddress()); - Assert.assertNotNull(NetUtils.getHostName()); - } - - @Test - public void testGetRealListenAddress() { - Assert.assertNull(NetUtils.getRealListenAddress("http", null)); - Assert.assertNull(NetUtils.getRealListenAddress("http:1", "1.1.1.1:8080")); - Assert.assertEquals("http://1.1.1.1:8080", NetUtils.getRealListenAddress("http", "1.1.1.1:8080")); - } - - @Test - public void testNetworkInterface() { - Map org = Deencapsulation.getField(NetUtils.class, "allInterfaceAddresses"); - - Map newValue = new HashMap<>(); - InetAddress addr = Mockito.mock(InetAddress.class); - newValue.put("eth100", addr); - Deencapsulation.setField(NetUtils.class, "allInterfaceAddresses", newValue); - - Assert.assertEquals(addr, NetUtils.getInterfaceAddress("eth100")); - Assert.assertEquals(addr, NetUtils.ensureGetInterfaceAddress("eth100")); - - try { - NetUtils.ensureGetInterfaceAddress("xxx"); - Assert.fail("must throw exception"); - } catch (IllegalArgumentException e) { - Assert.assertEquals("Can not find address for interface name: xxx", e.getMessage()); - } - - Deencapsulation.setField(NetUtils.class, "allInterfaceAddresses", org); - } - - @Test - public void testCanTcpListenNo() throws IOException { - InetAddress address = InetAddress.getByName("127.0.0.1"); - try (ServerSocket ss = new ServerSocket(0, 0, address)) { - Assert.assertFalse(NetUtils.canTcpListen(address, ss.getLocalPort())); - } - } - - @Test - public void testCanTcpListenYes() throws IOException { - InetAddress address = InetAddress.getByName("127.0.0.1"); - ServerSocket ss = new ServerSocket(0, 0, address); - int port = ss.getLocalPort(); - ss.close(); - - Assert.assertTrue(NetUtils.canTcpListen(address, port)); - } -} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/net/TestURIEndpointObject.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/net/TestURIEndpointObject.java deleted file mode 100644 index f0684964e6b..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/net/TestURIEndpointObject.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.net; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - -import mockit.Deencapsulation; - -public class TestURIEndpointObject { - @Test - public void testRestEndpointObject() { - URIEndpointObject obj = new URIEndpointObject("http://127.0.2.0:8080"); - Assert.assertEquals("127.0.2.0", obj.getHostOrIp()); - Assert.assertEquals(8080, obj.getPort()); - Assert.assertFalse(obj.isSslEnabled()); - - obj = new URIEndpointObject("http://127.0.2.0:8080?sslEnabled=true"); - Assert.assertEquals("127.0.2.0", obj.getHostOrIp()); - Assert.assertEquals(8080, obj.getPort()); - Assert.assertTrue(obj.isSslEnabled()); - Assert.assertNull(obj.getFirst("notExist")); - } - - @Test(expected = IllegalArgumentException.class) - public void testRestEndpointObjectException() { - new URIEndpointObject("http://127.0.2.0"); - } - - @Test - public void testQueryChineseAndSpaceAndEmpty() throws UnsupportedEncodingException { - String strUri = - "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"); - Assert.assertEquals(3, querys.size()); - - Assert.assertEquals(1, ep.getQuery("a").size()); - Assert.assertEquals("1", ep.getFirst("a")); - - Assert.assertEquals(1, ep.getQuery("b").size()); - Assert.assertEquals("", ep.getFirst("b")); - - Assert.assertEquals(1, ep.getQuery("country").size()); - Assert.assertEquals("中 国", ep.getFirst("country")); - } -} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/spring/TestPaasNamespaceHandler.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/spring/TestPaasNamespaceHandler.java deleted file mode 100644 index 9f29e1ba9f8..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/spring/TestPaasNamespaceHandler.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.spring; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestPaasNamespaceHandler { - - PaasNamespaceHandler paasNamespaceHandler = null; - - @Before - public void setUp() throws Exception { - paasNamespaceHandler = new PaasNamespaceHandler(); - } - - @After - public void tearDown() throws Exception { - paasNamespaceHandler = null; - } - - @Test - public void testInit() { - boolean status = false; - try { - paasNamespaceHandler.init(); - } catch (Exception e) { - status = true; - } - Assert.assertFalse(status); - } -} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/SPIServiceDef.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/SPIServiceDef.java deleted file mode 100644 index 6d4c1eacb2d..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/SPIServiceDef.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.utils; - -public interface SPIServiceDef { - -} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/SPIServiceDef0.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/SPIServiceDef0.java deleted file mode 100644 index d6aca8b61c1..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/SPIServiceDef0.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.utils; - -public interface SPIServiceDef0 { - -} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/SPIServiceDefImpl.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/SPIServiceDefImpl.java deleted file mode 100644 index 522cb009f54..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/SPIServiceDefImpl.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.utils; - -public class SPIServiceDefImpl implements SPIServiceDef { - -} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestBeanUtils.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestBeanUtils.java deleted file mode 100644 index de943db9d9c..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestBeanUtils.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.foundation.common.utils; - -import org.aspectj.lang.annotation.Aspect; -import org.junit.Assert; -import org.junit.Test; -import org.springframework.aop.aspectj.annotation.AspectJProxyFactory; - -public class TestBeanUtils { - static interface Intf { - - } - - static class Impl implements Intf { - - } - - @Aspect - static class MyAspect { - } - - @Test - public void test() { - Intf target = new Impl(); - AspectJProxyFactory factory = new AspectJProxyFactory(target); - MyAspect aspect = new MyAspect(); - factory.addAspect(aspect); - Intf proxy = factory.getProxy(); - - Assert.assertEquals(Impl.class, BeanUtils.getImplClassFromBean(proxy)); - Assert.assertEquals(Impl.class, BeanUtils.getImplClassFromBean(new Impl())); - } -} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestFortifyUtils.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestFortifyUtils.java deleted file mode 100644 index 369197b4504..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestFortifyUtils.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.utils; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import java.io.IOException; - -import org.junit.Assert; -import org.junit.Test; - -public class TestFortifyUtils { - @Test - public void testFortifyUtils() throws IOException { - Assert.assertEquals("", FortifyUtils.getErrorMsg(null)); - Assert.assertEquals("", FortifyUtils.getErrorStack(null)); - } - - @Test - public void testFilePerm() { - Assert.assertEquals(10, (FilePerm.getDefaultAclPerm().size())); - Assert.assertEquals(3, (FilePerm.getDefaultPosixPerm().size())); - Assert.assertEquals(4, (FilePerm.getPosixPerm(400).size())); - } - - @Test - public void testGetErrorMsg() { - - Throwable e = new Throwable(); - - FortifyUtils.getErrorMsg(e); - - assertNull(FortifyUtils.getErrorMsg(e)); - } - - @Test - public void testGetSecurityXmlDocumentFactory() { - - try { - FortifyUtils.getSecurityXmlDocumentFactory(); - assertNotNull(FortifyUtils.getSecurityXmlDocumentFactory()); - } catch (Exception e) { - /* Do not Worry */ - Assert.assertTrue(false); - } - } - - @Test - public void testGetErrorStack() { - - Throwable e = new Throwable(); - FortifyUtils.getErrorStack(e); - Assert.assertNotEquals(true, FortifyUtils.getErrorStack(e)); - } - - @Test - public void testGetErrorInfo() { - - Throwable e = new Throwable(); - FortifyUtils.getErrorInfo(e, true); - Assert.assertNotEquals(true, FortifyUtils.getErrorInfo(e, true)); - } -} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestRollingFileAppenderExt.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestRollingFileAppenderExt.java deleted file mode 100644 index b8b5d08934d..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestRollingFileAppenderExt.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.utils; - -import java.io.File; - -import org.apache.log4j.Layout; -import org.apache.log4j.spi.LoggingEvent; -import org.junit.Assert; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Injectable; - -public class TestRollingFileAppenderExt { - @Test - public void testRollingFileAppenderExt(@Injectable LoggingEvent event, - @Injectable Layout layout) throws Exception { - new Expectations() { - { - layout.format(event); - result = "test"; - } - }; - File cur = new File(System.getProperty("user.dir")); - File temp = new File(cur, "temptestfile.log"); - if (temp.exists()) { - temp.delete(); - } - RollingFileAppenderExt ext = new RollingFileAppenderExt(); - ext.setLayout(layout); - ext.setLogPermission("rw-------"); - ext.setFile(temp.getAbsolutePath()); - ext.setFile(temp.getAbsolutePath(), false, false, 300000); - Assert.assertEquals(ext.getLogPermission(), "rw-------"); - Assert.assertTrue(temp.exists()); - - temp.delete(); - ext.subAppend(event); - Assert.assertTrue(temp.exists()); - - ext.close(); - temp.delete(); - Assert.assertFalse(temp.exists()); - } -} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestSPIServiceUtils.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestSPIServiceUtils.java deleted file mode 100644 index ce3c62a7d33..00000000000 --- a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestSPIServiceUtils.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.common.utils; - -import org.junit.Assert; -import org.junit.Test; - -/** - * Test SPIServiceUtils - * - * - */ -public class TestSPIServiceUtils { - @Test - public void testGetTargetServiceNull() { - SPIServiceDef0 service = SPIServiceUtils.getTargetService(SPIServiceDef0.class); - Assert.assertNull(service); - } - - @Test - public void testGetTargetServiceNotNull() { - SPIServiceDef service = SPIServiceUtils.getTargetService(SPIServiceDef.class); - Assert.assertTrue(SPIServiceDef.class.isInstance(service)); - } -} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/DynamicObjectTest.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/DynamicObjectTest.java new file mode 100644 index 00000000000..6867124543b --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/DynamicObjectTest.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.foundation.common; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import io.vertx.core.json.Json; +import io.vertx.core.json.jackson.DatabindCodec; + +class DynamicObjectTest { + @Test + void should_support_json_encode_decode() { + Map map = new HashMap<>(); + map.put("k", "v"); + + DynamicObject dynamicObject = DatabindCodec.mapper().convertValue(map, DynamicObject.class); + + assertThat(dynamicObject.getDynamic()).isNotSameAs(map); + assertThat(dynamicObject.getDynamic()).isEqualTo(map); + assertThat(Json.encode(dynamicObject)).isEqualTo("{\"k\":\"v\"}"); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/base/DynamicEnumTest.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/base/DynamicEnumTest.java new file mode 100644 index 00000000000..836be565147 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/base/DynamicEnumTest.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.foundation.common.base; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonCreator; + +import io.vertx.core.json.Json; +import io.vertx.core.json.jackson.DatabindCodec; + +class DynamicEnumTest { + static final String UNKNOWN_COLOR = "\"UNKNOWN-COLOR\""; + + static class Color extends DynamicEnum { + public static final Color RED = new Color("RED"); + + public static final Color BLUE = new Color("BLUE"); + + private static final DynamicEnumCache CACHE = new DynamicEnumCache<>(Color.class); + + public Color(String value) { + super(value); + } + + @JsonCreator + public static Color fromValue(String value) { + return CACHE.fromValue(value); + } + } + + static class ColorModel { + public Color color; + } + + @Test + void should_be_dynamic_for_unknown_value() { + Color color = Color.fromValue("unknown"); + assertThat(color.isDynamic()).isTrue(); + assertThat(color.isStatic()).isFalse(); + } + + @Test + void should_be_static_for_known_value() { + Color color = Color.fromValue("RED"); + assertThat(color.isDynamic()).isFalse(); + assertThat(color.isStatic()).isTrue(); + } + + @Test + void should_encode() { + assertThat(Json.encode(Color.RED)).isEqualTo("\"RED\""); + } + + @Test + void should_be_null_when_convert_from_null() { + assertThat(DatabindCodec.mapper().convertValue(null, Color.class)).isNull(); + } + + @Test + void should_be_null_when_decode_from_null() { + ColorModel model = Json.decodeValue(Json.encode(new ColorModel()), ColorModel.class); + assertThat(model.color).isNull(); + } + + @Test + void should_decode_from_known_value() { + Color color = Json.decodeValue(Json.encode(Color.RED), Color.class); + assertThat(color).isEqualTo(Color.RED); + } + + @Test + void should_decode_from_unknown_value() { + Color color = Json.decodeValue(UNKNOWN_COLOR, Color.class); + assertThat(color).isEqualTo(Color.fromValue("UNKNOWN-COLOR")); + } + + @Test + void should_not_cache_unknown_value() { + Color value1 = Json.decodeValue(UNKNOWN_COLOR, Color.class); + Color value2 = Json.decodeValue(UNKNOWN_COLOR, Color.class); + assertThat(value1).isNotSameAs(value2); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/cache/TestVersionedCache.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/cache/TestVersionedCache.java new file mode 100644 index 00000000000..cdfe99f57b8 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/cache/TestVersionedCache.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cache; + +import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; + +import mockit.Deencapsulation; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestVersionedCache { + private static final AtomicInteger VERSION = Deencapsulation.getField(VersionedCache.class, "VERSION"); + + @Test + public void construct() { + VersionedCache cache = new VersionedCache(); + + Assertions.assertNull(cache.data()); + Assertions.assertNull(cache.arrayData()); + Assertions.assertTrue(cache.isEmpty()); + } + + @Test + public void autoCacheVersion() { + VersionedCache cache = new VersionedCache().autoCacheVersion(); + + Assertions.assertEquals(VERSION.get(), cache.cacheVersion()); + } + + @Test + public void setCacheVersion() { + VersionedCache parent = new VersionedCache().autoCacheVersion(); + VersionedCache cache = new VersionedCache().cacheVersion(parent.cacheVersion()); + + Assertions.assertEquals(parent.cacheVersion(), cache.cacheVersion()); + } + + @Test + public void setName() { + VersionedCache cache = new VersionedCache().name("n"); + + Assertions.assertEquals("n", cache.name()); + } + + @Test + public void setSubName() { + VersionedCache parent = new VersionedCache().name("parent"); + VersionedCache child = new VersionedCache().subName(parent, "child"); + + Assertions.assertEquals("parent/child", child.name()); + } + + @Test + public void setMapData() { + VersionedCache cache = new VersionedCache().data(Collections.emptyMap()); + + Assertions.assertSame(Collections.emptyMap(), cache.data()); + Assertions.assertSame(Collections.emptyMap(), cache.mapData()); + Assertions.assertTrue(cache.isEmpty()); + + cache.data(Collections.singletonMap("k", "v")); + Assertions.assertFalse(cache.isEmpty()); + } + + @Test + public void setCollectionData() { + VersionedCache cache = new VersionedCache().data(Collections.emptyList()); + + Assertions.assertSame(Collections.emptyList(), cache.data()); + Assertions.assertSame(Collections.emptyList(), cache.collectionData()); + Assertions.assertTrue(cache.isEmpty()); + + cache.data(Collections.singletonList("v")); + Assertions.assertFalse(cache.isEmpty()); + } + + @Test + public void setArrayData() { + Object[] array = Collections.emptyList().toArray(); + VersionedCache cache = new VersionedCache().data(array); + + Assertions.assertSame(array, cache.data()); + Assertions.assertSame(array, cache.arrayData()); + Assertions.assertTrue(cache.isEmpty()); + + cache.data(new String[] {"a", "b"}); + Assertions.assertFalse(cache.isEmpty()); + } + + @Test + public void setCommonData() { + VersionedCache cache = new VersionedCache().data(null); + + Assertions.assertNull(cache.data()); + Assertions.assertNull(cache.arrayData()); + Assertions.assertTrue(cache.isEmpty()); + + cache.data("a"); + Assertions.assertFalse(cache.isEmpty()); + } + + @Test + public void isExpired() { + VersionedCache cacheOld = new VersionedCache().autoCacheVersion(); + VersionedCache cacheNew = new VersionedCache().autoCacheVersion(); + + Assertions.assertTrue(cacheOld.isExpired(cacheNew)); + Assertions.assertFalse(cacheOld.isExpired(cacheOld)); + Assertions.assertFalse(cacheNew.isExpired(cacheOld)); + } + + @Test + public void isSameVersion() { + VersionedCache cacheOld = new VersionedCache().autoCacheVersion(); + VersionedCache cacheNew = new VersionedCache().autoCacheVersion(); + VersionedCache cacheSame = new VersionedCache().cacheVersion(cacheNew.cacheVersion()); + + Assertions.assertFalse(cacheOld.isSameVersion(cacheNew)); + Assertions.assertTrue(cacheSame.isSameVersion(cacheNew)); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/concurrency/RunnableWrapperTest.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/concurrency/RunnableWrapperTest.java new file mode 100644 index 00000000000..79581ef3985 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/concurrency/RunnableWrapperTest.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.foundation.common.concurrency; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class RunnableWrapperTest { + /** + * {@link SuppressedRunnableWrapper} should ensure that any {@link Throwable} thrown from {@link Runnable} + * should not interrupt the scheduled tasks. + */ + @Test + public void run() { + ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1); + CountDownLatch countDownLatch = new CountDownLatch(3); + + try { + scheduledThreadPoolExecutor.scheduleAtFixedRate( + new SuppressedRunnableWrapper( + () -> { + countDownLatch.countDown(); + switch ((int) countDownLatch.getCount()) { + case 2: + throw new Error("Normal case, this is a mocked error"); + case 1: + throw new IllegalStateException("Normal case, this is a mocked exception"); + default: + } + } + ), + 1, + 1, + TimeUnit.MILLISECONDS); + + countDownLatch.await(1, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("get an InterruptedException! " + e.getMessage()); + e.printStackTrace(); + } finally { + scheduledThreadPoolExecutor.shutdownNow(); + } + + Assertions.assertEquals(0, countDownLatch.getCount()); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/config/BeanProp.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/config/BeanProp.java new file mode 100644 index 00000000000..014be9283f1 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/config/BeanProp.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.foundation.common.config; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class BeanProp { + @JsonProperty(value = "1.2") + private String test; + + public String getTest() { + return test; + } + + public void setTest(String test) { + this.test = test; + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/encrypt/TestEncryptions.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/encrypt/TestEncryptions.java new file mode 100644 index 00000000000..35090bad520 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/encrypt/TestEncryptions.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.foundation.common.encrypt; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestEncryptions { + static class MyEncryption implements Encryption { + + @Override + public char[] decode(char[] encrypted, String tags) { + if (tags == null) { + return null; + } + return encrypted; + } + + @Override + public char[] encode(char[] plain, String tags) { + if (tags == null) { + return null; + } + return plain; + } + } + + @Test + public void testEncryptions() { + Assertions.assertNull(Encryptions.decode((String) null, "")); + Assertions.assertEquals(Encryptions.decode("abcd", ""), "abcd"); + Assertions.assertEquals(Encryptions.decode("abcd", null), "abcd"); + Assertions.assertNull(Encryptions.encode((String) null, "")); + Assertions.assertEquals(Encryptions.encode("abcd", ""), "abcd"); + Assertions.assertEquals(Encryptions.decode("abcd", null), "abcd"); + } + + @Test + public void testEncryptionsMy() { + Encryption old = Encryptions.getEncryption(); + Encryptions.setEncryption(new MyEncryption()); + Assertions.assertNull(Encryptions.decode((String) null, "")); + Assertions.assertEquals(Encryptions.decode("abcd", ""), "abcd"); + Assertions.assertNull(Encryptions.decode("abcd", null)); + Assertions.assertNull(Encryptions.encode((String) null, "")); + Assertions.assertEquals(Encryptions.encode("abcd", ""), "abcd"); + Assertions.assertNull(Encryptions.encode("abcd", null)); + Encryptions.setEncryption(old); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventBus.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventBus.java new file mode 100644 index 00000000000..68cac748436 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventBus.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.event; + +import java.util.ArrayList; +import java.util.List; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import org.junit.jupiter.api.Test; + +public class TestEventBus { + private final EventBus eventBus = new SimpleEventBus(); + + private final List events = new ArrayList<>(); + + public static class SubscriberForTest { + private final List events; + + public SubscriberForTest(List events) { + this.events = events; + } + @Subscribe + @AllowConcurrentEvents + public void s1(Integer event) { + events.add(event); + } + + @Subscribe + public void s2(String event) { + events.add(event); + } + } + + public static class SubscriberWithOrderForTest { + private final List events; + + public SubscriberWithOrderForTest(List events) { + this.events = events; + } + + @Subscribe + @SubscriberOrder(100) + public void s1(String event) { + events.add("s1:" + event); + } + + @Subscribe + @SubscriberOrder(-100) + public void s2(String event) { + events.add("s2:" + event); + } + } + + @Test + public void order() { + eventBus.register(new SubscriberWithOrderForTest(events)); + + eventBus.post("value"); + MatcherAssert.assertThat(events, Matchers.contains("s2:value", "s1:value")); + } + + @Test + public void unregister() { + Object obj = new SubscriberForTest(events); + + eventBus.register(obj); + eventBus.unregister(obj); + } + + @Test + public void oneSubscriber() { + Object obj = new SubscriberForTest(events); + + eventBus.register(obj); + + eventBus.post(0.1); + eventBus.post(1); + eventBus.post("str"); + MatcherAssert.assertThat(events, Matchers.contains(1, "str")); + + eventBus.unregister(obj); + + events.clear(); + eventBus.post(0.1); + eventBus.post(1); + eventBus.post("str"); + MatcherAssert.assertThat(events, Matchers.empty()); + } + + @Test + public void twoSubscriber() { + Object obj1 = new SubscriberForTest(events); + Object obj2 = new SubscriberForTest(events); + + eventBus.register(obj1); + eventBus.register(obj2); + + eventBus.post(0.1); + eventBus.post(1); + eventBus.post("str"); + MatcherAssert.assertThat(events, Matchers.contains(1, 1, "str", "str")); + + events.clear(); + eventBus.unregister(obj1); + eventBus.post(0.1); + eventBus.post(1); + eventBus.post("str"); + MatcherAssert.assertThat(events, Matchers.contains(1, "str")); + } +} 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 new file mode 100644 index 00000000000..736a933159b --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventManager.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.foundation.common.event; + +import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; + +import com.google.common.eventbus.Subscribe; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestEventManager { + private int objCount = 0; + + private int iCount = 0; + + public void test(Object listener) { + EventManager.register(listener); + EventManager.post("a"); + EventManager.post(1); + Assertions.assertEquals(2, objCount); + Assertions.assertEquals(1, iCount); + + EventManager.unregister(listener); + EventManager.post("a"); + EventManager.post(1); + Assertions.assertEquals(2, objCount); + Assertions.assertEquals(1, iCount); + } + + @Test + public void normalListener() { + LogCollector collector = new LogCollector(); + + test(this); + Assertions.assertTrue(collector.getEvents().isEmpty()); // ensure no warning logs + collector.tearDown(); + } + + @Test + public void anonymousListener() { + LogCollector collector = new LogCollector(); + Object listener = new Object() { + @Subscribe + private void onObject(Object obj) { + objCount++; + } + + @Subscribe + void onInt(Integer obj) { + iCount++; + } + }; + try { + test(listener); + Assertions.fail(); + } catch (IllegalStateException e) { + Assertions.assertTrue(true); + } + + collector.tearDown(); + } + + @Test + public void anonymousListenerPublic() { + LogCollector collector = new LogCollector(); + Object listener = new Object() { + @Subscribe + public void onObject(Object obj) { + objCount++; + } + + @Subscribe + public void onInt(Integer obj) { + iCount++; + } + }; + try { + test(listener); + Assertions.fail(); + } catch (IllegalStateException e) { + Assertions.assertTrue(true); + } + + // ensure logs: "LOGGER.warn("Failed to create lambda for method: {}, fallback to reflect.", method, throwable);" + Assertions.assertFalse(collector.getEvents().isEmpty()); + collector.tearDown(); + } + + @Subscribe + public void onObject(Object obj) { + objCount++; + } + + @Subscribe + 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 new file mode 100644 index 00000000000..b48ceeb4ee0 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpStatus.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.foundation.common.http; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status.Family; + +public class TestHttpStatus { + @Test + public void testIsSuccessCode() { + Assertions.assertTrue(HttpStatus.isSuccess(200)); + Assertions.assertFalse(HttpStatus.isSuccess(300)); + } + + @Test + public void testIsSuccessType() { + Assertions.assertTrue(HttpStatus.isSuccess(Status.OK)); + Assertions.assertFalse(HttpStatus.isSuccess(Status.BAD_REQUEST)); + } + + @Test + public void testGetStatusCode() { + HttpStatus status = new HttpStatus(200, "ok"); + Assertions.assertEquals(200, status.getStatusCode()); + } + + @Test + public void testGetFamily() { + HttpStatus status = new HttpStatus(200, "ok"); + Assertions.assertEquals(Family.familyOf(200), status.getFamily()); + } + + @Test + public void testGetReasonPhrase() { + HttpStatus status = new HttpStatus(200, "ok"); + Assertions.assertEquals("ok", status.getReasonPhrase()); + } +} 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 new file mode 100644 index 00000000000..87776b326ef --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpStatusUtils.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.foundation.common.http; + +import jakarta.ws.rs.core.Response.StatusType; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestHttpStatusUtils { + HttpStatusManager mgr = new HttpStatusManager(); + + StatusType st; + + @Test + public void testStandard() { + st = HttpStatusUtils.getOrCreateByStatusCode(200); + Assertions.assertEquals(200, st.getStatusCode()); + } + + @Test + public void testNotStandard() { + st = mgr.getOrCreateByStatusCode(250); + Assertions.assertEquals(250, st.getStatusCode()); + } + + @Test + public void testAddRepeat() { + IllegalStateException illegalStateException = Assertions.assertThrows(IllegalStateException.class, + () -> mgr.addStatusType(new HttpStatus(200, "ok"))); + Assertions.assertEquals("repeated status code: 200", illegalStateException.getMessage()); + } +} 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 new file mode 100644 index 00000000000..113df2905f1 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/http/TestHttpUtils.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.http; + +import java.net.URISyntaxException; + +import jakarta.ws.rs.core.MediaType; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestHttpUtils { + + @Test + public void parseParamFromHeaderValue_normal() { + 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, "key")); + Assertions.assertNull(HttpUtils.parseParamFromHeaderValue("key1=a; key2=b", "key")); + } + + @Test + public void parseParamFromHeaderValue_emptyStr() { + Assertions.assertEquals("", HttpUtils.parseParamFromHeaderValue("key1=a; key2=", "key2")); + } + + @Test + public void uriEncode_null() { + Assertions.assertEquals("", HttpUtils.uriEncodePath(null)); + } + + @Test + public void uriDecode_null() { + Assertions.assertNull(HttpUtils.uriDecodePath(null)); + } + + @Test + public void uriEncode_chineseAndSpace() { + String encoded = HttpUtils.uriEncodePath("测 试"); + Assertions.assertEquals("%E6%B5%8B%20%E8%AF%95", encoded); + Assertions.assertEquals("测 试", HttpUtils.uriDecodePath(encoded)); + } + + @Test + public void uriEncode_failed() { + IllegalArgumentException illegalArgumentException = Assertions.assertThrows(IllegalArgumentException.class, + () -> HttpUtils.uriEncodePath(":")); + Assertions.assertEquals("uriEncode failed, path=\":\".", illegalArgumentException.getMessage()); + Assertions.assertTrue(illegalArgumentException.getCause() instanceof URISyntaxException); + } + + @Test + public void uriEncode_plus() { + String encoded = HttpUtils.uriEncodePath("a+b"); + Assertions.assertEquals("a+b", encoded); + Assertions.assertEquals("a+b", HttpUtils.uriDecodePath(encoded)); + } + + @Test + public void uriEncode_encodeEntirePath() { + String encoded = HttpUtils.uriEncodePath("a%%'+b/def"); + Assertions.assertEquals("a%25%25'+b/def", encoded); + } + + @Test + public void pathParamEncode() { + Assertions.assertEquals("a+b", HttpUtils.encodePathParam("a+b")); + Assertions.assertEquals("a%25b", HttpUtils.encodePathParam("a%b")); + Assertions.assertEquals("a%25%25b", HttpUtils.encodePathParam("a%%b")); + Assertions.assertEquals("%3C%20%3E'%22%EF%BC%88)&%2F%20%20", HttpUtils.encodePathParam("< >'\"()&/ ")); + Assertions.assertEquals("%E6%B5%8B%20%E8%AF%95", HttpUtils.encodePathParam("测 试")); + } + + /** + * SafeChar: the characters that are not encoded. + * This test is to show those safe chars excepting 0..9, a..z and A..Z + */ + @Test + public void pathParamEncode_SafeChar() { + Assertions.assertEquals("-._~!$'()*,;&=@:+", HttpUtils.encodePathParam("-._~!$'()*,;&=@:+")); + } + + @Test + public void uriDecode_failed() { + IllegalArgumentException illegalArgumentException = Assertions.assertThrows(IllegalArgumentException.class, + () -> HttpUtils.uriDecodePath(":")); + Assertions.assertEquals("uriDecode failed, path=\":\".", illegalArgumentException.getMessage()); + Assertions.assertTrue(illegalArgumentException.getCause() instanceof URISyntaxException); + } + + @Test + public void parseFileNameFromHeaderValue() { + String fileName = "测 试.txt"; + String encoded = HttpUtils.uriEncodePath(fileName); + Assertions.assertEquals(fileName, HttpUtils.parseFileNameFromHeaderValue("xx;filename=" + encoded)); + } + + @Test + public void parseFileNameFromHeaderValue_defaultName() { + Assertions.assertEquals("default", HttpUtils.parseFileNameFromHeaderValue("xx")); + } + + @Test + public void parseFileNameFromHeaderValue_ignorePath() { + Assertions.assertEquals("a.txt", HttpUtils.parseFileNameFromHeaderValue("xx;filename=../../a.txt")); + } + + @Test + public void getCharsetFromContentType_noContentType() { + String character = HttpUtils.getCharsetFromContentType(null); + + Assertions.assertNull(character); + } + + @Test + public void getCharsetFromContentType_noCharset() { + String character = HttpUtils.getCharsetFromContentType(MediaType.APPLICATION_JSON); + + Assertions.assertNull(character); + } + + @Test + public void getCharsetFromContentType_noSemicolonEnd() { + String character = HttpUtils.getCharsetFromContentType(MediaType.APPLICATION_JSON + ";charset=utf-8"); + + Assertions.assertEquals("utf-8", character); + } + + @Test + public void getCharsetFromContentType_semicolonEnd() { + String character = HttpUtils.getCharsetFromContentType(MediaType.APPLICATION_JSON + ";charset=utf-8;"); + + Assertions.assertEquals("utf-8", character); + } + + @Test + public void getCharsetFromContentType_needTrim() { + String character = HttpUtils.getCharsetFromContentType(MediaType.APPLICATION_JSON + ";charset= utf-8 ;"); + + Assertions.assertEquals("utf-8", character); + } + + @Test + public void getCharsetFromContentType_quotationMarks() { + String character = HttpUtils.getCharsetFromContentType(MediaType.APPLICATION_JSON + ";charset=\"utf-8\";"); + + Assertions.assertEquals("utf-8", character); + } + + @Test + public void getCharsetFromContentType_quotationMarks_needTrim() { + String character = HttpUtils.getCharsetFromContentType(MediaType.APPLICATION_JSON + ";charset=\" utf-8 \";"); + + Assertions.assertEquals("utf-8", character); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestIpPort.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestIpPort.java new file mode 100644 index 00000000000..9194515cb28 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestIpPort.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.foundation.common.net; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestIpPort { + @Test + public void testIpPort() { + IpPort inst1 = new IpPort(); + inst1.setHostOrIp("localhost"); + inst1.setPort(3333); + IpPort inst2 = new IpPort("localhost", 3333); + Assertions.assertEquals(inst1.getHostOrIp(), inst2.getHostOrIp()); + Assertions.assertEquals(inst1.getPort(), inst2.getPort()); + Assertions.assertEquals(inst1.getSocketAddress().getHostName(), inst2.getSocketAddress().getHostName()); + Assertions.assertEquals(inst1, inst1); + Assertions.assertEquals(inst1, inst2); + Assertions.assertEquals(inst1.toString(), "localhost:3333"); + Assertions.assertNotEquals(inst1, new Object()); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestNetUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestNetUtils.java new file mode 100644 index 00000000000..dd13d96d4c6 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestNetUtils.java @@ -0,0 +1,241 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import mockit.Deencapsulation; + +public class TestNetUtils { + @Test + public void testFindProperHostAddress() { + NetUtils.resetHostName(); + String result = NetUtils.getHostName(); + System.out.println(result); + Assertions.assertNotNull(result); + + result = NetUtils.getHostAddress(); + System.out.println(result); + Assertions.assertNotNull(result); + + result = NetUtils.getIpv6HostAddress(); + System.out.println(result); + if (result != null) { + Assertions.assertFalse(result.contains("%")); + } + } + + @Test + public void testIpPort() { + IpPort oIPPort = new IpPort("10.145.154.45", 8080); + Assertions.assertEquals("10.145.154.45", oIPPort.getHostOrIp()); + Assertions.assertEquals(8080, oIPPort.getPort()); + oIPPort.setPort(9090); + Assertions.assertEquals(9090, oIPPort.getPort()); + Assertions.assertNotEquals(null, oIPPort.getSocketAddress()); + } + + @Test + public void testNetUtils() { + Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPort("127.0.0.1:8080").getHostOrIp()); + Assertions.assertEquals(8080, NetUtils.parseIpPort("127.0.0.1:8080").getPort()); + Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPort("127.0.0.1").getHostOrIp()); + Assertions.assertEquals(-1, NetUtils.parseIpPort("127.0.0.1").getPort()); + Assertions.assertNull(NetUtils.parseIpPort((String) null)); + Assertions.assertNull(NetUtils.parseIpPortFromURI(null)); + Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPortFromURI("rest://127.0.0.1:8080").getHostOrIp()); + Assertions.assertEquals(8080, NetUtils.parseIpPortFromURI("http://127.0.0.1:8080").getPort()); + Assertions.assertEquals(80, NetUtils.parseIpPortFromURI("http://127.0.0.1").getPort()); + Assertions.assertEquals(8080, NetUtils.parseIpPortFromURI("https://127.0.0.1:8080").getPort()); + Assertions.assertEquals(443, NetUtils.parseIpPortFromURI("https://127.0.0.1").getPort()); + + Assertions.assertEquals(30000, NetUtils.parseIpPort("http", "127.0.0.1:30000").getPort()); + Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPort("http", "127.0.0.1:30000").getHostOrIp()); + Assertions.assertEquals(30000, NetUtils.parseIpPort("https", "127.0.0.1:30000").getPort()); + Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPort("https", "127.0.0.1:30000").getHostOrIp()); + Assertions.assertEquals(80, NetUtils.parseIpPort("http", "127.0.0.1").getPort()); + Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPort("http", "127.0.0.1").getHostOrIp()); + Assertions.assertEquals(443, NetUtils.parseIpPort("https", "127.0.0.1").getPort()); + Assertions.assertEquals("127.0.0.1", NetUtils.parseIpPort("https", "127.0.0.1").getHostOrIp()); + Assertions.assertNull(NetUtils.parseIpPort("http", null)); + + checkException(v -> NetUtils.parseIpPort("127.0.0.18080")); + checkException(v -> NetUtils.parseIpPortFromURI("ss")); + } + + @Test + public void testNetUtilsIPv6() { + Assertions.assertEquals("[::1]", NetUtils.parseIpPort("[::1]:8080").getHostOrIp()); + Assertions.assertEquals("[::]", NetUtils.parseIpPort("[::]:8080").getHostOrIp()); + Assertions.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]", + NetUtils.parseIpPort("[fe80::f816:3eff:feda:38cd%eth0]:8080").getHostOrIp()); + Assertions.assertEquals("[fe80::38f7:44b8:8ab1:468%16]", + NetUtils.parseIpPort("[fe80::38f7:44b8:8ab1:468%16]:8080").getHostOrIp()); + Assertions.assertEquals(8080, NetUtils.parseIpPort("[::1]:8080").getPort()); + Assertions.assertEquals(8080, NetUtils.parseIpPort("[::]:8080").getPort()); + Assertions.assertEquals(8080, NetUtils.parseIpPort("[fe80::f816:3eff:feda:38cd%eth0]:8080").getPort()); + Assertions.assertEquals(8080, NetUtils.parseIpPort("[fe80::38f7:44b8:8ab1:468%16]:8080").getPort()); + + Assertions.assertEquals("[::1]", NetUtils.parseIpPortFromURI("rest://[::1]:8080").getHostOrIp()); + Assertions.assertEquals("[::]", NetUtils.parseIpPortFromURI("rest://[::]:8080").getHostOrIp()); + Assertions.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]", + NetUtils.parseIpPortFromURI("rest://[fe80::f816:3eff:feda:38cd%eth0]:8080").getHostOrIp()); + Assertions.assertEquals("[fe80::38f7:44b8:8ab1:468%16]", + NetUtils.parseIpPortFromURI("rest://[fe80::38f7:44b8:8ab1:468%16]:8080").getHostOrIp()); + Assertions.assertEquals(8080, NetUtils.parseIpPortFromURI("rest://[::1]:8080").getPort()); + Assertions.assertEquals(80, NetUtils.parseIpPortFromURI("http://[::1]").getPort()); + Assertions.assertEquals(8080, NetUtils.parseIpPortFromURI("https://[::1]:8080").getPort()); + Assertions.assertEquals(443, NetUtils.parseIpPortFromURI("https://[::1]").getPort()); + + Assertions.assertEquals(30000, NetUtils.parseIpPort("http", "[fe80::f816:3eff:feda:38cd%eth0]:30000").getPort()); + Assertions.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]", + NetUtils.parseIpPort("http", "[fe80::f816:3eff:feda:38cd%eth0]:30000").getHostOrIp()); + Assertions.assertEquals(30000, NetUtils.parseIpPort("https", "[fe80::f816:3eff:feda:38cd%eth0]:30000").getPort()); + Assertions.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]", + NetUtils.parseIpPort("https", "[fe80::f816:3eff:feda:38cd%eth0]:30000").getHostOrIp()); + Assertions.assertEquals(80, NetUtils.parseIpPort("http", "[fe80::f816:3eff:feda:38cd%eth0]").getPort()); + Assertions.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]", + NetUtils.parseIpPort("http", "[fe80::f816:3eff:feda:38cd%eth0]").getHostOrIp()); + Assertions.assertEquals(443, NetUtils.parseIpPort("https", "[fe80::f816:3eff:feda:38cd%eth0]").getPort()); + Assertions.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]", + NetUtils.parseIpPort("https", "[fe80::f816:3eff:feda:38cd%eth0]").getHostOrIp()); + } + + @Test + public void testFullOperation() { + Assertions.assertNotNull(NetUtils.getHostAddress()); + Assertions.assertNotNull(NetUtils.getHostName()); + } + + @Test + public void testGetRealListenAddress() { + Assertions.assertNull(NetUtils.getRealListenAddress("http", null)); + Assertions.assertEquals("http://1.1.1.1:8080", NetUtils.getRealListenAddress("http", "1.1.1.1:8080")); + + checkException(v -> NetUtils.getRealListenAddress("http:1", "1.1.1.1:8080")); + } + + @Test + public void testNetworkInterface() { + Map org = Deencapsulation.getField(NetUtils.class, "allInterfaceAddresses"); + + Map newValue = new HashMap<>(); + InetAddress addr = Mockito.mock(InetAddress.class); + newValue.put("eth100", addr); + Deencapsulation.setField(NetUtils.class, "allInterfaceAddresses", newValue); + + Assertions.assertEquals(addr, NetUtils.getInterfaceAddress("eth100")); + Assertions.assertEquals(addr, NetUtils.ensureGetInterfaceAddress("eth100")); + + try { + NetUtils.ensureGetInterfaceAddress("xxx"); + Assertions.fail("must throw exception"); + } catch (IllegalArgumentException e) { + Assertions.assertEquals("Can not find address for interface name: xxx", e.getMessage()); + } + + Deencapsulation.setField(NetUtils.class, "allInterfaceAddresses", org); + } + + @Test + public void testCanTcpListenNo() throws IOException { + InetAddress address = InetAddress.getByName("127.0.0.1"); + try (ServerSocket ss = new ServerSocket(0, 0, address)) { + Assertions.assertFalse(NetUtils.canTcpListen(address, ss.getLocalPort())); + } + } + + @Test + public void testCanTcpListenYes() throws IOException { + InetAddress address = InetAddress.getByName("127.0.0.1"); + ServerSocket ss = new ServerSocket(0, 0, address); + int port = ss.getLocalPort(); + ss.close(); + + Assertions.assertTrue(NetUtils.canTcpListen(address, port)); + } + + @Test + public void humanReadableBytes() { + Assertions.assertEquals("0", NetUtils.humanReadableBytes(0L)); + Assertions.assertEquals("1", NetUtils.humanReadableBytes(1L)); + Assertions.assertEquals("1023", NetUtils.humanReadableBytes(1023L)); + + Assertions.assertEquals("1.000K", NetUtils.humanReadableBytes(1024L)); + Assertions.assertEquals("1.001K", NetUtils.humanReadableBytes(1025L)); + Assertions.assertEquals("1023.999K", NetUtils.humanReadableBytes(1024L * 1024 - 1)); + + Assertions.assertEquals("1.000M", NetUtils.humanReadableBytes(1024L * 1024)); + Assertions.assertEquals("1.000M", NetUtils.humanReadableBytes(1024L * 1024 + 1)); + Assertions.assertEquals("1.001M", NetUtils.humanReadableBytes(1024L * 1024 + 1024)); + Assertions.assertEquals("1023.999M", NetUtils.humanReadableBytes(1024L * 1024 * 1024 - 1024)); + Assertions.assertEquals("1024.000M", NetUtils.humanReadableBytes(1024L * 1024 * 1024 - 1)); + Assertions.assertEquals("1.000G", NetUtils.humanReadableBytes(1024L * 1024 * 1024)); + Assertions.assertEquals("1.000G", NetUtils.humanReadableBytes(1024L * 1024 * 1024 + 1)); + Assertions.assertEquals("1.000G", NetUtils.humanReadableBytes(1024L * 1024 * 1024 + 1024)); + Assertions.assertEquals("1023.999G", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 - 1024 * 1024)); + Assertions.assertEquals("1024.000G", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 - 1024)); + Assertions.assertEquals("1.000T", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024)); + Assertions.assertEquals("1.001T", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 + 1024 * 1024 * 1024)); + Assertions.assertEquals("1023.999T", + NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 * 1024 - 1024L * 1024 * 1024)); + + Assertions.assertEquals("1.000P", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 * 1024)); + Assertions.assertEquals("1.001P", + NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 * 1024 + 1024L * 1024 * 1024 * 1024)); + Assertions.assertEquals("1023.999P", + NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 * 1024 * 1024 - 1024L * 1024 * 1024 * 1024)); + + Assertions.assertEquals("1.000E", NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 * 1024 * 1024)); + Assertions.assertEquals("1.001E", + NetUtils.humanReadableBytes(1024L * 1024 * 1024 * 1024 * 1024 * 1024 + 1024L * 1024 * 1024 * 1024 * 1024)); + Assertions.assertEquals("8.000E", NetUtils.humanReadableBytes(Long.MAX_VALUE)); + } + + @Test + public void testGetHostName() { + Assertions.assertNotEquals(null, NetUtils.getHostName()); + Deencapsulation.setField(NetUtils.class, "hostName", null); + Assertions.assertNotEquals(null, NetUtils.getHostName()); + } + + @Test + public void testGetHostAddress() { + Assertions.assertNotEquals(null, NetUtils.getHostAddress()); + Deencapsulation.setField(NetUtils.class, "hostAddress", null); + Assertions.assertNotEquals(null, NetUtils.getHostAddress()); + } + + public void checkException(Consumer testedBehavior) { + try { + testedBehavior.accept(null); + Assertions.fail("IllegalArgumentException is expected!"); + } catch (Exception e) { + Assertions.assertEquals(IllegalArgumentException.class, e.getClass()); + } + } +} 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 new file mode 100644 index 00000000000..dc3323fecc3 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestURIEndpointObject.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.net; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; + +import mockit.Deencapsulation; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestURIEndpointObject { + @Test + public void testRestEndpointObject() { + URIEndpointObject obj = new URIEndpointObject("http://127.0.2.0:8080"); + Assertions.assertEquals("127.0.2.0", obj.getHostOrIp()); + Assertions.assertEquals(8080, obj.getPort()); + Assertions.assertFalse(obj.isSslEnabled()); + + obj = new URIEndpointObject("http://127.0.2.0:8080?sslEnabled=true"); + Assertions.assertEquals("127.0.2.0", obj.getHostOrIp()); + Assertions.assertEquals(8080, obj.getPort()); + Assertions.assertTrue(obj.isSslEnabled()); + Assertions.assertNull(obj.getFirst("notExist")); + + obj = new URIEndpointObject("http://127.0.2.0:8080?sslEnabled=true&protocol=http2"); + Assertions.assertEquals("127.0.2.0", obj.getHostOrIp()); + Assertions.assertEquals(8080, obj.getPort()); + Assertions.assertTrue(obj.isSslEnabled()); + Assertions.assertTrue(obj.isHttp2Enabled()); + Assertions.assertNull(obj.getFirst("notExist")); + + obj = new URIEndpointObject("rest://127.0.2.0:8080?urlPrefix=%2Froot"); + Assertions.assertEquals("127.0.2.0", obj.getHostOrIp()); + Assertions.assertEquals(8080, obj.getPort()); + Assertions.assertEquals("/root", obj.getQuery("urlPrefix").get(0)); + } + + @Test + public void testRestEndpointObjectException() { + Assertions.assertThrows(IllegalArgumentException.class, () -> new URIEndpointObject("http://127.0.2.0")); + } + + @Test + public void testQueryChineseAndSpaceAndEmpty() throws UnsupportedEncodingException { + String strUri = + "cse://1.1.1.1:1234/abc?a=1&b=&country=" + URLEncoder.encode("中 国", StandardCharsets.UTF_8.name()); + URIEndpointObject ep = new URIEndpointObject(strUri); + + Map> queries = Deencapsulation.getField(ep, "queries"); + Assertions.assertEquals(3, queries.size()); + + Assertions.assertEquals(1, ep.getQuery("a").size()); + Assertions.assertEquals("1", ep.getFirst("a")); + + Assertions.assertEquals(1, ep.getQuery("b").size()); + Assertions.assertEquals("", ep.getFirst("b")); + + Assertions.assertEquals(1, ep.getQuery("country").size()); + Assertions.assertEquals("中 国", ep.getFirst("country")); + } +} 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 new file mode 100644 index 00000000000..ae84a460e98 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestAbstractPart.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.foundation.common.part; + +import java.io.IOException; + +import jakarta.ws.rs.core.MediaType; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestAbstractPart { + AbstractPart part = new AbstractPart(); + + private void checkError(Error error) { + Assertions.assertEquals("not supported method", error.getMessage()); + } + + @Test + public void getInputStream() throws IOException { + Error error = Assertions.assertThrows(Error.class, () -> part.getInputStream()); + checkError(error); + } + + @Test + public void getContentType() throws IOException { + Assertions.assertEquals(MediaType.APPLICATION_OCTET_STREAM, part.getContentType()); + + String contentType = "abc"; + part.contentType(contentType); + Assertions.assertEquals(contentType, part.getContentType()); + } + + @Test + public void getName() throws IOException { + Assertions.assertNull(part.getName()); + + String name = "abc"; + part.name = name; + Assertions.assertEquals(name, part.getName()); + } + + @Test + public void getSubmittedFileName() throws IOException { + Assertions.assertNull(part.getSubmittedFileName()); + + String submittedFileName = "abc"; + part.setSubmittedFileName(submittedFileName); + Assertions.assertEquals(submittedFileName, part.getSubmittedFileName()); + } + + @Test + public void setSubmittedFileName_contentTypeNotNull() { + part.contentType(MediaType.TEXT_PLAIN); + part.setSubmittedFileName("a.zip"); + + Assertions.assertEquals(MediaType.TEXT_PLAIN, part.getContentType()); + } + + @Test + public void setSubmittedFileName_setNull() { + part.setSubmittedFileName(null); + + Assertions.assertEquals(MediaType.APPLICATION_OCTET_STREAM, part.getContentType()); + } + + @Test + public void setSubmittedFileName_setNormal() { + part.setSubmittedFileName("a.zip"); + + Assertions.assertEquals("application/zip", part.getContentType()); + } + + @Test + public void getSize() { + Error error = Assertions.assertThrows(Error.class, () -> part.getSize()); + checkError(error); + } + + @Test + public void write() throws IOException { + Error error = Assertions.assertThrows(Error.class, () -> part.write("file")); + checkError(error); + } + + @Test + public void delete() throws IOException { + Error error = Assertions.assertThrows(Error.class, () -> part.delete()); + checkError(error); + } + + @Test + public void getHeader() { + Error error = Assertions.assertThrows(Error.class, () -> part.getHeader("header")); + checkError(error); + } + + @Test + public void getHeaders() { + Error error = Assertions.assertThrows(Error.class, () -> part.getHeaders("header")); + checkError(error); + } + + @Test + public void getHeaderNames() { + Error error = Assertions.assertThrows(Error.class, () -> part.getHeaderNames()); + checkError(error); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestFilePart.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestFilePart.java new file mode 100644 index 00000000000..b129bc2ec42 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestFilePart.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.common.part; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +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 TestFilePart { + static File file = new File("testFilePart.txt"); + + static String content = "testFilePart content"; + + String name = "paramName"; + + FilePart part = new FilePart(name, file.getAbsolutePath()); + + @BeforeAll + public static void setup() throws IOException { + file.delete(); + FileUtils.write(file, content, StandardCharsets.UTF_8); + } + + @AfterAll + public static void teardown() { + file.delete(); + } + + @Test + public void getName() { + Assertions.assertEquals(name, part.getName()); + } + + @Test + public void getInputStream() throws IOException { + try (InputStream is = part.getInputStream()) { + Assertions.assertEquals(content, IOUtils.toString(is, StandardCharsets.UTF_8)); + } + } + + @Test + public void getSize() { + Assertions.assertEquals(content.length(), part.getSize()); + } + + @Test + public void write() throws IOException { + File destFile = new File("testFilePartCopy.txt"); + + part.write(destFile.getPath()); + Assertions.assertEquals(content, FileUtils.readFileToString(destFile, StandardCharsets.UTF_8)); + + FilePart destPart = new FilePart(null, destFile); + destPart.delete(); + Assertions.assertFalse(destFile.exists()); + } + + @Test + public void deleteAfterFinished() { + Assertions.assertFalse(part.isDeleteAfterFinished()); + + Assertions.assertTrue(part.setDeleteAfterFinished(true).isDeleteAfterFinished()); + } + + @Test + public void getAbsolutePath() { + Assertions.assertEquals(file.getAbsolutePath(), part.getAbsolutePath()); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestInputStreamPart.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestInputStreamPart.java new file mode 100644 index 00000000000..6af3016ecd3 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestInputStreamPart.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.foundation.common.part; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestInputStreamPart { + String name = "paramName"; + + byte[] bytes = new byte[] {1, 2, 3}; + + InputStream is = new ByteArrayInputStream(bytes); + + InputStreamPart part = new InputStreamPart(name, is); + + @Test + public void getName() { + Assertions.assertEquals(name, part.getName()); + } + + @Test + public void test() throws IOException { + try (InputStream is = part.getInputStream()) { + byte[] content = IOUtils.toByteArray(is); + Assertions.assertArrayEquals(bytes, content); + } + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestResourcePart.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestResourcePart.java new file mode 100644 index 00000000000..e0d398e697c --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/part/TestResourcePart.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.common.part; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; + +public class TestResourcePart { + String name = "paramName"; + + byte[] bytes = new byte[] {1, 2, 3}; + + Resource resource = new ByteArrayResource(bytes); + + ResourcePart part = new ResourcePart(name, resource); + + @Test + public void getName() { + Assertions.assertEquals(name, part.getName()); + } + + @Test + public void getInputStream() throws IOException { + try (InputStream is = part.getInputStream()) { + byte[] content = IOUtils.toByteArray(is); + Assertions.assertArrayEquals(bytes, content); + } + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/spring/TestPaasNamespaceHandler.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/spring/TestPaasNamespaceHandler.java new file mode 100644 index 00000000000..41d5c03640a --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/spring/TestPaasNamespaceHandler.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.foundation.common.spring; + +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 TestPaasNamespaceHandler { + + PaasNamespaceHandler paasNamespaceHandler = null; + + @BeforeEach + public void setUp() throws Exception { + paasNamespaceHandler = new PaasNamespaceHandler(); + } + + @AfterEach + public void tearDown() throws Exception { + paasNamespaceHandler = null; + } + + @Test + public void testInit() { + boolean status = false; + try { + paasNamespaceHandler.init(); + } catch (Exception e) { + status = true; + } + Assertions.assertFalse(status); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/AsyncUtilsTest.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/AsyncUtilsTest.java new file mode 100644 index 00000000000..7ec1a215034 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/AsyncUtilsTest.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.foundation.common.utils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; +import org.junit.jupiter.api.Test; + +class AsyncUtilsTest { + @Test + void should_convert_success_async_to_sync() { + CompletableFuture future = CompletableFuture.completedFuture("value"); + + assertThat(AsyncUtils.toSync(future)).isEqualTo("value"); + } + + @Test + void should_convert_fail_async_to_sync() { + RuntimeExceptionWithoutStackTrace throwable = new RuntimeExceptionWithoutStackTrace(); + CompletableFuture future = AsyncUtils.completeExceptionally(throwable); + + Throwable catchThrowable = catchThrowable(() -> AsyncUtils.toSync(future)); + assertThat(catchThrowable).isSameAs(throwable); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/JsonUtilsTest.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/JsonUtilsTest.java new file mode 100644 index 00000000000..60c5d9c43a0 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/JsonUtilsTest.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.foundation.common.utils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +public class JsonUtilsTest { + + @Test + void testWriteUnicodeValueAsString() throws JsonProcessingException { + String unicodeStr = "测试"; + Map inMap = new HashMap<>(); + inMap.put("key", unicodeStr); + + Assertions.assertFalse(StringUtils.isAsciiPrintable(JsonUtils.writeValueAsString(inMap))); + String jsonStr = JsonUtils.writeUnicodeValueAsString(inMap); + Assertions.assertTrue(StringUtils.isAsciiPrintable(jsonStr)); + + Map outMap = JsonUtils.OBJ_MAPPER.readValue(jsonStr, new TypeReference>() { + }); + Assertions.assertEquals(unicodeStr, outMap.get("key")); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/ResourceUtilTest.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/ResourceUtilTest.java new file mode 100644 index 00000000000..9982eca9252 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/ResourceUtilTest.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.foundation.common.utils; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ResourceUtilTest { + + @Test + public void loadResources_in_disk() throws IOException, URISyntaxException { + List uris = ResourceUtil.findResourcesBySuffix("META-INF/spring", ".xml"); + Assertions.assertEquals(1, uris.size()); + URI uri = uris.get(0); + Assertions.assertTrue(uri.toString().startsWith("file:"), "unexpected uri: " + uri); + Assertions.assertTrue(uri.toString().endsWith("META-INF/spring/config.bean.xml"), "unexpected uri: " + uri); + } + + @Test + public void loadResources_exact_file_in_disk() throws IOException, URISyntaxException { + List uris = ResourceUtil.findResources("META-INF/spring/config.bean.xml"); + Assertions.assertEquals(1, uris.size()); + URI uri = uris.get(0); + Assertions.assertTrue(uri.toString().startsWith("file:"), "unexpected uri: " + uri); + Assertions.assertTrue(uri.toString().endsWith("META-INF/spring/config.bean.xml"), "unexpected uri: " + uri); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestExceptionUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestExceptionUtils.java new file mode 100644 index 00000000000..a9746b078e6 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestExceptionUtils.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.foundation.common.utils; + +import java.io.IOException; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestExceptionUtils { + @Test + public void testExceptionUtils() { + Exception e = new Exception("Hello"); + Assertions.assertEquals("cause:Exception,message:Hello", ExceptionUtils.getExceptionMessageWithoutTrace(e)); + Exception e2 = new Exception("FAIL", new IOException("IO")); + Assertions.assertEquals("cause:Exception,message:FAIL;cause:IOException,message:IO", + ExceptionUtils.getExceptionMessageWithoutTrace(e2)); + } +} 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 new file mode 100644 index 00000000000..0ccf049ffba --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFileNameTooLong.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.utils; + +import java.io.File; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestFileNameTooLong { + private static final Logger LOGGER = LoggerFactory.getLogger(TestFileNameTooLong.class); + + // ensure the maximum file path do not exceed. now set to 200(some old windows max 260) + // smaller is better, can refactor in future + // NOTICE: compiled classes or other generated files like surefire reports may exceed this size + private static final int MAN_FILE_SIZE = 200; + + @Test + public void assertFileNotTooLong() { + File folder = new File(System.getProperty("user.dir")); + LOGGER.error(folder.getAbsolutePath()); // $ROOT\foundations\foundation-common + File root = new File(folder.getParentFile().getParent()); + LOGGER.error(root.getAbsolutePath()); // $ROOT\foundations\foundation-common + Assertions.assertTrue(root.exists()); + Assertions.assertTrue(root.isDirectory()); + + List names = new LinkedList<>(); + findLongFileName(root, names, root.getAbsolutePath().length()); + Collections.sort(names); + names.forEach(LOGGER::error); + if (!names.isEmpty()) { + // for debug + Assertions.assertEquals("", names.toString()); + } + Assertions.assertTrue(names.isEmpty()); + } + + private static void findLongFileName(File folder, List holder, int baseLength) { + if (folder.isFile()) { + 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, baseLength); + } + } else { + return; + } + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFortifyUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFortifyUtils.java new file mode 100644 index 00000000000..5ca8147e146 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFortifyUtils.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.foundation.common.utils; + +import java.io.IOException; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestFortifyUtils { + @Test + public void testFortifyUtils() throws IOException { + Assertions.assertEquals("", FortifyUtils.getErrorMsg(null)); + Assertions.assertEquals("", FortifyUtils.getErrorStack(null)); + } + + @Test + public void testFilePerm() { + Assertions.assertEquals(10, (FilePerm.getDefaultAclPerm().size())); + Assertions.assertEquals(3, (FilePerm.getDefaultPosixPerm().size())); + Assertions.assertEquals(4, (FilePerm.getPosixPerm(400).size())); + } + + @Test + public void testGetErrorMsg() { + + Throwable e = new Throwable(); + + FortifyUtils.getErrorMsg(e); + + Assertions.assertNull(FortifyUtils.getErrorMsg(e)); + } + + @Test + public void testGetSecurityXmlDocumentFactory() { + + try { + FortifyUtils.getSecurityXmlDocumentFactory(); + Assertions.assertNotNull(FortifyUtils.getSecurityXmlDocumentFactory()); + } catch (Exception e) { + /* Do not Worry */ + Assertions.fail(); + } + } + + @Test + public void testGetErrorStack() { + + Throwable e = new Throwable(); + FortifyUtils.getErrorStack(e); + Assertions.assertNotEquals(true, FortifyUtils.getErrorStack(e)); + } + + @Test + public void testGetErrorInfo() { + + Throwable e = new Throwable(); + FortifyUtils.getErrorInfo(e, true); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestGenericsUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestGenericsUtils.java new file mode 100644 index 00000000000..06de9612dd2 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestGenericsUtils.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.foundation.common.utils; + +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.core.ParameterizedTypeReference; + +public class TestGenericsUtils { + @Test + public void testIsGenerics() { + Assertions.assertTrue(GenericsUtils.isGenericResponseType(List.class)); + Assertions.assertTrue(GenericsUtils.isGenericResponseType(Map.class)); + Assertions.assertTrue(GenericsUtils.isGenericResponseType(Person.class)); + + Assertions.assertFalse(GenericsUtils.isGenericResponseType(Man.class)); + Assertions.assertFalse(GenericsUtils.isGenericResponseType(String.class)); + Assertions.assertFalse(GenericsUtils.isGenericResponseType(GenericsUtils.class)); + + Assertions.assertFalse(GenericsUtils.isGenericResponseType(new ParameterizedTypeReference>() { + }.getType())); + } +} + +class Person { + private T value; + + public T getValue() { + return value; + } +} + +class Man extends Person { + +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestIOUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestIOUtils.java new file mode 100644 index 00000000000..42c7abe5d4a --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestIOUtils.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.foundation.common.utils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestIOUtils { + + @Test + public void testAnonymousPath() { + Assertions.assertEquals(":/:/r/2/y/b/y/0/s/microservice.yaml", + IOUtils.anonymousPath("jar:file:/D:/User/.m2/repository/servicecomb" + + "/transport-highway/2.3.0/classes/microservice.yaml")); + + Assertions.assertEquals(":/:/r/microservice.yaml", IOUtils.anonymousPath("file:/D:/User/microservice.yaml")); + + Assertions.assertEquals(":\\:\\r\\microservice.yaml", IOUtils.anonymousPath("file:\\D:\\User\\microservice.yaml")); + + Assertions.assertEquals("r\\t\\a.txt", IOUtils.anonymousPath("user\\test\\a.txt")); + + Assertions.assertEquals(":\\:\\a.txt", IOUtils.anonymousPath("file:\\D:\\a.txt")); + + Assertions.assertEquals(":\\a.txt", IOUtils.anonymousPath("D:\\a.txt")); + + Assertions.assertEquals("a.txt", IOUtils.anonymousPath("a.txt")); + } + +} 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 new file mode 100644 index 00000000000..10054efe4e8 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.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.foundation.common.utils; + +import mockit.Mock; +import mockit.MockUp; +import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; +import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URL; + + +public class TestJvmUtils { + + @Test + @Disabled + public void findMainClass_notExist() { + System.clearProperty(JvmUtils.SUN_JAVA_COMMAND); + Assertions.assertNull(JvmUtils.findMainClass()); + } + + @Test + @Disabled + public void findMainClass_existButEmpty() { + System.setProperty(JvmUtils.SUN_JAVA_COMMAND, ""); + Assertions.assertNull(JvmUtils.findMainClass()); + } + + @Test + @Disabled + public void findMainClass_invalid() { + LogCollector logCollector = new LogCollector(); + + System.setProperty(JvmUtils.SUN_JAVA_COMMAND, "invalidCls"); + + Assertions.assertNull(JvmUtils.findMainClass()); + Assertions.assertEquals("\"invalidCls\" is not a valid class.", logCollector.getEvents().get(0).getMessage()); + logCollector.tearDown(); + } + + @Test + @Disabled + public void findMainClass_class_normal() { + System.setProperty(JvmUtils.SUN_JAVA_COMMAND, TestJvmUtils.class.getName() + " arg"); + + Assertions.assertEquals(TestJvmUtils.class, JvmUtils.findMainClass()); + } + + @Test + @Disabled + public void findMainClass_jar_normal() throws Exception { + String command = "a.jar"; + + String content = String.format("Manifest-Version: 1.0\nMain-Class: %s\n", TestJvmUtils.class.getName()); + InputStream inputStream = new ByteArrayInputStream(content.getBytes()); + new MockUp() { + @Mock + InputStream openStream() throws Exception { + return inputStream; + } + }; + + System.setProperty(JvmUtils.SUN_JAVA_COMMAND, command + " arg"); + + Assertions.assertEquals(TestJvmUtils.class, JvmUtils.findMainClass()); + } + + @Test + @Disabled + public void findMainClass_jar_null() throws Exception { + String content = "Manifest-Version: 1.0\n"; + InputStream inputStream = new ByteArrayInputStream(content.getBytes()); + + String command = "a.jar"; + + new MockUp() { + @Mock + InputStream openStream() throws Exception { + return inputStream; + } + }; + + System.setProperty(JvmUtils.SUN_JAVA_COMMAND, command + " arg"); + + Assertions.assertNull(JvmUtils.findMainClass()); + } + + @Test + @Disabled + @EnabledOnJre(JRE.JAVA_17) + public void findMainClass_jar_readFailed() throws Exception { + String command = "a.jar"; + + new MockUp() { + @Mock + InputStream openStream() throws Exception { + throw new RuntimeExceptionWithoutStackTrace(); + } + }; + + System.setProperty(JvmUtils.SUN_JAVA_COMMAND, command + " arg"); + + Assertions.assertNull(JvmUtils.findMainClass()); + } + + + @Test + @Disabled + public void findMainClassByStackTrace_normal() throws Exception{ + StackTraceElement[] stackTraceElements = { + new StackTraceElement("declaring.class.fileName", "methodName", "fileName", 100), + new StackTraceElement("java.lang.String", "main", "fileName", 120) + }; + new MockUp() { + @Mock + public StackTraceElement[] getStackTrace() { + return stackTraceElements; + } + }; + + Assertions.assertEquals(String.class, JvmUtils.findMainClassByStackTrace()); + } + + @Test + @Disabled + public void findMainClassByStackTrace_invalidClass() throws Exception{ + StackTraceElement[] stackTraceElements = { + new StackTraceElement("declaring.class.fileName", "methodName", "fileName", 100), + new StackTraceElement("InvalidClass", "main", "fileName", 120) + }; + new MockUp() { + @Mock + public StackTraceElement[] getStackTrace() { + return stackTraceElements; + } + }; + + Assertions.assertNull(JvmUtils.findMainClassByStackTrace()); + } + + + @Test + @Disabled + public void findMainClassByStackTrace_withoutMainMethod() throws Exception{ + StackTraceElement[] stackTraceElements = { + new StackTraceElement("declaring.class.fileName", "methodName", "fileName", 100), + new StackTraceElement("InvalidClass", "methodName", "fileName", 120) + }; + new MockUp() { + @Mock + public StackTraceElement[] getStackTrace() { + return stackTraceElements; + } + }; + + Assertions.assertNull(JvmUtils.findMainClassByStackTrace()); + } + + @Test + @Disabled + public void findMainClassByStackTrace_emptyStackTrace() throws Exception{ + new MockUp() { + @Mock + public StackTraceElement[] getStackTrace() { + return new StackTraceElement[]{}; + } + }; + + Assertions.assertNull(JvmUtils.findMainClassByStackTrace()); + } + + @Test + @Disabled + public void findMainClassByStackTrace_nullStackTrace() throws Exception{ + new MockUp() { + @Mock + public StackTraceElement[] getStackTrace() { + return null; + } + }; + + Assertions.assertNull(JvmUtils.findMainClassByStackTrace()); + } + +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestLambdaMetafactoryUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestLambdaMetafactoryUtils.java new file mode 100644 index 00000000000..39647ba3dcf --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestLambdaMetafactoryUtils.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils.createObjectGetter; +import static org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils.createObjectSetter; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; +import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; + +@EnabledOnJre(JRE.JAVA_8) +public class TestLambdaMetafactoryUtils { + public static class Model { + public int f1; + + private int f2; + + public int getF1() { + return f1; + } + + public void setF1(int f1) { + this.f1 = f1; + } + + public Model fluentSetF1(int f1) { + this.f1 = f1; + return this; + } + + public List echo(List value) { + return value; + } + } + + Model model = new Model(); + + @SuppressWarnings("unchecked") + @Test + public void createLambda_withInstance() throws Throwable { + Supplier getter = LambdaMetafactoryUtils + .createLambda(model, Model.class.getMethod("getF1"), Supplier.class); + Consumer setter = LambdaMetafactoryUtils + .createLambda(model, Model.class.getMethod("setF1", int.class), Consumer.class); + Function echo = LambdaMetafactoryUtils + .createLambda(model, Model.class.getMethod("echo", List.class), Function.class); + + setter.accept(1); + int f1 = (int) getter.get(); + Assertions.assertEquals(1, f1); + MatcherAssert.assertThat((List) echo.apply(Arrays.asList(2)), Matchers.contains(2)); + } + + @SuppressWarnings("unchecked") + @Test + public void createGetterSetterByMethod() throws Throwable { + IntGetter getter = LambdaMetafactoryUtils.createGetter(Model.class.getMethod("getF1")); + IntSetter setter = LambdaMetafactoryUtils.createSetter(Model.class.getMethod("setF1", int.class)); + IntSetter fluentSetter = LambdaMetafactoryUtils + .createSetter(Model.class.getMethod("fluentSetF1", int.class)); + BiFunction echo = LambdaMetafactoryUtils + .createLambda(Model.class.getMethod("echo", List.class), BiFunction.class); + + setter.set(model, 1); + int f1 = getter.get(model); + Assertions.assertEquals(1, f1); + MatcherAssert.assertThat((List) echo.apply(model, Arrays.asList(2)), Matchers.contains(2)); + + fluentSetter.set(model, 2); + int ff1 = getter.get(model); + Assertions.assertEquals(2, ff1); + } + + @Test + public void should_failed_when_createGetterSetterByField_and_field_is_not_public() throws Throwable { + Field field = Model.class.getDeclaredField("f2"); + assertThat(catchThrowable(() -> LambdaMetafactoryUtils.createGetter(field))) + .isInstanceOf(IllegalStateException.class) + .hasMessage( + "Can not access field, a public field or accessor is required.Declaring class is org.apache.servicecomb.foundation.common.utils.TestLambdaMetafactoryUtils$Model, field is f2"); + assertThat(catchThrowable(() -> LambdaMetafactoryUtils.createSetter(field))) + .isInstanceOf(IllegalStateException.class) + .hasMessage( + "Can not access field, a public field or accessor is required.Declaring class is org.apache.servicecomb.foundation.common.utils.TestLambdaMetafactoryUtils$Model, field is f2"); + } + + public static class Base { + private T base; + + public T getBase() { + return base; + } + + public Base setBase(T base) { + this.base = base; + return this; + } + } + + public static class Child extends Base { + private int child; + + public int getChild() { + return child; + } + + public Child setChild(int child) { + this.child = child; + return this; + } + } + + @Test + public void should_support_primitive_type() { + Child child = new Child(); + + ObjectMapper mapper = JsonUtils.OBJ_MAPPER; + BeanDescription beanDescription = mapper.getSerializationConfig().introspect(mapper.constructType(Child.class)); + List properties = beanDescription.findProperties(); + assertThat(properties).hasSize(2); + + for (int idx = 0; idx < properties.size(); idx++) { + BeanPropertyDefinition property = properties.get(idx); + + Setter setter = createObjectSetter(property.getSetter().getAnnotated()); + setter.set(child, idx); + + Getter getter = createObjectGetter(property.getGetter().getAnnotated()); + Object value = getter.get(child); + + assertThat(value).isEqualTo(idx); + } + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestLambdaPerformance.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestLambdaPerformance.java new file mode 100644 index 00000000000..ab92c86216b --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestLambdaPerformance.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.apache.servicecomb.foundation.common.utils.TestLambdaMetafactoryUtils.Model; +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; + +public class TestLambdaPerformance { + static Model model = new Model(); + + // 20 field/msg, 10_0000tps + static int count = 20 * 100_0000; + + static int sum = 0; + + static Method f1_getter; + + static MethodHandle mh_f1_getter; + + static IntGetter lambda_f1_method_getter; + + static Field f1_field; + + static Getter lambda_f1_field_getter; + + static { + model.setF1(123456); + try { + f1_field = Model.class.getDeclaredField("f1"); + f1_field.setAccessible(true); + + f1_getter = Model.class.getMethod("getF1"); + mh_f1_getter = MethodHandles.lookup().unreflect(f1_getter); + lambda_f1_method_getter = LambdaMetafactoryUtils.createGetter(f1_getter); + lambda_f1_field_getter = LambdaMetafactoryUtils.createGetter(f1_field); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + } + + public static long directGetter() { + long start = System.nanoTime(); + sum = 0; + for (int idx = 0; idx < count; idx++) { + sum += model.getF1(); + } + return System.nanoTime() - start; + } + + public static long reflectfieldF1() throws IllegalAccessException { + long start = System.nanoTime(); + sum = 0; + for (int idx = 0; idx < count; idx++) { + sum += (int) f1_field.get(model); + } + return System.nanoTime() - start; + } + + public static long reflectIntFieldF1() throws IllegalAccessException { + long start = System.nanoTime(); + sum = 0; + for (int idx = 0; idx < count; idx++) { + sum += f1_field.getInt(model); + } + return System.nanoTime() - start; + } + + public static long lambdaMethodGetter() throws Throwable { + long start = System.nanoTime(); + sum = 0; + for (int idx = 0; idx < count; idx++) { + sum += lambda_f1_method_getter.get(model); + } + return System.nanoTime() - start; + } + + public static long lambdaFieldGetter() throws Throwable { + long start = System.nanoTime(); + sum = 0; + for (int idx = 0; idx < count; idx++) { + sum += lambda_f1_field_getter.get(model); + } + return System.nanoTime() - start; + } + + public static long mhGetterInvoke() throws Throwable { + long start = System.nanoTime(); + sum = 0; + for (int idx = 0; idx < count; idx++) { + sum += (int) mh_f1_getter.invoke(model); + } + return System.nanoTime() - start; + } + + public static long mhGetterExact() throws Throwable { + long start = System.nanoTime(); + sum = 0; + for (int idx = 0; idx < count; idx++) { + sum += (int) mh_f1_getter.invokeExact(model); + } + return System.nanoTime() - start; + } + + public static long reflectGetter() throws InvocationTargetException, IllegalAccessException { + long start = System.nanoTime(); + sum = 0; + for (int idx = 0; idx < count; idx++) { + sum += (int) f1_getter.invoke(model); + } + return System.nanoTime() - start; + } + + public static void main(String[] args) throws Throwable { + lambdaMethodGetter(); + directGetter(); + mhGetterInvoke(); + reflectGetter(); + mhGetterExact(); + reflectfieldF1(); + reflectIntFieldF1(); + + System.out.println("mhGetterInvoke : " + mhGetterInvoke()); + System.out.println("mhGetterExact : " + mhGetterExact()); + System.out.println("reflectGetter : " + reflectGetter()); + + System.out.println(""); + + System.out.println("reflectfieldF1 : " + reflectfieldF1()); + System.out.println("lambdaFieldGetter : " + lambdaFieldGetter()); + + System.out.println(""); + + System.out.println("lambdaMethodGetter : " + lambdaMethodGetter()); + System.out.println("directGetter : " + directGetter()); + System.out.println("reflectIntFieldF1 : " + reflectIntFieldF1()); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestMimeTypesUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestMimeTypesUtils.java new file mode 100644 index 00000000000..55d88f6ef96 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestMimeTypesUtils.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.utils; + +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestMimeTypesUtils { + @Test + public void testSortedAcceptableMimeTypes1() { + String accept = "text/html"; + List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); + Assertions.assertEquals(1, types.size()); + Assertions.assertEquals("text/html", types.get(0)); + } + + @Test + public void testSortedAcceptableMimeTypes2() { + String accept = "text/html, application/json"; + List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); + Assertions.assertEquals(2, types.size()); + Assertions.assertEquals("text/html", types.get(0)); + Assertions.assertEquals("application/json", types.get(1)); + } + + @Test + public void testSortedAcceptableMimeTypes3() { + String accept = "text/html,application/json"; + List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); + Assertions.assertEquals(2, types.size()); + Assertions.assertEquals("text/html", types.get(0)); + Assertions.assertEquals("application/json", types.get(1)); + } + + @Test + public void testSortedAcceptableMimeTypes4() { + String accept = "text/html; q=0.8,application/json; q=0.9"; + List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); + Assertions.assertEquals(2, types.size()); + Assertions.assertEquals("application/json", types.get(0)); + Assertions.assertEquals("text/html", types.get(1)); + } + + @Test + public void testSortedAcceptableMimeTypes5() { + String accept = "text/html;q=0.8,application/json;q=0.9"; + List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); + Assertions.assertEquals(2, types.size()); + Assertions.assertEquals("application/json", types.get(0)); + Assertions.assertEquals("text/html", types.get(1)); + } + + @Test + public void testSortedAcceptableMimeTypes6() { + String accept = "text/html; q=0.8,application/json; q=0.9, text/plain"; + List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); + Assertions.assertEquals(3, types.size()); + Assertions.assertEquals("text/plain", types.get(0)); + Assertions.assertEquals("application/json", types.get(1)); + Assertions.assertEquals("text/html", types.get(2)); + } + + @Test + public void testSortedAcceptableMimeTypes7() { + String accept = "text/html;q=0.8,application/json;q=0.9,text/plain"; + List types = MimeTypesUtils.getSortedAcceptableMimeTypes(accept); + Assertions.assertEquals(3, types.size()); + Assertions.assertEquals("text/plain", types.get(0)); + Assertions.assertEquals("application/json", types.get(1)); + Assertions.assertEquals("text/html", types.get(2)); + } + + @Test + public void getSortedAcceptableMimeTypesNull() { + List types = MimeTypesUtils.getSortedAcceptableMimeTypes(null); + Assertions.assertSame(Collections.emptyList(), types); + } +} 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 new file mode 100644 index 00000000000..6bd9b386e36 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestRSAUtil.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.foundation.common.utils; + +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() + throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException { + KeyPairEntry keyPairEntry = KeyPairUtils.generateALGKeyPair(); + + Assertions.assertNotNull(keyPairEntry.getPublicKeyEncoded()); + Assertions.assertNotNull(keyPairEntry.getPrivateKey()); + Assertions.assertNotNull(keyPairEntry.getPublicKey()); + String testContent = "instance-id@201711201930@randomstr"; + String signstr = KeyPairUtils.sign(testContent, keyPairEntry.getPrivateKey()); + Assertions.assertTrue(KeyPairUtils.verify(keyPairEntry.getPublicKeyEncoded(), signstr, testContent)); + } + + @Test + public void testSignVerify2() + throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException { + String sign = + "WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; + String content = + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ"; + String pubKey = + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxKl5TNUTec7fL2degQcCk6vKf3c0wsfNK5V6elKzjWxm0MwbRj/UeR20VSnicBmVIOWrBS9LiERPPvjmmWUOSS2vxwr5XfhBhZ07gCAUNxBOTzgMo5nE45DhhZu5Jzt5qSV6o10Kq7+fCCBlDZ1UoWxZceHkUt5AxcrhEDulFjQIDAQAB"; + Assertions.assertTrue(KeyPairUtils.verify(pubKey, sign, content)); + } +} diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestTypesUtil.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestTypesUtil.java new file mode 100644 index 00000000000..487ad288585 --- /dev/null +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestTypesUtil.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.foundation.common.utils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestTypesUtil { + @Test + public void testTypesUtil() { + Assertions.assertEquals(double.class, TypesUtil.wrapperTypeToPrimitive(Double.class)); + Assertions.assertEquals(Float.class, TypesUtil.primitiveTypeToWrapper(float.class)); + Assertions + .assertEquals(TypesUtil.PRIMITIVE_CHAR, TypesUtil.wrapperJavaTypeToPrimitive(TypesUtil.PRIMITIVE_WRAPPER_CHAR)); + Assertions + .assertEquals(TypesUtil.PRIMITIVE_WRAPPER_BYTE, TypesUtil.primitiveJavaTypeToWrapper(TypesUtil.PRIMITIVE_BYTE)); + } +} diff --git a/foundations/foundation-common/src/test/resources/META-INF/services/io.servicecomb.foundation.common.utils.SPIServiceDef b/foundations/foundation-common/src/test/resources/META-INF/services/io.servicecomb.foundation.common.utils.SPIServiceDef deleted file mode 100644 index a1044ce5da2..00000000000 --- a/foundations/foundation-common/src/test/resources/META-INF/services/io.servicecomb.foundation.common.utils.SPIServiceDef +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -io.servicecomb.foundation.common.utils.SPIServiceDefImpl \ No newline at end of file diff --git a/foundations/foundation-common/src/test/resources/META-INF/spring/common.bean.xml b/foundations/foundation-common/src/test/resources/META-INF/spring/common.bean.xml deleted file mode 100644 index e56474e504e..00000000000 --- a/foundations/foundation-common/src/test/resources/META-INF/spring/common.bean.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - \ No newline at end of file diff --git a/foundations/foundation-common/src/test/resources/META-INF/spring/config.bean.xml b/foundations/foundation-common/src/test/resources/META-INF/spring/config.bean.xml index cae33b427b9..dde401dec31 100644 --- a/foundations/foundation-common/src/test/resources/META-INF/spring/config.bean.xml +++ b/foundations/foundation-common/src/test/resources/META-INF/spring/config.bean.xml @@ -1,12 +1,13 @@ - - - - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> - - - ${1.2} - - - \ No newline at end of file + + + ${1.2} + + + diff --git a/foundations/foundation-common/src/test/resources/config/config.test.1.inc.xml b/foundations/foundation-common/src/test/resources/config/config.test.1.inc.xml deleted file mode 100644 index 1fb697d4d84..00000000000 --- a/foundations/foundation-common/src/test/resources/config/config.test.1.inc.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - classpath*:config/test.ext.properties - - - - classpath*:config/test.ext.xml - - \ No newline at end of file diff --git a/foundations/foundation-common/src/test/resources/config/config.test.inc.xml b/foundations/foundation-common/src/test/resources/config/config.test.inc.xml deleted file mode 100644 index 0fd3be82641..00000000000 --- a/foundations/foundation-common/src/test/resources/config/config.test.inc.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - classpath*:config/test.properties - - - - classpath*:config/test.xml - - \ No newline at end of file diff --git a/foundations/foundation-common/src/test/resources/config/log4j.common.test.properties b/foundations/foundation-common/src/test/resources/config/log4j.common.test.properties deleted file mode 100644 index 2e08045e81b..00000000000 --- a/foundations/foundation-common/src/test/resources/config/log4j.common.test.properties +++ /dev/null @@ -1,21 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Override the log setting -paas.logs.dir=./target/logs -paas.logs.file=cse.log - -log4j.rootLogger=INFO,paas,stdout diff --git a/foundations/foundation-common/src/test/resources/config/test.1.properties b/foundations/foundation-common/src/test/resources/config/test.1.properties deleted file mode 100644 index e8d15c780a1..00000000000 --- a/foundations/foundation-common/src/test/resources/config/test.1.properties +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -1.1=1 \ No newline at end of file diff --git a/foundations/foundation-common/src/test/resources/config/test.2.properties b/foundations/foundation-common/src/test/resources/config/test.2.properties deleted file mode 100644 index fdc26f9a1ea..00000000000 --- a/foundations/foundation-common/src/test/resources/config/test.2.properties +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -1.2=2 \ No newline at end of file diff --git a/foundations/foundation-common/src/test/resources/config/test.properties b/foundations/foundation-common/src/test/resources/config/test.properties deleted file mode 100644 index d12341ed3dc..00000000000 --- a/foundations/foundation-common/src/test/resources/config/test.properties +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -1=0 -1.1=0 -1.2=0 \ No newline at end of file diff --git a/foundations/foundation-common/src/test/resources/config/test.xml b/foundations/foundation-common/src/test/resources/config/test.xml deleted file mode 100644 index 6bd304031de..00000000000 --- a/foundations/foundation-common/src/test/resources/config/test.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - test value - \ No newline at end of file diff --git a/foundations/foundation-common/src/test/resources/log4j.properties b/foundations/foundation-common/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/foundations/foundation-common/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/foundations/foundation-common/src/test/resources/log4j2.xml b/foundations/foundation-common/src/test/resources/log4j2.xml new file mode 100644 index 00000000000..8cee07220ec --- /dev/null +++ b/foundations/foundation-common/src/test/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/foundations/foundation-config/README.md b/foundations/foundation-config/README.md deleted file mode 100644 index 1f8091ec371..00000000000 --- a/foundations/foundation-config/README.md +++ /dev/null @@ -1,97 +0,0 @@ -# Config Center设计思路 -* Config Center本身可以以客户端服务发现的方式被每一个微服务发现和消费。 -* 微服务与Config Center的Transport等与Service Center相似,逻辑可以重用。 - - -## 配置策略 -* SDK 出厂时的默认值: SDK 内部的: meta-inf/microservice.yaml 在 Annotation 上指定 -* 业务开发人员的调整: meta-info/microservice.yaml 系列 (yaml 文件支持 import 能力) -* 业务部署人员的调整:
- 磁盘 microservice.yaml -环境变量, 及环境变量制定的 yaml 文件 -* 运行时期的治理调整: 配置中心的文件、UI调整 - -## 需求描述 -### 配置供给(Source): - * loadDefault() : SDK 出厂时的默认值 - * loadFile() : 业务开发人员的调整 - * loadEnv() : 业务部署人员的调整 - * loadRemote() : 运行时期的治理调整 -#### 期望: - * OS Env - * 磁盘上一个: microservice.yaml --> import "file-name" - * jar 里面有一系列: /META-INF/microservice.yaml --> import "file-name" - * ConfigManager - -### 配置消费: -* yaml/properties: 底层机制, 基本能力 get() -* @标注消费 -* @Spring友好消费 -* notify() : callback 机制 - - -### 需求: -1. SDK 本身依赖在里面, 以 cse 开头, 静态的元信息 -2. Handle 链的信息 -3. handler 的默认值 -4. Transport/Provider 的配置信息, 例如线程数、默认限额等 - - - -# 如何使用 -可以参考和调试工程中的UT代码以获取如何使用的详细信息 - -## 引入工程后,需要在系统启动的时候对配置系统进行初始化 -如: - - YAMLConfigurationSource configSource = new YAMLConfigurationSource(); - DynamicConfiguration configuration = new DynamicConfiguration(configSource, new FixedDelayPollingScheduler()); - ConfigurationManager.install(configuration); - -配置文件默认是classpath下的microservice.yaml, 但是可以通过环境变量传入其他文件,可以设置的环境变量为: - -|变量|描述| -|---|---| -|cse.configurationSource.additionalUrls|配置文件的列表,以,分隔的多个包含具体位置的完整文件名| -|cse.configurationSource.defaultFileName|默认配置文件名| -|cse.config.client.refreshMode|应用配置的刷新方式,0为config-center主动push,1为client周期pull,默认为0| -|cse.config.client.refreshPort|config-center推送配置的端口| -|cse.config.client.tenantName|应用的租户名称| -|cse.config.client.serviceName|应用名称| -|cse.config.client.regUri|service-center访问地址,http(s)://{ip}:{port},以,分隔多个地址(可选,若不配置,则使用cse.config.client.serverUri中的配置中心地址)| -|cse.config.client.serverUri|config-center访问地址,http(s)://{ip}:{port},以,分隔多个地址(可选,当cse.config.client.regUri配置为空时该配置项才会生效)| - -## 在程序中获取配置信息 - -举例: - - DynamicDoubleProperty myprop = DynamicPropertyFactory.getInstance().getDoubleProperty("trace.handler.sampler.percent", 0.1); - -具体方法可参考[API DOC](https://netflix.github.io/archaius/archaius-core-javadoc/com/netflix/config/DynamicPropertyFactory.html) - -## 处理配置变更 -可以注册callback处理配置变更: - - myprop.addCallback(new Runnable() { - public void run() { - // Handle configuration changes - } - }); - -## 进行配置项映射 -有些情况下,我们要屏蔽我们使用的一些开源组件的配置并给用户提供我们自己的配置项。在这种情况下,可以通过classpath下的config.yaml进行映射定义: - - registry: - client: - serviceUrl: - defaultZone: eureka.client.serviceUrl.defaultZone - -定义映射后,在配置装载的时候框架会默认进行映射,把以我们定义的配置项映射为开源组件可以认的配置项。 - -## 多配置源的继承配置 -参考[测试代码](http://code.huawei.com/CSE-Incubator/cse-config/blob/master/archaius-yaml/src/test/java/com/huawei/paas/config/archaius/sources/YAMLConfigurationSourceTest.java#L46), 在初始化配置的时候按照测试代码中的方式对多个ConfigurationSource进行初始化以实现继承式的配置装载。 - -## TODO -* 接入Config-center,做PUSH模式的动态刷新 -* 对接Spring Configuration实现对业务系统的配置 -* 配置中心和服务中心以及整个微服务runtime的关系和后续演进策略 \ No newline at end of file diff --git a/foundations/foundation-config/pom.xml b/foundations/foundation-config/pom.xml index cfaa1e56082..12c4a54c5be 100644 --- a/foundations/foundation-config/pom.xml +++ b/foundations/foundation-config/pom.xml @@ -1,12 +1,13 @@ - - foundations - io.servicecomb - 0.3.0-SNAPSHOT - - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + foundations + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + + + + foundation-config + Java Chassis::Foundations::Config + + + org.apache.servicecomb + foundation-common + + + org.springframework + spring-beans + + + org.springframework.boot + spring-boot-autoconfigure + - - + + org.yaml + snakeyaml + + + jakarta.annotation + jakarta.annotation-api + + + org.slf4j + slf4j-api + - foundation-config - - - io.servicecomb - foundation-common - - - org.springframework - spring-beans - - - com.netflix.archaius - archaius-core - - - commons-configuration - commons-configuration - - - org.yaml - snakeyaml - - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - - net.sf.json-lib - json-lib - jdk15 - - - org.apache.httpcomponents - httpclient - - - io.netty - netty-codec - - - io.netty - netty-codec-http - - - io.netty - netty-codec-http2 - - - io.netty - netty-common - - - io.netty - netty-handler - - - io.netty - netty-buffer - - - io.netty - netty-codec-socks - - - io.netty - netty-handler-proxy - - - io.netty - netty-resolver - - - io.netty - netty-transport - - - com.google.guava - guava - - + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.servicecomb + foundation-test-scaffolding + + + org.apache.httpcomponents + httpclient + + + commons-logging + commons-logging + + + + + io.netty + netty-codec + + + io.netty + netty-codec-http + + + io.netty + netty-codec-http2 + + + io.netty + netty-common + + + io.netty + netty-handler + + + io.netty + netty-buffer + + + io.netty + netty-codec-socks + + + io.netty + netty-handler-proxy + + + io.netty + netty-resolver + + + io.netty + netty-transport + + + com.google.guava + guava + + + org.jmockit + jmockit + test + + diff --git a/foundations/foundation-config/src/main/java/io/servicecomb/config/ConfigMapping.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/ConfigMapping.java deleted file mode 100644 index 738e8aac04e..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/ConfigMapping.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config; - -import java.io.InputStream; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Created by on 2017/1/5. - */ -public final class ConfigMapping { - private static Map configMap = null; - - static { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - InputStream is = loader.getResourceAsStream("mapping.yaml"); - configMap = YAMLUtil.yaml2Properties(is); - } - - private ConfigMapping() { - } - - public static String map(String key) { - return (String) configMap.get(key); - } - - public static Map getMapping() { - return configMap; - } - - public static Map getConvertedMap(Map oldMap) { - if (configMap == null) { - return new LinkedHashMap<>(); - } - Map retMap = new LinkedHashMap(); - retMap.putAll(oldMap); - for (Map.Entry entry : configMap.entrySet()) { - String key = entry.getKey(); - Object configValue = oldMap.get(key); - if (configValue != null) { - String newKey = (String) entry.getValue(); - retMap.put(newKey, configValue); - retMap.remove(key); - } - } - return retMap; - } -} diff --git a/foundations/foundation-config/src/main/java/io/servicecomb/config/ConfigUtil.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/ConfigUtil.java deleted file mode 100644 index 9bdc83cccb0..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/ConfigUtil.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config; - -import static io.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_CSE_PREFIX; -import static io.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_SERVICECOMB_PREFIX; - -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.EnvironmentConfiguration; -import org.apache.commons.configuration.SystemConfiguration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -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.DynamicWatchedConfiguration; -import com.netflix.config.WatchedUpdateListener; -import com.netflix.config.WatchedUpdateResult; - -import io.servicecomb.config.archaius.scheduler.NeverStartPollingScheduler; -import io.servicecomb.config.archaius.sources.ConfigModel; -import io.servicecomb.config.archaius.sources.MicroserviceConfigLoader; -import io.servicecomb.config.archaius.sources.MicroserviceConfigurationSource; -import io.servicecomb.config.spi.ConfigCenterConfigurationSource; -import io.servicecomb.foundation.common.utils.SPIServiceUtils; - -public final class ConfigUtil { - private static final Logger LOGGER = LoggerFactory.getLogger(ConfigUtil.class); - - protected static final String configCenterUrlKey = "cse.config.client.serverUri"; - - private static final String MICROSERVICE_CONFIG_LOADER_KEY = "cse-microservice-config-loader"; - - private ConfigUtil() { - } - - public static Object getProperty(String key) { - Object config = DynamicPropertyFactory.getBackingConfigurationSource(); - return getProperty(config, key); - } - - public static Object getProperty(Object config, String key) { - if (null != config && Configuration.class.isInstance(config)) { - Configuration configuration = (Configuration) config; - return configuration.getProperty(key); - } - return null; - } - - private static void setMicroserviceConfigLoader(Configuration config, MicroserviceConfigLoader loader) { - config.setProperty(MICROSERVICE_CONFIG_LOADER_KEY, loader); - } - - public static MicroserviceConfigLoader getMicroserviceConfigLoader() { - return (MicroserviceConfigLoader) getProperty(MICROSERVICE_CONFIG_LOADER_KEY); - } - - public static MicroserviceConfigLoader getMicroserviceConfigLoader(Configuration config) { - return (MicroserviceConfigLoader) getProperty(config, MICROSERVICE_CONFIG_LOADER_KEY); - } - - public static ConcurrentCompositeConfiguration createLocalConfig() { - MicroserviceConfigLoader loader = new MicroserviceConfigLoader(); - loader.loadAndSort(); - - LOGGER.info("create local config:"); - for (ConfigModel configModel : loader.getConfigModels()) { - LOGGER.info(" {}.", configModel.getUrl()); - } - - ConcurrentCompositeConfiguration config = ConfigUtil.createLocalConfig(loader.getConfigModels()); - ConfigUtil.setMicroserviceConfigLoader(config, loader); - return config; - } - - public static ConcurrentCompositeConfiguration createLocalConfig(List configModelList) { - ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration(); - - duplicateServiceCombConfigToCse(config, - new ConcurrentMapConfiguration(new SystemConfiguration()), - "configFromSystem"); - duplicateServiceCombConfigToCse(config, - new ConcurrentMapConfiguration(new EnvironmentConfiguration()), - "configFromEnvironment"); - duplicateServiceCombConfigToCse(config, - new DynamicConfiguration( - new MicroserviceConfigurationSource(configModelList), new NeverStartPollingScheduler()), - "configFromYamlFile"); - - return config; - } - - //inject a copy of cse.xxx for servicecomb.xxx - private static void duplicateServiceCombConfigToCse(AbstractConfiguration source) { - Iterator keys = source.getKeys(); - while (keys.hasNext()) { - String key = keys.next(); - if (!key.startsWith(CONFIG_SERVICECOMB_PREFIX)) { - continue; - } - - String cseKey = CONFIG_CSE_PREFIX + key.substring(key.indexOf(".") + 1); - source.addProperty(cseKey, source.getProperty(key)); - } - } - - private static void duplicateServiceCombConfigToCse(ConcurrentCompositeConfiguration compositeConfiguration, - AbstractConfiguration source, - String sourceName) { - duplicateServiceCombConfigToCse(source); - - compositeConfiguration.addConfiguration(source, sourceName); - } - - public static DynamicWatchedConfiguration createConfigFromConfigCenter(Configuration localConfiguration) { - if (localConfiguration.getProperty(configCenterUrlKey) == null) { - LOGGER.info("config center URL is missing, skip to load configuration from config center"); - return null; - } - - ConfigCenterConfigurationSource configCenterConfigurationSource = - SPIServiceUtils.getTargetService(ConfigCenterConfigurationSource.class); - if (null == configCenterConfigurationSource) { - LOGGER.info( - "config center SPI service can not find, skip to load configuration from config center"); - return null; - } - - configCenterConfigurationSource.init(localConfiguration); - return new DynamicWatchedConfiguration(configCenterConfigurationSource); - } - - public static AbstractConfiguration createDynamicConfig() { - LOGGER.info("create dynamic config:"); - ConcurrentCompositeConfiguration config = ConfigUtil.createLocalConfig(); - DynamicWatchedConfiguration configFromConfigCenter = createConfigFromConfigCenter(config); - if (configFromConfigCenter != null) { - ConcurrentMapConfiguration injectConfig = new ConcurrentMapConfiguration(); - config.addConfigurationAtFront(injectConfig, "extraInjectConfig"); - - duplicateServiceCombConfigToCse(configFromConfigCenter); - config.addConfigurationAtFront(configFromConfigCenter, "configCenterConfig"); - - configFromConfigCenter.getSource().addUpdateListener(new ServiceCombPropertyUpdateListener(injectConfig)); - } - - return config; - } - - public static void installDynamicConfig() { - if (ConfigurationManager.isConfigurationInstalled()) { - LOGGER.warn("Configuration installed by others, will ignore this configuration."); - return; - } - - AbstractConfiguration dynamicConfig = ConfigUtil.createDynamicConfig(); - ConfigurationManager.install(dynamicConfig); - } - - private static class ServiceCombPropertyUpdateListener implements WatchedUpdateListener { - - private final ConcurrentMapConfiguration injectConfig; - - ServiceCombPropertyUpdateListener(ConcurrentMapConfiguration injectConfig) { - this.injectConfig = injectConfig; - } - - @Override - public void updateConfiguration(WatchedUpdateResult watchedUpdateResult) { - Map adds = watchedUpdateResult.getAdded(); - if (adds != null) { - for (String add : adds.keySet()) { - if (add.startsWith(CONFIG_SERVICECOMB_PREFIX)) { - String key = CONFIG_CSE_PREFIX + add.substring(add.indexOf(".") + 1); - injectConfig.addProperty(key, adds.get(add)); - } - } - } - - Map deletes = watchedUpdateResult.getDeleted(); - if (deletes != null) { - for (String delete : deletes.keySet()) { - if (delete.startsWith(CONFIG_SERVICECOMB_PREFIX)) { - injectConfig.clearProperty(CONFIG_CSE_PREFIX + delete.substring(delete.indexOf(".") + 1)); - } - } - } - - Map changes = watchedUpdateResult.getChanged(); - if (changes != null) { - for (String change : changes.keySet()) { - if (change.startsWith(CONFIG_SERVICECOMB_PREFIX)) { - String key = CONFIG_CSE_PREFIX + change.substring(change.indexOf(".") + 1); - injectConfig.setProperty(key, changes.get(change)); - } - } - } - } - } -} diff --git a/foundations/foundation-config/src/main/java/io/servicecomb/config/ConfigurationSpringInitializer.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/ConfigurationSpringInitializer.java deleted file mode 100644 index 51d0bb7c9b3..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/ConfigurationSpringInitializer.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config; - -import java.io.IOException; -import java.util.Iterator; -import java.util.Properties; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; -import org.springframework.core.Ordered; - -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicPropertyFactory; - -public class ConfigurationSpringInitializer extends PropertyPlaceholderConfigurer { - public ConfigurationSpringInitializer() { - ConfigUtil.installDynamicConfig(); - setOrder(Ordered.LOWEST_PRECEDENCE / 2); - setIgnoreUnresolvablePlaceholders(true); - } - - @Override - protected Properties mergeProperties() throws IOException { - Properties properties = super.mergeProperties(); - - AbstractConfiguration config = ConfigurationManager.getConfigInstance(); - Iterator iter = config.getKeys(); - while (iter.hasNext()) { - String key = iter.next(); - Object value = config.getProperty(key); - properties.put(key, value); - } - return properties; - } - - @Override - protected String resolvePlaceholder(String placeholder, Properties props) { - String propertyValue = super.resolvePlaceholder(placeholder, props); - if (propertyValue == null) { - return DynamicPropertyFactory.getInstance().getStringProperty(placeholder, null).get(); - } - return propertyValue; - } -} diff --git a/foundations/foundation-config/src/main/java/io/servicecomb/config/DynamicPropertiesImpl.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/DynamicPropertiesImpl.java deleted file mode 100644 index c6a6d28fcbb..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/DynamicPropertiesImpl.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config; - -import java.util.Arrays; -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 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; - -class DynamicPropertiesImpl implements DynamicProperties { - - DynamicPropertiesImpl() { - } - - DynamicPropertiesImpl(AbstractConfiguration... configurations) { - ConcurrentCompositeConfiguration configuration = new ConcurrentCompositeConfiguration(); - Arrays.stream(configurations).forEach(configuration::addConfiguration); - - ConfigurationManager.install(configuration); - } - - @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(); - } - - @Override - public String getStringProperty(String propertyName, String defaultValue) { - return propertyFactoryInstance().getStringProperty(propertyName, defaultValue).get(); - } - - @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(); - } - - @Override - public int getIntProperty(String propertyName, int defaultValue) { - return propertyFactoryInstance().getIntProperty(propertyName, defaultValue).get(); - } - - @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(); - } - - @Override - public long getLongProperty(String propertyName, long defaultValue) { - return propertyFactoryInstance().getLongProperty(propertyName, defaultValue).get(); - } - - @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(); - } - - @Override - public float getFloatProperty(String propertyName, float defaultValue) { - return propertyFactoryInstance().getFloatProperty(propertyName, defaultValue).get(); - } - - @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(); - } - - @Override - public double getDoubleProperty(String propertyName, double defaultValue) { - return propertyFactoryInstance().getDoubleProperty(propertyName, defaultValue).get(); - } - - @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(); - } - - @Override - public boolean getBooleanProperty(String propertyName, boolean defaultValue) { - return propertyFactoryInstance().getBooleanProperty(propertyName, defaultValue).get(); - } - - private DynamicPropertyFactory propertyFactoryInstance() { - return DynamicPropertyFactory.getInstance(); - } -} diff --git a/foundations/foundation-config/src/main/java/io/servicecomb/config/LastPropertyPlaceholderConfigurer.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/LastPropertyPlaceholderConfigurer.java deleted file mode 100644 index c6eb8d29cac..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/LastPropertyPlaceholderConfigurer.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; -import org.springframework.core.Ordered; -import org.springframework.stereotype.Component; - -// 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 -@Component -public class LastPropertyPlaceholderConfigurer implements BeanFactoryPostProcessor, Ordered { - @Override - public int getOrder() { - return Ordered.LOWEST_PRECEDENCE; - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - new PropertyPlaceholderConfigurer().postProcessBeanFactory(beanFactory); - } -} diff --git a/foundations/foundation-config/src/main/java/io/servicecomb/config/YAMLUtil.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/YAMLUtil.java deleted file mode 100644 index 126231304e2..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/YAMLUtil.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config; - -import java.io.InputStream; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.yaml.snakeyaml.Yaml; - -/** - * Created by on 2017/1/5. - */ -public final class YAMLUtil { - private YAMLUtil() { - } - - @SuppressWarnings("unchecked") - public static Map yaml2Properties(InputStream input) { - Map configurations = new LinkedHashMap(); - Yaml yaml = new Yaml(); - yaml.loadAll(input).forEach(data -> configurations.putAll(retrieveItems("", (Map) data))); - return configurations; - } - - @SuppressWarnings("unchecked") - public static Map retrieveItems(String prefix, Map propertieMap) { - Map result = new LinkedHashMap(); - if (!prefix.isEmpty()) { - prefix += "."; - } - - for (Map.Entry entry : propertieMap.entrySet()) { - if (entry.getValue() instanceof Map) { - result.putAll(retrieveItems(prefix + entry.getKey(), (Map) entry.getValue())); - } else { - result.put(prefix + entry.getKey(), entry.getValue()); - } - } - return result; - } -} diff --git a/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/scheduler/NeverStartPollingScheduler.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/scheduler/NeverStartPollingScheduler.java deleted file mode 100644 index afe132326c4..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/scheduler/NeverStartPollingScheduler.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.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/io/servicecomb/config/archaius/sources/AbstractConfigLoader.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/AbstractConfigLoader.java deleted file mode 100644 index cd62e14dab8..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/AbstractConfigLoader.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config.archaius.sources; - -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.util.ResourceUtils; - -public abstract class AbstractConfigLoader { - protected String orderKey = "config-order"; - - public void setOrderKey(String orderKey) { - this.orderKey = orderKey; - } - - protected final List configModels = new ArrayList<>(); - - public List getConfigModels() { - return configModels; - } - - public void load(String resourceName) throws IOException { - loadFromClassPath(resourceName); - } - - protected void loadFromClassPath(String resourceName) throws IOException { - List urlList = findURLFromClassPath(resourceName); - for (URL url : urlList) { - ConfigModel configModel = load(url); - configModels.add(configModel); - } - } - - public ConfigModel load(URL url) throws IOException { - Map config = loadData(url); - // load a empty or all commented yaml, will get a null map - // this is not a error - if (config == null) { - config = new LinkedHashMap<>(); - } - - ConfigModel configModel = new ConfigModel(); - configModel.setUrl(url); - configModel.setConfig(config); - Object objOrder = config.get(orderKey); - if (objOrder != null) { - if (Integer.class.isInstance(objOrder)) { - configModel.setOrder((int) objOrder); - } else { - configModel.setOrder(Integer.parseInt(String.valueOf(objOrder))); - } - } - return configModel; - } - - protected abstract Map loadData(URL url) throws IOException; - - protected List findURLFromClassPath(String resourceName) throws IOException { - List urlList = new ArrayList<>(); - - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - Enumeration urls = loader.getResources(resourceName); - while (urls.hasMoreElements()) { - urlList.add(urls.nextElement()); - } - - return urlList; - } - - private class ConfigModelWrapper { - ConfigModel model; - - int addOrder; - } - - // sort rule: - // 1.files in jar - // 2.smaller order - // 3.add to list earlier - protected void sort() { - List list = new ArrayList<>(configModels.size()); - for (int idx = 0; idx < configModels.size(); idx++) { - ConfigModelWrapper wrapper = new ConfigModelWrapper(); - wrapper.model = configModels.get(idx); - wrapper.addOrder = idx; - list.add(wrapper); - } - - list.sort(this::doSort); - - for (int idx = 0; idx < configModels.size(); idx++) { - configModels.set(idx, list.get(idx).model); - } - } - - private int doSort(ConfigModelWrapper w1, ConfigModelWrapper w2) { - ConfigModel m1 = w1.model; - ConfigModel m2 = w2.model; - boolean isM1Jar = ResourceUtils.isJarURL(m1.getUrl()); - boolean isM2Jar = ResourceUtils.isJarURL(m2.getUrl()); - if (isM1Jar != isM2Jar) { - if (isM1Jar) { - return -1; - } - - return 1; - } - // min order load first - int result = m1.getOrder() - m2.getOrder(); - if (result != 0) { - return result; - } - - return doFinalSort(w1, w2); - } - - private int doFinalSort(ConfigModelWrapper w1, ConfigModelWrapper w2) { - return w1.addOrder - w2.addOrder; - } -} diff --git a/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/ConfigModel.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/ConfigModel.java deleted file mode 100644 index bc605733830..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/ConfigModel.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config.archaius.sources; - -import java.net.URL; -import java.util.Map; - -public class ConfigModel { - private URL url; - - private int order; - - private Map config; - - public URL getUrl() { - return url; - } - - public void setUrl(URL url) { - this.url = url; - } - - public int getOrder() { - return order; - } - - public void setOrder(int order) { - this.order = order; - } - - public Map getConfig() { - return config; - } - - public void setConfig(Map config) { - this.config = config; - } -} diff --git a/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/ConfigSourceMaker.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/ConfigSourceMaker.java deleted file mode 100644 index eeec109f074..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/ConfigSourceMaker.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.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/io/servicecomb/config/archaius/sources/MicroserviceConfigLoader.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/MicroserviceConfigLoader.java deleted file mode 100644 index 01236b0aba2..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/MicroserviceConfigLoader.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config.archaius.sources; - -import java.io.IOException; -import java.net.URL; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -import io.servicecomb.foundation.common.exceptions.ServiceCombException; - -public class MicroserviceConfigLoader extends YAMLConfigLoader { - private static final Logger LOGGER = LoggerFactory.getLogger(MicroserviceConfigLoader.class); - - private static final String ADDITIONAL_CONFIG_URL = "cse.configurationSource.additionalUrls"; - - /** - * Default configuration file name to be used by default constructor. This file should - * be on the classpath. The file name can be overridden by the value of system property - * configurationSource.defaultFileName - */ - private static final String DEFAULT_CONFIG_FILE_NAME = "microservice.yaml"; - - public MicroserviceConfigLoader() { - setOrderKey("cse-config-order"); - } - - public void loadAndSort() { - try { - String configFileFromClasspath = - System.getProperty("cse.configurationSource.defaultFileName") == null ? DEFAULT_CONFIG_FILE_NAME - : System.getProperty("cse.configurationSource.defaultFileName"); - super.load(configFileFromClasspath); - loadAdditionalConfig(); - - if (configModels.isEmpty()) { - LOGGER.warn("No URLs will be polled as dynamic configuration sources."); - LOGGER.warn( - "To enable URLs as dynamic configuration sources, define System property {} or make {} available on classpath.", - ADDITIONAL_CONFIG_URL, - configFileFromClasspath); - } - - sort(); - } catch (IOException e) { - throw new ServiceCombException("Failed to load microservice config", e); - } - } - - private void loadAdditionalConfig() throws IOException { - String strUrls = System.getProperty(ADDITIONAL_CONFIG_URL); - if (StringUtils.isEmpty(strUrls)) { - return; - } - - for (String strUrl : strUrls.split(",")) { - URL url = new URL(strUrl); - ConfigModel configModel = load(url); - configModels.add(configModel); - } - } -} diff --git a/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/MicroserviceConfigurationSource.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/MicroserviceConfigurationSource.java deleted file mode 100644 index 8ff65333cdf..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/MicroserviceConfigurationSource.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config.archaius.sources; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.netflix.config.PollResult; -import com.netflix.config.PolledConfigurationSource; - -import io.servicecomb.config.ConfigMapping; -import io.servicecomb.config.YAMLUtil; - -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())); - } - - configurations = ConfigMapping.getConvertedMap(configurations); - return PollResult.createFull(configurations); - } -} diff --git a/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/YAMLConfigLoader.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/YAMLConfigLoader.java deleted file mode 100644 index 2093d8a7ae1..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/archaius/sources/YAMLConfigLoader.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config.archaius.sources; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Map; - -import org.yaml.snakeyaml.Yaml; - -public class YAMLConfigLoader extends AbstractConfigLoader { - @SuppressWarnings("unchecked") - @Override - protected Map loadData(URL url) throws IOException { - Yaml yaml = new Yaml(); - - try (InputStream inputStream = url.openStream()) { - return yaml.loadAs(inputStream, Map.class); - } - } -} diff --git a/foundations/foundation-config/src/main/java/io/servicecomb/config/spi/ConfigCenterConfigurationSource.java b/foundations/foundation-config/src/main/java/io/servicecomb/config/spi/ConfigCenterConfigurationSource.java deleted file mode 100644 index 2ca4e96a313..00000000000 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/spi/ConfigCenterConfigurationSource.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config.spi; - -import org.apache.commons.configuration.Configuration; - -import com.netflix.config.WatchedConfigurationSource; - -public interface ConfigCenterConfigurationSource extends WatchedConfigurationSource { - void init(Configuration localConfiguration); -} 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 new file mode 100644 index 00000000000..046b326b8b2 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/BootStrapProperties.java @@ -0,0 +1,218 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.Set; + +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 + // service definition keys of old version + public static final String OLD_CONFIG_SERVICE_APPLICATION = "APPLICATION_ID"; + + public static final String OLD_CONFIG_SERVICE_NAME = "service_description.name"; + + public static final String OLD_CONFIG_SERVICE_VERSION = "service_description.version"; + + 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"; + + public static final String OLD_CONFIG_SERVICE_EXTENDED_CLASS = "service_description.propertyExtendedClass"; + + public static final String OLD_CONFIG_SERVICE_PROPERTIES = "service_description.properties"; + + // 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_INITIAL_STATUS = "instance_description.initialStatus"; + + // service definition keys of new version + public static final String CONFIG_SERVICE_APPLICATION = "servicecomb.service.application"; + + public static final String CONFIG_SERVICE_NAME = "servicecomb.service.name"; + + public static final String CONFIG_SERVICE_VERSION = "servicecomb.service.version"; + + 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"; + + public static final String CONFIG_SERVICE_EXTENDED_CLASS = "servicecomb.service.propertyExtendedClass"; + + public static final String CONFIG_SERVICE_PROPERTIES = "servicecomb.service.properties"; + + // 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_INITIAL_STATUS = "servicecomb.instance.initialStatus"; + + // configuration default values + public static final String DEFAULT_APPLICATION = "default"; + + public static final String DEFAULT_MICROSERVICE_NAME = "defaultMicroservice"; + + public static final String DEFAULT_MICROSERVICE_VERSION = "1.0.0.0"; + + public static final String DEFAULT_MICROSERVICE_ROLE = "FRONT"; + + public static final String DEFAULT_MICROSERVICE_ENVIRONMENT = ""; + + public static final String DEFAULT_MICROSERVICE_INSTANCE_INITIAL_STATUS = "UP"; + + public static String readApplication(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_APPLICATION, + OLD_CONFIG_SERVICE_APPLICATION, DEFAULT_APPLICATION); + } + + 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 readServiceVersion(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_VERSION, OLD_CONFIG_SERVICE_VERSION, + DEFAULT_MICROSERVICE_VERSION); + } + + public static String readServiceRole(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_ROLE, OLD_CONFIG_SERVICE_ROLE, DEFAULT_MICROSERVICE_ROLE); + } + + public static String readServiceAlias(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_ALIAS, OLD_CONFIG_SERVICE_ALIAS, null); + } + + public static String readServiceDescription(Environment environment) { + String[] descriptionArray = environment.getProperty(CONFIG_SERVICE_DESCRIPTION, String[].class); + if (null == descriptionArray || descriptionArray.length < 1) { + descriptionArray = environment.getProperty(OLD_CONFIG_SERVICE_DESCRIPTION, String[].class); + } + + if (null == descriptionArray || descriptionArray.length < 1) { + return null; + } + + StringBuilder rawDescriptionBuilder = new StringBuilder(); + for (String desc : descriptionArray) { + rawDescriptionBuilder.append(desc).append(","); + } + + return rawDescriptionBuilder.substring(0, rawDescriptionBuilder.length() - 1); + } + + public static String readServiceEnvironment(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_ENVIRONMENT, OLD_CONFIG_SERVICE_ENVIRONMENT, + DEFAULT_MICROSERVICE_ENVIRONMENT); + } + + public static String readServiceExtendedClass(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_EXTENDED_CLASS, OLD_CONFIG_SERVICE_EXTENDED_CLASS, + null); + } + + public static Map readServiceProperties(Environment environment) { + return readProperties(environment, CONFIG_SERVICE_PROPERTIES, OLD_CONFIG_SERVICE_PROPERTIES); + } + + public static Map readServiceInstanceProperties(Environment environment) { + return readProperties(environment, CONFIG_SERVICE_INSTANCE_PROPERTIES, OLD_CONFIG_SERVICE_INSTANCE_PROPERTIES); + } + + public static String readServiceInstanceExtendedClass(Environment environment) { + return readStringValue(environment, CONFIG_SERVICE_INSTANCE_EXTENDED_CLASS, + OLD_CONFIG_SERVICE_INSTANCE_EXTENDED_CLASS, + null); + } + + 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); + } + + private static String readStringValue(Environment environment, String newKey, String oldKey, + String defaultValue) { + String result = environment.getProperty(newKey); + if (result == null) { + return environment.getProperty(oldKey, defaultValue); + } + return result; + } + + 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); + } + Map result = new HashMap<>(keys.size()); + for (String key : keys) { + result.put(key.substring(prefix.length() + 1), environment.getProperty(key)); + } + return result; + } + + private static void checkMicroserviceName(String name) { + // the configuration we used + // when resolve placeholder failed + // the result will remains ${var} + if (StringUtils.isEmpty(name) || name.contains("${")) { + throw new IllegalArgumentException(String.format( + "MicroserviceName '%s' is invalid. you must configure '%s' or set the placeholder value.", + name, + CONFIG_SERVICE_NAME)); + } + } +} 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 new file mode 100644 index 00000000000..d80caaa51a9 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigMapping.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.config; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.config.file.AbstractConfigLoader; +import org.apache.servicecomb.foundation.common.utils.JvmUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +import com.google.common.annotations.VisibleForTesting; + +/** + * Created by on 2017/1/5. + */ +public final class ConfigMapping { + private static final Map configMap; + + private static final Logger LOGGER = LoggerFactory.getLogger(ConfigMapping.class); + + static { + ClassLoader loader = JvmUtils.findClassLoader(); + List> mappingList = new ArrayList<>(); + configMap = new HashMap<>(); + Enumeration urls; + try { + urls = loader.getResources("mapping.yaml"); + while (urls.hasMoreElements()) { + try (InputStream in = urls.nextElement().openStream()) { + mappingList.add(YAMLUtil.yaml2Properties(in)); + } + } + + mappingList.sort((a, b) -> { + int orderA = a.get(AbstractConfigLoader.ORDER_KEY) == null ? 0 : (int) a.get(AbstractConfigLoader.ORDER_KEY); + int orderB = b.get(AbstractConfigLoader.ORDER_KEY) == null ? 0 : (int) b.get(AbstractConfigLoader.ORDER_KEY); + return orderA - orderB; + }); + + mappingList.forEach(item -> { + item.remove(AbstractConfigLoader.ORDER_KEY); + configMap.putAll(item); + }); + } catch (IOException e) { + LOGGER.error("get config mapping file error!", e); + } + } + + private ConfigMapping() { + } + + @VisibleForTesting + @SuppressWarnings("unchecked") + static T map(String key) { + return (T) configMap.get(key); + } + + public static Map getMapping() { + return configMap; + } + + public static Map getConvertedMap(Map oldMap) { + if (configMap == null) { + return new LinkedHashMap<>(); + } + Map retMap = new LinkedHashMap<>(); + retMap.putAll(oldMap); + configMap.entrySet().forEach(entry -> putConfigsToRetMap(retMap, entry, oldMap.get(entry.getKey()))); + return retMap; + } + + public static Map getConvertedMap(Environment environment) { + if (configMap == null) { + return new LinkedHashMap<>(); + } + Map retMap = new LinkedHashMap<>(); + configMap.entrySet().forEach(entry -> putConfigsToRetMap(retMap, entry, environment.getProperty(entry.getKey()))); + return retMap; + } + + private static void putConfigsToRetMap(Map retMap, Map.Entry entry, + Object configValue) { + if (configValue != null) { + if (entry.getValue() instanceof List) { + @SuppressWarnings("unchecked") + List newKeys = (List) entry.getValue(); + newKeys.forEach(newKey -> retMap.put(newKey, configValue)); + return; + } + String newKey = (String) entry.getValue(); + retMap.put(newKey, configValue); + } + } +} 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 new file mode 100644 index 00000000000..a09b2eaac36 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigUtil.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.config; + +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.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 ConfigUtil() { + } + + public static List parseArrayValue(String value) { + if (value == null) { + return new ArrayList<>(0); + } + String[] tokens = value.split(","); + List result = new ArrayList<>(tokens.length); + for (String t : tokens) { + result.add(t.trim()); + } + 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); + } + } + } + } + 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)); + } + } + } + } + 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/io/servicecomb/config/DynamicProperties.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicProperties.java similarity index 79% rename from foundations/foundation-config/src/main/java/io/servicecomb/config/DynamicProperties.java rename to foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicProperties.java index a9190ad10bb..58f84dd97b0 100644 --- a/foundations/foundation-config/src/main/java/io/servicecomb/config/DynamicProperties.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicProperties.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +15,7 @@ * limitations under the License. */ -package io.servicecomb.config; +package org.apache.servicecomb.config; import java.util.function.Consumer; import java.util.function.DoubleConsumer; @@ -22,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; } @@ -73,4 +70,4 @@ default boolean getBooleanProperty(String propertyName, Consumer consum default boolean getBooleanProperty(String propertyName, boolean defaultValue) { return defaultValue; } -} \ No newline at end of file +} 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 new file mode 100644 index 00000000000..323e84d80aa --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesImpl.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.servicecomb.foundation.common.event.EventManager; +import org.springframework.core.env.Environment; + +import com.google.common.eventbus.Subscribe; + +public class DynamicPropertiesImpl implements DynamicProperties { + 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); + } + + @Subscribe + public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { + for (Entry entry : event.getAdded().entrySet()) { + updateValue(entry); + } + + for (Entry entry : event.getUpdated().entrySet()) { + updateValue(entry); + } + + 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) { + 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 environment.getProperty(propertyName, defaultValue); + } + + @Override + public int getIntProperty(String propertyName, IntConsumer consumer, int defaultValue) { + 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 environment.getProperty(propertyName, int.class, defaultValue); + } + + @Override + public long getLongProperty(String propertyName, LongConsumer consumer, long defaultValue) { + 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 environment.getProperty(propertyName, long.class, defaultValue); + } + + @Override + public float getFloatProperty(String propertyName, DoubleConsumer consumer, float defaultValue) { + 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 environment.getProperty(propertyName, float.class, defaultValue); + } + + @Override + public double getDoubleProperty(String propertyName, DoubleConsumer consumer, double defaultValue) { + 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 environment.getProperty(propertyName, double.class, defaultValue); + } + + @Override + public boolean getBooleanProperty(String propertyName, Consumer consumer, boolean defaultValue) { + 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 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/FoundationConfigConfiguration.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/FoundationConfigConfiguration.java new file mode 100644 index 00000000000..1f74e897dd6 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/FoundationConfigConfiguration.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.config; + +import org.apache.servicecomb.config.inject.InjectBeanPostProcessor; +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 static InjectBeanPostProcessor scbInjectBeanPostProcessor( + @Autowired @Lazy PriorityPropertyManager priorityPropertyManager) { + return new InjectBeanPostProcessor(priorityPropertyManager); + } + + @Bean + public PriorityPropertyManager scbPriorityPropertyManager(ConfigObjectFactory configObjectFactory) { + return new PriorityPropertyManager(configObjectFactory); + } + + @Bean + public PriorityPropertyFactory scbPriorityPropertyFactory(Environment environment) { + return new PriorityPropertyFactory(environment); + } + + @Bean + public DynamicPropertiesImpl scbDynamicProperties(Environment environment) { + return new DynamicPropertiesImpl(environment); + } + + @Bean + public ConfigObjectFactory scbConfigObjectFactory(PriorityPropertyFactory propertyFactory) { + return new ConfigObjectFactory(propertyFactory); + } + + @Bean + @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/YAMLUtil.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/YAMLUtil.java new file mode 100644 index 00000000000..2dc116a304b --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/YAMLUtil.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.InputStream; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.yaml.snakeyaml.LoaderOptions; +import org.yaml.snakeyaml.TypeDescription; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.constructor.SafeConstructor; + +public final class YAMLUtil { + private YAMLUtil() { + } + + private static Yaml safeParser() { + // Yaml instance is not thread safe, create a new instance for each parser + return new Yaml(new SafeConstructor(new LoaderOptions())); + } + + /** + * load a input {@link InputStream} to be a map {@link Map}, you have to close the inputStream by yourself, such as:
+ *

try (InputStream in = url.openStream()) {
+ *      configMap.putAll(YAMLUtil.yaml2Properties(in));
+ * }
+ *

+ * @param input the stream to be loaded + * @return a config map + */ + @SuppressWarnings("unchecked") + public static Map yaml2Properties(InputStream input) { + Map configurations = new LinkedHashMap<>(); + safeParser().loadAll(input).forEach(data -> { + if (data instanceof Map && isValidMap((Map) data)) { + configurations.putAll(retrieveItems("", (Map) data)); + } else { + throw new IllegalArgumentException("input cannot be convert to map"); + } + }); + return configurations; + } + + @SuppressWarnings("unchecked") + private static boolean isValidMap(Map data) { + for (Map.Entry entry : data.entrySet()) { + Object key = entry.getKey(); + Object value = entry.getValue(); + if (key instanceof String) { + if (value instanceof Map) { + return isValidMap((Map) value); + } + continue; + } + return false; + } + return true; + } + + /** + * load a input {@link String} to be a map {@link Map} + * @param input the String to be loaded + * @return a config map + */ + @SuppressWarnings("unchecked") + public static Map yaml2Properties(String input) { + Map configurations = new LinkedHashMap<>(); + safeParser().loadAll(input).forEach(data -> { + if (data instanceof Map && isValidMap((Map) data)) { + configurations.putAll(retrieveItems("", (Map) data)); + } else { + throw new IllegalArgumentException("input cannot be convert to map"); + } + }); + return configurations; + } + + public static T parserObject(String yamlContent, Class clazz) { + Yaml parser = new Yaml(new Constructor(new TypeDescription(clazz, clazz), new LoaderOptions())); + return parser.loadAs(yamlContent, clazz); + } + + @SuppressWarnings("unchecked") + public static Map retrieveItems(String prefix, Map propertiesMap) { + Map result = new LinkedHashMap<>(); + if (!prefix.isEmpty()) { + prefix += "."; + } + + for (Map.Entry entry : propertiesMap.entrySet()) { + if (entry.getValue() instanceof Map) { + result.putAll(retrieveItems(prefix + entry.getKey(), (Map) entry.getValue())); + } else { + String key = prefix + entry.getKey(); + result.put(key, entry.getValue()); + } + } + return result; + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/AbstractConfigLoader.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/AbstractConfigLoader.java new file mode 100644 index 00000000000..085afc8c7e5 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/AbstractConfigLoader.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.file; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.utils.JvmUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ResourceUtils; + +public abstract class AbstractConfigLoader { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractConfigLoader.class); + + public static final String ORDER_KEY = "servicecomb-config-order"; + + protected final List configModels = new ArrayList<>(); + + public List getConfigModels() { + return configModels; + } + + public void load(String resourceName) throws IOException { + loadFromClassPath(resourceName); + } + + protected void loadFromClassPath(String resourceName) throws IOException { + List urlList = findURLFromClassPath(resourceName); + for (URL url : urlList) { + ConfigModel configModel = load(url); + configModels.add(configModel); + } + } + + public ConfigModel load(URL url) throws IOException { + Map config = loadData(url); + // load a empty or all commented yaml, will get a null map + // this is not an error + if (config == null) { + config = new LinkedHashMap<>(); + } + + ConfigModel configModel = new ConfigModel(); + configModel.setUrl(url); + configModel.setConfig(config); + + Object objOrder = config.get(ORDER_KEY); + if (objOrder == null) { + // compatible check + objOrder = config.get("cse-config-order"); + if (objOrder != null) { + LOGGER.error("cse-config-order will not be supported in future, please change it to servicecomb-config-order"); + } + } + + if (objOrder != null) { + if (Integer.class.isInstance(objOrder)) { + configModel.setOrder((int) objOrder); + } else { + configModel.setOrder(Integer.parseInt(String.valueOf(objOrder))); + } + } + return configModel; + } + + protected abstract Map loadData(URL url) throws IOException; + + protected List findURLFromClassPath(String resourceName) throws IOException { + List urlList = new ArrayList<>(); + + ClassLoader loader = JvmUtils.findClassLoader(); + Enumeration urls = loader.getResources(resourceName); + while (urls.hasMoreElements()) { + urlList.add(urls.nextElement()); + } + + return urlList; + } + + private static class ConfigModelWrapper { + ConfigModel model; + + int addOrder; + } + + // sort rule: + // 1.files in jar + // 2.smaller order + // 3.add to list earlier + protected void sort() { + List list = new ArrayList<>(configModels.size()); + for (int idx = 0; idx < configModels.size(); idx++) { + ConfigModelWrapper wrapper = new ConfigModelWrapper(); + wrapper.model = configModels.get(idx); + wrapper.addOrder = idx; + list.add(wrapper); + } + + list.sort(this::doSort); + + for (int idx = 0; idx < configModels.size(); idx++) { + configModels.set(idx, list.get(idx).model); + } + } + + private int doSort(ConfigModelWrapper w1, ConfigModelWrapper w2) { + ConfigModel m1 = w1.model; + ConfigModel m2 = w2.model; + boolean isM1Jar = ResourceUtils.isJarURL(m1.getUrl()); + boolean isM2Jar = ResourceUtils.isJarURL(m2.getUrl()); + if (isM1Jar != isM2Jar) { + if (isM1Jar) { + return -1; + } + + return 1; + } + // min order load first + int result = Integer.compare(m1.getOrder(), m2.getOrder()); + if (result != 0) { + return result; + } + + return doFinalSort(w1, w2); + } + + private int doFinalSort(ConfigModelWrapper w1, ConfigModelWrapper w2) { + return Integer.compare(w1.addOrder, w2.addOrder); + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/ConfigModel.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/ConfigModel.java new file mode 100644 index 00000000000..41732f9ab71 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/ConfigModel.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.config.file; + +import java.net.URL; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.utils.IOUtils; + +public class ConfigModel { + + private URL url; + + private int order; + + private Map config; + + public URL getUrl() { + return url; + } + + public void setUrl(URL url) { + this.url = url; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public Map getConfig() { + return config; + } + + public void setConfig(Map config) { + this.config = config; + } + + @Override + public String toString() { + return url == null ? "" : IOUtils.anonymousPath(url.toString()); + } + +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/MicroserviceConfigLoader.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/MicroserviceConfigLoader.java new file mode 100644 index 00000000000..f33f9cc237f --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/MicroserviceConfigLoader.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.config.file; + +import java.io.IOException; +import java.net.URL; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class MicroserviceConfigLoader extends YAMLConfigLoader { + private static final Logger LOGGER = LoggerFactory.getLogger(MicroserviceConfigLoader.class); + + private static final String ADDITIONAL_CONFIG_URL = "servicecomb.configurationSource.additionalUrls"; + + private static final String DEFAULT_FILE_NAME = "servicecomb.configurationSource.defaultFileName"; + + /** + * Default configuration file name to be used by default constructor. This file should + * be on the classpath. The file name can be overridden by the value of system property + * configurationSource.defaultFileName + */ + private static final String DEFAULT_CONFIG_FILE_NAME = "microservice.yaml"; + + public MicroserviceConfigLoader() { + // Help to resolve incompatible changes. Can be deleted in future. + if (!StringUtils.isEmpty(System.getProperty("cse.configurationSource.additionalUrls"))) { + throw new IllegalArgumentException("-Dcse.configurationSource.additionalUrls" + + " has been replaced with -D" + ADDITIONAL_CONFIG_URL + ", please change it and restart."); + } + if (!StringUtils.isEmpty(System.getProperty("cse.configurationSource.defaultFileName"))) { + throw new IllegalArgumentException("-Dcse.configurationSource.additionalUrls" + + " has been replaced with -D" + DEFAULT_FILE_NAME + ", please change it and restart."); + } + } + + public void loadAndSort() { + String configFileFromClasspath = null; + try { + configFileFromClasspath = + System.getProperty(DEFAULT_FILE_NAME) == null ? DEFAULT_CONFIG_FILE_NAME + : System.getProperty(DEFAULT_FILE_NAME); + super.load(configFileFromClasspath); + loadAdditionalConfig(); + + if (configModels.isEmpty()) { + LOGGER.warn("No URLs will be polled as dynamic configuration sources."); + LOGGER.warn( + "To enable URLs as dynamic configuration sources, define System property {} or make {} available on classpath.", + ADDITIONAL_CONFIG_URL, + configFileFromClasspath); + } + + sort(); + } catch (Exception e) { + throw new ServiceCombException("Failed to load microservice configFile " + configFileFromClasspath, e); + } + } + + private void loadAdditionalConfig() throws IOException { + String strUrls = System.getProperty(ADDITIONAL_CONFIG_URL); + if (StringUtils.isEmpty(strUrls)) { + return; + } + + for (String strUrl : strUrls.split(",")) { + URL url = new URL(strUrl); + ConfigModel configModel = load(url); + configModels.add(configModel); + } + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/YAMLConfigLoader.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/YAMLConfigLoader.java new file mode 100644 index 00000000000..ac69e8d4df0 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/file/YAMLConfigLoader.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.config.file; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Map; + +import org.apache.servicecomb.config.YAMLUtil; + +public class YAMLConfigLoader extends AbstractConfigLoader { + @Override + protected Map loadData(URL url) throws IOException { + try (InputStream inputStream = url.openStream()) { + return YAMLUtil.yaml2Properties(inputStream); + } + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/InjectBeanPostProcessor.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/InjectBeanPostProcessor.java new file mode 100644 index 00000000000..fbdeff42554 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/InjectBeanPostProcessor.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.config.inject; + +import java.util.Collections; + +import org.apache.servicecomb.config.priority.PriorityPropertyManager; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; + +public class InjectBeanPostProcessor implements BeanPostProcessor { + private final PriorityPropertyManager priorityPropertyManager; + + public InjectBeanPostProcessor(PriorityPropertyManager priorityPropertyManager) { + this.priorityPropertyManager = priorityPropertyManager; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + Class beanCls = BeanUtils.getImplClassFromBean(bean); + InjectProperties injectProperties = beanCls.getAnnotation(InjectProperties.class); + if (injectProperties == null) { + return bean; + } + + priorityPropertyManager.createConfigObject(bean, Collections.emptyMap()); + + return bean; + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/InjectProperties.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/InjectProperties.java new file mode 100644 index 00000000000..1bee2882473 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/InjectProperties.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.config.inject; + +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; + +@Documented +@Retention(RUNTIME) +@Target(TYPE) +@Inherited +public @interface InjectProperties { + /** + * The name prefix of the properties that are valid to bind to this object. + * @return the name prefix of the properties to bind + */ + String prefix() default ""; +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/InjectProperty.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/InjectProperty.java new file mode 100644 index 00000000000..3c19e6f7e1d --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/InjectProperty.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.config.inject; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Documented +@Retention(RUNTIME) +@Target(FIELD) +public @interface InjectProperty { + /** + * override prefix of {@link InjectProperties} + * @return prefix of keys + */ + String prefix() default ""; + + /** + *
+   * The name of the property
+   * 1.support priority
+   *   high,middle,low
+   * 2.support placeholder
+   *   name is root.${placeholder-1}.name
+   *   placeholder-1 is k1
+   *   then final name is root.k1.name
+   * 3.placeholder can be a list
+   *   name is root.${placeholder-1}.name
+   *   placeholder-1 is a list, value is k1-1/k1-2
+   *   then final name is root.k1-1.name/root.k1-2.name
+   * 
+ * @return the name of the property + */ + String[] keys() default {}; + + String defaultValue() default ""; +} 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 new file mode 100644 index 00000000000..b05ab11a73c --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/inject/PlaceholderResolver.java @@ -0,0 +1,210 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config.inject; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; + +/** + *
+ * not care for performance
+ *
+ * behavior of multiple list if defined:
+ *   org.apache.servicecomb.config.inject.TestPlaceholderResolver#multi_list()
+ * behavior of nested and multiple list variable is undefined
+ *   org.apache.servicecomb.config.inject.TestPlaceholderResolver#mixed()
+ * 
+ */ +public class PlaceholderResolver { + private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("(?\\\\)?\\$\\{(?[^{}]+)\\}"); + + static class SplitPart { + boolean var; + + String fullName; + + Object value; + + public SplitPart(boolean var, String fullName) { + this.var = var; + this.fullName = fullName; + } + + @Override + public String toString() { + return "SplitPart{" + + "var=" + var + + ", fullName='" + fullName + '\'' + + ", value=" + value + + '}'; + } + } + + static class Row { + List parts = new ArrayList<>(); + + int cartesianProductCount = 1; + + int varCount = 0; + } + + public String replaceFirst(String str) { + return replace(str, Collections.emptyMap()).get(0); + } + + public String replaceFirst(String str, Map parameters) { + return replace(str, parameters).get(0); + } + + public List replace(String str, Map parameters) { + List finalRows = replaceToRows(str, parameters); + + List replaced = new ArrayList<>(); + for (Row row : finalRows) { + resolve(row, replaced); + } + + replaced.replaceAll(s -> s.replace("\\$", "$")); + return replaced; + } + + private List replaceToRows(String str, Map parameters) { + List finalRows = new ArrayList<>(); + List remainRows = new ArrayList<>(); + replaceToRows(str, parameters, remainRows, finalRows); + + for (String row : remainRows) { + List nestedRows = replaceToRows(row, parameters); + finalRows.addAll(nestedRows); + } + return finalRows; + } + + private void replaceToRows(String str, Map parameters, List remainRows, + List finalRows) { + Row row = parseToRow(str, parameters); + if (row.varCount == 0 && row.cartesianProductCount == 1) { + finalRows.add(row); + return; + } + + resolve(row, remainRows); + } + + private Row parseToRow(String str, Map parameters) { + Matcher matcher = PLACEHOLDER_PATTERN.matcher(str); + Row row = new Row(); + + int last = 0; + while (matcher.find()) { + row.parts.add(new SplitPart(false, str.substring(last, matcher.start()))); + last = matcher.end(); + + if (matcher.group("escape") != null) { + row.parts.add(new SplitPart(false, matcher.group().substring(1))); + continue; + } + + String name = matcher.group("name"); + Object value = findValue(parameters, name); + if (value instanceof Collection) { + row.cartesianProductCount *= ((Collection) value).size(); + } + if (value != null) { + row.varCount++; + } + + SplitPart splitPart = new SplitPart(value != null, matcher.group()); + splitPart.value = value; + row.parts.add(splitPart); + } + row.parts.add(new SplitPart(false, str.substring(last))); + + return row; + } + + // resolve placeholder and execute cartesian product + @SuppressWarnings("unchecked") + private void resolve(Row row, List resolvedRows) { + List stringBuilders = new ArrayList<>(); + for (int idx = 0; idx < row.cartesianProductCount; idx++) { + stringBuilders.add(new StringBuilder()); + } + + int collectionRepeatCount = 1; + for (SplitPart part : row.parts) { + if (!part.var) { + for (int idx = 0; idx < row.cartesianProductCount; idx++) { + StringBuilder sb = stringBuilders.get(idx); + + if (part.fullName.startsWith("$")) { + sb.append("\\" + part.fullName); + continue; + } + + sb.append(part.fullName); + } + continue; + } + + if (part.value instanceof Collection) { + int size = ((Collection) part.value).size(); + int rowRepeatCount = row.cartesianProductCount / size / collectionRepeatCount; + + int valueIdx = 0; + for (int collectionRepeatIdx = 0; collectionRepeatIdx < collectionRepeatCount; collectionRepeatIdx++) { + for (String value : (Collection) part.value) { + for (int repeatIdx = 0; repeatIdx < rowRepeatCount; repeatIdx++) { + StringBuilder sb = stringBuilders.get(valueIdx); + valueIdx++; + sb.append(value); + } + } + } + + collectionRepeatCount *= size; + continue; + } + + // normal var + for (int idx = 0; idx < row.cartesianProductCount; idx++) { + StringBuilder sb = stringBuilders.get(idx); + sb.append(part.value); + } + } + + for (StringBuilder sb : stringBuilders) { + resolvedRows.add(sb.toString()); + } + } + + private Object findValue(Map parameters, String key) { + Object value = parameters.get(key); + if (value == null) { + value = LegacyPropertyFactory.getStringProperty(key); + } + return value; + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/Parser.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/Parser.java new file mode 100644 index 00000000000..216253a67f1 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/Parser.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.parser; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import org.apache.commons.lang3.StringUtils; + +public interface Parser { + String CONTENT_TYPE_YAML = "yaml"; + String CONTENT_TYPE_PROPERTIES = "properties"; + String CONTENT_TYPE_RAW = "raw"; + + YamlParser yamlParser = new YamlParser(); + PropertiesParser propertiesParser = new PropertiesParser(); + RawParser rawParser = new RawParser(); + + Map parse(String content, String prefix, boolean addPrefix); + + static Parser findParser(String contentType) { + switch (contentType) { + case CONTENT_TYPE_YAML: + return yamlParser; + case CONTENT_TYPE_PROPERTIES: + return propertiesParser; + case CONTENT_TYPE_RAW: + return rawParser; + default: + throw new IllegalArgumentException("not supported contentType=" + contentType); + } + } + + @SuppressWarnings("unchecked") + static Map propertiesToMap(Properties properties, String prefix, boolean addPrefix) { + Map result = new HashMap<>(); + Enumeration keys = (Enumeration) properties.propertyNames(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + String value = properties.getProperty(key); + + if (addPrefix && !StringUtils.isEmpty(prefix)) { + key = prefix + "." + key; + } + + if (value == null) { + result.put(key, null); + } else { + result.put(key, value.trim()); + } + } + return result; + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/PropertiesParser.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/PropertiesParser.java new file mode 100644 index 00000000000..84bfae13d3a --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/PropertiesParser.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.config.parser; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Map; +import java.util.Properties; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PropertiesParser implements Parser { + private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesParser.class); + + @Override + public Map parse(String content, String prefix, boolean addPrefix) { + Properties properties = new Properties(); + try { + properties.load(new StringReader(content)); + } catch (IOException e) { + LOGGER.error("parse properties content failed, message={}", e.getMessage()); + } + return Parser.propertiesToMap(properties, prefix, addPrefix); + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/RawParser.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/RawParser.java new file mode 100644 index 00000000000..b22d52d42fb --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/RawParser.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.config.parser; + +import java.util.HashMap; +import java.util.Map; + +public class RawParser implements Parser { + @Override + public Map parse(String content, String prefix, boolean addPrefix) { + Map result = new HashMap<>(); + result.put(prefix, content); + return result; + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/YamlParser.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/YamlParser.java new file mode 100644 index 00000000000..fd572eaab55 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/parser/YamlParser.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.config.parser; + +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Properties; + +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.io.ByteArrayResource; + +public class YamlParser implements Parser { + @Override + public Map parse(String content, String prefix, boolean addPrefix) { + YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); + yamlFactory.setResources(new ByteArrayResource(content.getBytes(StandardCharsets.UTF_8))); + Properties properties = yamlFactory.getObject(); + return Parser.propertiesToMap(properties, prefix, addPrefix); + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObject.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObject.java new file mode 100644 index 00000000000..19e75c1ed34 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObject.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.config.priority; + +import java.util.List; + +public class ConfigObject { + private final T instance; + + private final List properties; + + public ConfigObject(T instance, List properties) { + this.instance = instance; + this.properties = properties; + } + + public T getInstance() { + return instance; + } + + public List getProperties() { + return properties; + } +} 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 new file mode 100644 index 00000000000..ff18b827e39 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObjectFactory.java @@ -0,0 +1,237 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.config.inject.InjectProperties; +import org.apache.servicecomb.config.inject.InjectProperty; +import org.apache.servicecomb.config.inject.PlaceholderResolver; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.fasterxml.jackson.databind.type.TypeFactory; + +/** + * must create by PriorityPropertyManager
+ * or register to PriorityPropertyManager manually
+ *
+ * ${} or ${not-exist-key} is valid key in archaius
+ * so this wrapper mechanism will not throw exception even can not find value by placeholder + */ +public class ConfigObjectFactory { + private final PriorityPropertyFactory propertyFactory; + + private final Map, JavaType> classCache = new ConcurrentHashMapEx<>(); + + private final Map javaTypeCache = new ConcurrentHashMapEx<>(); + + private final Map> beanDescriptionCache = new ConcurrentHashMapEx<>(); + + public ConfigObjectFactory(PriorityPropertyFactory propertyFactory) { + this.propertyFactory = propertyFactory; + } + + public PriorityPropertyFactory getPropertyFactory() { + return propertyFactory; + } + + public ConfigObject create(Class cls, Map parameters) { + try { + return create(cls.getDeclaredConstructor().newInstance(), parameters); + } catch (Throwable e) { + throw new IllegalStateException("create config object failed, class=" + cls.getName(), e); + } + } + + public ConfigObject create(T instance, Map parameters) { + String prefix = initPrefix(instance.getClass()); + List properties = createProperties(instance, prefix, parameters); + + return new ConfigObject<>(instance, properties); + } + + private String initPrefix(Class cls) { + InjectProperties injectProperties = cls.getAnnotation(InjectProperties.class); + if (injectProperties == null) { + return ""; + } + + String prefix = injectProperties.prefix(); + if (prefix.isEmpty()) { + return ""; + } + return prefix + "."; + } + + public List createProperties(Object instance, String prefix, Map parameters) { + List properties = new ArrayList<>(); + JavaType javaType = classCache.computeIfAbsent(instance.getClass(), TypeFactory.defaultInstance()::constructType); + BeanDescription beanDescription = javaTypeCache.computeIfAbsent(javaType, + JsonUtils.OBJ_MAPPER.getSerializationConfig()::introspect); + for (BeanPropertyDefinition propertyDefinition : beanDescription.findProperties()) { + if (propertyDefinition.getField() == null) { + continue; + } + + if (propertyDefinition.getSetter() == null && !propertyDefinition.getField().isPublic()) { + continue; + } + + 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)); + } + return properties; + } + + private PriorityProperty createPriorityProperty(Field field, String prefix, Map parameters) { + String[] keys = collectPropertyKeys(field, prefix, parameters); + + Class fieldCls = field.getType(); + switch (fieldCls.getName()) { + case "int": + return createIntProperty(field, keys, 0); + case "java.lang.Integer": + return createIntProperty(field, keys, null); + case "long": + return createLongProperty(field, keys, 0L); + case "java.lang.Long": + return createLongProperty(field, keys, null); + case "java.lang.String": + return createStringProperty(field, keys); + case "float": + return createFloatProperty(field, keys, 0f); + case "java.lang.Float": + return createFloatProperty(field, keys, null); + case "double": + return createDoubleProperty(field, keys, 0.0); + case "java.lang.Double": + return createDoubleProperty(field, keys, null); + case "boolean": + return createBooleanProperty(field, keys, false); + case "java.lang.Boolean": + return createBooleanProperty(field, keys, null); + default: + return null; + } + } + + private PriorityProperty createStringProperty(Field field, String[] keys) { + String defaultValue = null; + InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); + if (injectProperty != null) { + if (!injectProperty.defaultValue().isEmpty()) { + defaultValue = injectProperty.defaultValue(); + } + } + + return propertyFactory.getOrCreate(String.class, null, defaultValue, keys); + } + + private PriorityProperty createDoubleProperty(Field field, String[] keys, Double defaultValue) { + InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); + if (injectProperty != null) { + if (!injectProperty.defaultValue().isEmpty()) { + defaultValue = Double.parseDouble(injectProperty.defaultValue()); + } + } + + return propertyFactory.getOrCreate(Double.class, null, defaultValue, keys); + } + + private PriorityProperty createFloatProperty(Field field, String[] keys, Float defaultValue) { + InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); + if (injectProperty != null) { + if (!injectProperty.defaultValue().isEmpty()) { + defaultValue = Float.parseFloat(injectProperty.defaultValue()); + } + } + + return propertyFactory.getOrCreate(Float.class, null, defaultValue, keys); + } + + private PriorityProperty createBooleanProperty(Field field, String[] keys, Boolean defaultValue) { + InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); + if (injectProperty != null) { + if (!injectProperty.defaultValue().isEmpty()) { + defaultValue = Boolean.parseBoolean(injectProperty.defaultValue()); + } + } + + return propertyFactory.getOrCreate(Boolean.class, null, defaultValue, keys); + } + + private PriorityProperty createLongProperty(Field field, String[] keys, Long defaultValue) { + InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); + if (injectProperty != null) { + if (!injectProperty.defaultValue().isEmpty()) { + defaultValue = Long.parseLong(injectProperty.defaultValue()); + } + } + + return propertyFactory.getOrCreate(Long.class, null, defaultValue, keys); + } + + private PriorityProperty createIntProperty(Field field, String[] keys, Integer defaultValue) { + InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); + if (injectProperty != null) { + if (!injectProperty.defaultValue().isEmpty()) { + defaultValue = Integer.parseInt(injectProperty.defaultValue()); + } + } + + return propertyFactory.getOrCreate(Integer.class, null, defaultValue, keys); + } + + private String[] collectPropertyKeys(Field field, String prefix, Map parameters) { + String propertyPrefix = prefix; + String[] keys = new String[] {field.getName()}; + + InjectProperty injectProperty = field.getAnnotation(InjectProperty.class); + if (injectProperty != null) { + if (!injectProperty.prefix().isEmpty()) { + propertyPrefix = injectProperty.prefix() + "."; + } + if (injectProperty.keys().length != 0) { + keys = injectProperty.keys(); + } + } + + List finalKeys = new ArrayList<>(); + for (String key : keys) { + List resolvedKeys = new PlaceholderResolver().replace(propertyPrefix + key, parameters); + finalKeys.addAll(resolvedKeys); + } + + return finalKeys.toArray(new String[0]); + } +} 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 new file mode 100644 index 00000000000..be17eee8644 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/ConfigObjectProperty.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.config.priority; + +import org.apache.servicecomb.foundation.common.utils.bean.Setter; + +/** + * do not reference config object instance, otherwise gc for weak hash map will failed + */ +public class ConfigObjectProperty { + private final Setter setter; + + private final PriorityProperty property; + + public ConfigObjectProperty(Setter setter, PriorityProperty property) { + this.setter = setter; + this.property = property; + } + + 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 new file mode 100644 index 00000000000..c1c30896159 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityProperty.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.config.priority; + +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Objects; +import java.util.function.Function; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +/** + * must create by PriorityPropertyManager
+ * or register to PriorityPropertyManager manually + * @param + */ +public final class PriorityProperty { + private static final Logger LOGGER = LoggerFactory.getLogger(PriorityProperty.class); + + private final PriorityPropertyType propertyType; + + private final String joinedPriorityKeys; + + private final Function internalValueReader; + + private final DynamicProperty[] properties; + + private T finalValue; + + public PriorityProperty(Environment environment, PriorityPropertyType propertyType) { + this.propertyType = propertyType; + this.joinedPriorityKeys = Arrays.toString(propertyType.getPriorityKeys()); + this.internalValueReader = collectReader(propertyType.getType()); + this.properties = createProperties(environment, propertyType.getPriorityKeys()); + initValue(); + } + + 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] = new DynamicProperty(environment, key); + } + return properties; + } + + private Function collectReader(Type type) { + if (type == int.class || type == Integer.class) { + return this::readInt; + } + + if (type == long.class || type == Long.class) { + return this::readLong; + } + + if (type == String.class) { + return this::readString; + } + + if (type == boolean.class || type == Boolean.class) { + return this::readBoolean; + } + + if (type == double.class || type == Double.class) { + return this::readDouble; + } + + if (type == float.class || type == Float.class) { + return this::readFloat; + } + + throw new IllegalStateException("not support, type=" + type.getTypeName()); + } + + @SuppressWarnings("unchecked") + private T readInt(DynamicProperty property) { + return (T) property.getInteger(); + } + + @SuppressWarnings("unchecked") + private T readLong(DynamicProperty property) { + return (T) property.getLong(); + } + + @SuppressWarnings("unchecked") + private T readString(DynamicProperty property) { + return (T) property.getString(); + } + + @SuppressWarnings("unchecked") + private T readBoolean(DynamicProperty property) { + return (T) property.getBoolean(); + } + + @SuppressWarnings("unchecked") + private T readDouble(DynamicProperty property) { + return (T) property.getDouble(); + } + + @SuppressWarnings("unchecked") + private T readFloat(DynamicProperty property) { + return (T) property.getFloat(); + } + + public String[] getPriorityKeys() { + return propertyType.getPriorityKeys(); + } + + public T getDefaultValue() { + return propertyType.getDefaultValue(); + } + + public DynamicProperty[] getProperties() { + return properties; + } + + void initValue() { + String effectiveKey = doUpdateFinalValue(); + LOGGER.debug("config inited, \"{}\" set to {}, effective key is \"{}\".", + joinedPriorityKeys, finalValue, effectiveKey); + } + + synchronized boolean updateValue() { + T lastValue = finalValue; + String effectiveKey = doUpdateFinalValue(); + if (effectiveKey != null) { + LOGGER.debug("config changed, \"{}\" changed from {} to {}, effective key is \"{}\".", + joinedPriorityKeys, lastValue, finalValue, effectiveKey); + return true; + } + + return false; + } + + /** + * + * @return if value changed, return effectiveKey, otherwise null + */ + private String doUpdateFinalValue() { + T lastValue = finalValue; + + String effectiveKey = "default value"; + T value = propertyType.getDefaultValue(); + for (DynamicProperty property : properties) { + T propValue = internalValueReader.apply(property); + if (propValue == null || propValue.equals(propertyType.getInvalidValue())) { + continue; + } + + effectiveKey = property.getName(); + value = propValue; + break; + } + + if (Objects.equals(lastValue, value)) { + return null; + } + + finalValue = value; + return effectiveKey; + } + + public T getValue() { + return finalValue; + } + + public boolean isChangedKey(String changedKey) { + if (changedKey == null) { + // property source changed or clear, and so on + return true; + } + + for (DynamicProperty property : properties) { + if (changedKey.equals(property.getName())) { + return true; + } + } + + return false; + } +} 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 new file mode 100644 index 00000000000..abd43f8d404 --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyFactory.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.config.priority; + +import java.lang.reflect.Type; +import java.util.Map; +import java.util.stream.Stream; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.springframework.core.env.Environment; + +import com.google.common.annotations.VisibleForTesting; + +public class PriorityPropertyFactory { + private final Map, PriorityProperty> properties = new ConcurrentHashMapEx<>(); + + private final Environment environment; + + public PriorityPropertyFactory(Environment environment) { + this.environment = environment; + } + + @VisibleForTesting + public Stream> getProperties() { + return properties.values().stream(); + } + + @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, + 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 new file mode 100644 index 00000000000..8ed1e7dea2e --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyManager.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 static java.util.Collections.synchronizedMap; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.WeakHashMap; + +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.foundation.common.event.EventManager; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.eventbus.Subscribe; + +public class PriorityPropertyManager { + private final ConfigObjectFactory configObjectFactory; + + // key is config object instance + // value is properties of the config object instance + private final Map> configObjectMap = synchronizedMap(new WeakHashMap<>()); + + public PriorityPropertyManager(ConfigObjectFactory configObjectFactory) { + this.configObjectFactory = configObjectFactory; + EventManager.register(this); + } + + public PriorityPropertyFactory getPropertyFactory() { + return configObjectFactory.getPropertyFactory(); + } + + public void close() { + + } + + @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 -> { + for (String updatedKey : event.getChanged()) { + configObjectProperty.updateValueWhenChanged(instance, updatedKey); + } + }); + } + } + + @VisibleForTesting + public Map> getConfigObjectMap() { + return configObjectMap; + } + + public T createConfigObject(Class cls, Object... kvs) { + Map parameters = new HashMap<>(); + for (int idx = 0; idx < kvs.length; idx += 2) { + parameters.put(kvs[idx].toString(), kvs[idx + 1]); + } + + return createConfigObject(cls, parameters); + } + + public T createConfigObject(Class cls, Map parameters) { + ConfigObject configObject = configObjectFactory.create(cls, parameters); + return saveConfigObject(configObject); + } + + public T createConfigObject(T instance, Map parameters) { + ConfigObject configObject = configObjectFactory.create(instance, parameters); + return saveConfigObject(configObject); + } + + private T saveConfigObject(ConfigObject configObject) { + configObjectMap.put(configObject.getInstance(), configObject.getProperties()); + return configObject.getInstance(); + } +} diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyType.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyType.java new file mode 100644 index 00000000000..843bbd83aaa --- /dev/null +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityPropertyType.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.priority; + +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Objects; + +public class PriorityPropertyType { + private final Type type; + + // when got invalid value will try next level + // null always be a invalid value + private final T invalidValue; + + // when got invalid value by lowest level, will use defaultValue + private final T defaultValue; + + // priorityKeys[0] has the highest priority + private final String[] priorityKeys; + + public PriorityPropertyType(Type type, T invalidValue, T defaultValue, String... priorityKeys) { + this.type = type; + this.invalidValue = invalidValue; + this.defaultValue = defaultValue; + this.priorityKeys = priorityKeys; + } + + public Type getType() { + return type; + } + + public T getInvalidValue() { + return invalidValue; + } + + public T getDefaultValue() { + return defaultValue; + } + + public String[] getPriorityKeys() { + return priorityKeys; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PriorityPropertyType that = (PriorityPropertyType) o; + return type.equals(that.type) && Objects.equals(invalidValue, that.invalidValue) && Objects + .equals(defaultValue, that.defaultValue) && Arrays.equals(priorityKeys, that.priorityKeys); + } + + @Override + public int hashCode() { + int result = Objects.hash(type, invalidValue, defaultValue); + result = 31 * result + Arrays.hashCode(priorityKeys); + return result; + } +} 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 new file mode 100644 index 00000000000..1654b618370 --- /dev/null +++ b/foundations/foundation-config/src/main/resources/META-INF/spring.factories @@ -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.springframework.boot.env.EnvironmentPostProcessor=\ + org.apache.servicecomb.config.ConfigEnvironmentPostProcessor diff --git a/foundations/foundation-config/src/main/resources/META-INF/spring/cse.bean.xml b/foundations/foundation-config/src/main/resources/META-INF/spring/cse.bean.xml deleted file mode 100644 index d2947846278..00000000000 --- a/foundations/foundation-config/src/main/resources/META-INF/spring/cse.bean.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - \ No newline at end of file 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/main/resources/mapping.yaml b/foundations/foundation-config/src/main/resources/mapping.yaml index 16208acf5d7..83cd60bbc75 100644 --- a/foundations/foundation-config/src/main/resources/mapping.yaml +++ b/foundations/foundation-config/src/main/resources/mapping.yaml @@ -1,4 +1,22 @@ -eureka: - client: - serviceUrl: - defaultZone: registry.client.serviceUrl.defaultZone \ No newline at end of file +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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 + +SERVICECOMB_ENV: service_description.environment + +servicecomb.loadbalance.stats.timerIntervalInMilis: + - servicecomb.loadbalance.stats.timerIntervalInMillis diff --git a/foundations/foundation-config/src/test/java/io/servicecomb/config/ArchaiusUtils.java b/foundations/foundation-config/src/test/java/io/servicecomb/config/ArchaiusUtils.java deleted file mode 100644 index afb7ca738d3..00000000000 --- a/foundations/foundation-config/src/test/java/io/servicecomb/config/ArchaiusUtils.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config; - -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicProperty; -import com.netflix.config.DynamicPropertyFactory; - -import mockit.Deencapsulation; - -// copy from other project -// this utils useful for unit tests, but now there is no project to own it. -public class ArchaiusUtils { - public static void resetConfig() { - Deencapsulation.setField(ConfigurationManager.class, "instance", null); - Deencapsulation.setField(ConfigurationManager.class, "customConfigurationInstalled", false); - Deencapsulation.setField(DynamicPropertyFactory.class, "config", null); - Deencapsulation.setField(DynamicPropertyFactory.class, "initializedWithDefaultConfig", false); - Deencapsulation.setField(DynamicProperty.class, "dynamicPropertySupportImpl", null); - } -} diff --git a/foundations/foundation-config/src/test/java/io/servicecomb/config/DynamicPropertiesTest.java b/foundations/foundation-config/src/test/java/io/servicecomb/config/DynamicPropertiesTest.java deleted file mode 100644 index f4f680a2a41..00000000000 --- a/foundations/foundation-config/src/test/java/io/servicecomb/config/DynamicPropertiesTest.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config; - -import static com.seanyinx.github.unit.scaffolding.Randomness.nextBoolean; -import static com.seanyinx.github.unit.scaffolding.Randomness.uniquify; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; -import static org.hamcrest.number.IsCloseTo.closeTo; - -import java.util.Objects; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -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"); - - private static final String longPropertyName = uniquify("longPropertyName"); - - private static final String floatPropertyName = uniquify("floatPropertyName"); - - private static final String doublePropertyName = uniquify("doublePropertyName"); - - private static final String booleanPropertyName = uniquify("booleanPropertyName"); - - private static final String stringOldValue = uniquify("stringPropertyValue"); - - private static final int intOldValue = Randomness.nextInt(); - - private static final long longOldValue = Randomness.nextLong(); - - private static final double floatOldValue = Randomness.nextDouble(); - - private static final double doubleOldValue = Randomness.nextDouble(); - - private static final boolean booleanOldValue = nextBoolean(); - - private static final double ERROR = 0.0000001; - - private static DynamicProperties dynamicProperties; - - private static ConcurrentMapConfiguration configuration; - - private final Poller poller = new Poller(2000, 100); - - private volatile String stringPropertyValue; - - private volatile int intPropertyValue; - - private volatile long longPropertyValue; - - private volatile double floatPropertyValue; - - private volatile double doublePropertyValue; - - private volatile boolean booleanPropertyValue; - - - @BeforeClass - public static void setUpClass() throws Exception { - tearDown(); - configuration = new ConcurrentMapConfiguration(); - writeInitialConfig(); - dynamicProperties = new DynamicPropertiesImpl(configuration); - } - - @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)); - } - - @Test - public void observesSpecifiedStringProperty() throws Exception { - String property = dynamicProperties.getStringProperty(stringPropertyName, null); - assertThat(property, is(stringOldValue)); - - property = dynamicProperties.getStringProperty(stringPropertyName, value -> stringPropertyValue = value, null); - assertThat(property, is(stringOldValue)); - - String newValue = uniquify("newValue"); - - configuration.setProperty(stringPropertyName, newValue); - - poller.assertEventually(() -> Objects.equals(stringPropertyValue, newValue)); - } - - @Test - public void observesSpecifiedIntProperty() throws Exception { - int property = dynamicProperties.getIntProperty(intPropertyName, 0); - assertThat(property, is(intOldValue)); - - property = dynamicProperties.getIntProperty(intPropertyName, value -> intPropertyValue = value, 0); - assertThat(property, is(intOldValue)); - - int newValue = Randomness.nextInt(); - - configuration.setProperty(intPropertyName, String.valueOf(newValue)); - - poller.assertEventually(() -> intPropertyValue == newValue); - } - - @Test - public void observesSpecifiedLongProperty() throws Exception { - long property = dynamicProperties.getLongProperty(longPropertyName, 0); - assertThat(property, is(longOldValue)); - - property = dynamicProperties.getLongProperty(longPropertyName, value -> longPropertyValue = value, 0); - assertThat(property, is(longOldValue)); - - long newValue = Randomness.nextLong(); - - configuration.setProperty(longPropertyName, String.valueOf(newValue)); - - poller.assertEventually(() -> longPropertyValue == newValue); - } - - @Test - public void observesSpecifiedFloatProperty() throws Exception { - double property = dynamicProperties.getFloatProperty(floatPropertyName, 0); - assertThat(property, closeTo(floatOldValue, ERROR)); - - property = dynamicProperties.getFloatProperty(floatPropertyName, value -> floatPropertyValue = value, 0); - assertThat(property, closeTo(floatOldValue, ERROR)); - - double newValue = Randomness.nextDouble(); - - configuration.setProperty(floatPropertyName, String.valueOf(newValue)); - - poller.assertEventually(() -> Math.abs(floatPropertyValue - newValue) < ERROR); - } - - @Test - public void observesSpecifiedDoubleProperty() throws Exception { - double property = dynamicProperties.getDoubleProperty(doublePropertyName, 0); - assertThat(property, closeTo(doubleOldValue, ERROR)); - - property = dynamicProperties.getDoubleProperty(doublePropertyName, value -> doublePropertyValue = value, 0); - assertThat(property, closeTo(doubleOldValue, ERROR)); - - double newValue = Randomness.nextDouble(); - - configuration.setProperty(doublePropertyName, String.valueOf(newValue)); - - poller.assertEventually(() -> Math.abs(doublePropertyValue - newValue) < ERROR); - } - - @Test - public void observesSpecifiedBooleanProperty() throws Exception { - boolean property = dynamicProperties.getBooleanProperty(booleanPropertyName, false); - assertThat(property, is(booleanOldValue)); - - property = dynamicProperties.getBooleanProperty( - booleanPropertyName, - value -> booleanPropertyValue = value, - false); - assertThat(property, is(booleanOldValue)); - - boolean newValue = !booleanOldValue; - - configuration.setProperty(booleanPropertyName, String.valueOf(newValue)); - - poller.assertEventually(() -> booleanPropertyValue == newValue); - } -} diff --git a/foundations/foundation-config/src/test/java/io/servicecomb/config/MapBasedConfigurationSource.java b/foundations/foundation-config/src/test/java/io/servicecomb/config/MapBasedConfigurationSource.java deleted file mode 100644 index 864e13a84a4..00000000000 --- a/foundations/foundation-config/src/test/java/io/servicecomb/config/MapBasedConfigurationSource.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * - */ - -package io.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 com.netflix.config.WatchedUpdateListener; -import com.netflix.config.WatchedUpdateResult; - -import io.servicecomb.config.spi.ConfigCenterConfigurationSource; - -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 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/io/servicecomb/config/TestConfigMapping.java b/foundations/foundation-config/src/test/java/io/servicecomb/config/TestConfigMapping.java deleted file mode 100644 index e8e8cb838b4..00000000000 --- a/foundations/foundation-config/src/test/java/io/servicecomb/config/TestConfigMapping.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.util.Map; - -import org.junit.Test; - -/** - * Created by on 2017/1/5. - */ -public class TestConfigMapping { - @Test - public void testMapping() { - String value = ConfigMapping.map("eureka.client.serviceUrl.defaultZone"); - Map m = ConfigMapping.getMapping(); - assertEquals(value, "registry.client.serviceUrl.defaultZone"); - assertNotNull(m); - } - - @Test - public void testConvertedMap() { - String value = ConfigMapping.map("eureka.client.serviceUrl.defaultZone"); - Map m = ConfigMapping.getMapping(); - Map m1 = ConfigMapping.getConvertedMap(m); - assertEquals(value, "registry.client.serviceUrl.defaultZone"); - assertNotNull(m1); - } -} diff --git a/foundations/foundation-config/src/test/java/io/servicecomb/config/TestConfigUtil.java b/foundations/foundation-config/src/test/java/io/servicecomb/config/TestConfigUtil.java deleted file mode 100644 index 00f0716d499..00000000000 --- a/foundations/foundation-config/src/test/java/io/servicecomb/config/TestConfigUtil.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config; - -import static com.seanyinx.github.unit.scaffolding.Randomness.uniquify; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.assertThat; - -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.commons.configuration.Configuration; -import org.junit.AfterClass; -import org.junit.Assert; -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 io.servicecomb.config.archaius.sources.ConfigModel; -import io.servicecomb.config.archaius.sources.MicroserviceConfigLoader; -import io.servicecomb.config.spi.ConfigCenterConfigurationSource; -import io.servicecomb.foundation.common.utils.SPIServiceUtils; -import mockit.Deencapsulation; -import mockit.Expectations; -import mockit.Mocked; - -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() { - ArchaiusUtils.resetConfig(); - - System.setProperty(systemPropertyName, systemExpected); - try { - setEnv(environmentPropertyName, environmentExpected); - } catch (Exception e) { - throw new IllegalStateException(e); - } - - ConfigUtil.installDynamicConfig(); - } - - @AfterClass - public static void tearDown() throws Exception { - ArchaiusUtils.resetConfig(); - } - - @Test - public void testCreateConfigFromConfigCenterNoUrl(@Mocked Configuration localConfiguration) { - AbstractConfiguration configFromConfigCenter = ConfigUtil.createConfigFromConfigCenter(localConfiguration); - Assert.assertNull(configFromConfigCenter); - } - - @Test - public void testCreateDynamicConfigNoConfigCenterSPI() { - new Expectations(SPIServiceUtils.class) { - { - SPIServiceUtils.getTargetService(ConfigCenterConfigurationSource.class); - result = null; - } - }; - - AbstractConfiguration dynamicConfig = ConfigUtil.createDynamicConfig(); - MicroserviceConfigLoader loader = ConfigUtil.getMicroserviceConfigLoader(dynamicConfig); - List list = loader.getConfigModels(); - Assert.assertEquals(loader, ConfigUtil.getMicroserviceConfigLoader(dynamicConfig)); - Assert.assertEquals(1, list.size()); - Assert.assertNotEquals(DynamicWatchedConfiguration.class, - ((ConcurrentCompositeConfiguration) dynamicConfig).getConfiguration(0).getClass()); - } - - @Test - public void testCreateDynamicConfigHasConfigCenter( - @Mocked ConfigCenterConfigurationSource configCenterConfigurationSource) { - AbstractConfiguration dynamicConfig = ConfigUtil.createDynamicConfig(); - Assert.assertEquals(DynamicWatchedConfiguration.class, - ((ConcurrentCompositeConfiguration) dynamicConfig).getConfiguration(0).getClass()); - } - - @Test - public void testGetPropertyInvalidConfig() { - Assert.assertNull(ConfigUtil.getProperty(null, "any")); - Assert.assertNull(ConfigUtil.getProperty(new Object(), "any")); - } - - @Test - public void propertiesFromFileIsDuplicatedToCse() throws Exception { - String expected = "value"; - - assertThat(DynamicPropertyFactory - .getInstance() - .getStringProperty("servicecomb.cse.servicecomb.file", null) - .get(), - equalTo(expected)); - - assertThat(DynamicPropertyFactory - .getInstance() - .getStringProperty("cse.cse.servicecomb.file", null) - .get(), - equalTo(expected)); - } - - @Test - public void propertiesFromSystemIsDuplicatedToCse() throws Exception { - assertThat(DynamicPropertyFactory - .getInstance() - .getStringProperty(systemPropertyName, null) - .get(), - equalTo(systemExpected)); - - assertThat(DynamicPropertyFactory - .getInstance() - .getStringProperty("servicecomb.cse.servicecomb.system.setting", null) - .get(), - equalTo(systemExpected)); - } - - @Test - public void propertiesFromEnvironmentIsDuplicatedToCse() throws Exception { - assertThat(DynamicPropertyFactory - .getInstance() - .getStringProperty(environmentPropertyName, null) - .get(), - equalTo(environmentExpected)); - - assertThat(DynamicPropertyFactory - .getInstance() - .getStringProperty("servicecomb.cse.servicecomb.environment.setting", null) - .get(), - equalTo(environmentExpected)); - } - - @Test - public void duplicateServiceCombConfigToCseListValue() throws Exception { - List list = Arrays.asList("a", "b"); - - AbstractConfiguration config = new DynamicConfiguration(); - config.addProperty("servicecomb.list", list); - Deencapsulation.invoke(ConfigUtil.class, "duplicateServiceCombConfigToCse", config); - - Object result = config.getProperty("cse.list"); - assertThat(result, instanceOf(List.class)); - assertThat(result, equalTo(list)); - } - - @Test - public void propertiesAddFromDynamicConfigSourceIsDuplicated() throws Exception { - String expected = uniquify("ran"); - String someProperty = "servicecomb.cse.servicecomb.add"; - String injectProperty = "cse.cse.servicecomb.add"; - - configurationSource.addProperty(someProperty, expected); - - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(expected)); - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(injectProperty, null).get(), - equalTo(expected)); - - String changed = uniquify("changed"); - configurationSource.addProperty(someProperty, changed); - - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(changed)); - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(injectProperty, null).get(), - equalTo(changed)); - - expected = uniquify("ran"); - someProperty = "servicecomb.servicecomb.cse.add"; - injectProperty = "cse.servicecomb.cse.add"; - - configurationSource.addProperty(someProperty, expected); - - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(expected)); - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(injectProperty, null).get(), - equalTo(expected)); - - changed = uniquify("changed"); - configurationSource.addProperty(someProperty, changed); - - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(changed)); - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(injectProperty, null).get(), - equalTo(changed)); - } - - @Test - public void propertiesChangeFromDynamicConfigSourceIsDuplicated() throws Exception { - String expected = uniquify("ran"); - String someProperty = "servicecomb.cse.servicecomb.change"; - String injectProperty = "cse.cse.servicecomb.change"; - configurationSource.addProperty(someProperty, expected); - - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(expected)); - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(injectProperty, null).get(), - equalTo(expected)); - - String changed = uniquify("changed"); - configurationSource.setProperty(someProperty, changed); - - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(changed)); - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(injectProperty, null).get(), - equalTo(changed)); - - expected = uniquify("ran"); - someProperty = "servicecomb.servicecomb.cse.change"; - injectProperty = "cse.servicecomb.cse.change"; - configurationSource.addProperty(someProperty, expected); - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(expected)); - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(injectProperty, null).get(), - equalTo(expected)); - - changed = uniquify("changed"); - configurationSource.setProperty(someProperty, changed); - - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(changed)); - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(injectProperty, null).get(), - equalTo(changed)); - } - - @Test - public void propertiesDeleteFromDynamicConfigSourceIsDuplicated() throws Exception { - String expected = uniquify("ran"); - String someProperty = "servicecomb.cse.servicecomb.delete"; - String injectProperty = "cse.cse.servicecomb.delete"; - configurationSource.addProperty(someProperty, expected); - - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(expected)); - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(injectProperty, null).get(), - equalTo(expected)); - - configurationSource.deleteProperty(someProperty); - - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(null)); - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(injectProperty, null).get(), - equalTo(null)); - - expected = uniquify("ran"); - someProperty = "servicecomb.servicecomb.cse.delete"; - injectProperty = "cse.servicecomb.cse.delete"; - configurationSource.addProperty(someProperty, expected); - - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(expected)); - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(injectProperty, null).get(), - equalTo(expected)); - - configurationSource.deleteProperty(someProperty); - - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(someProperty, null).get(), - equalTo(null)); - assertThat(DynamicPropertyFactory.getInstance().getStringProperty(injectProperty, null).get(), - equalTo(null)); - } - - @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); - } - } - } -} diff --git a/foundations/foundation-config/src/test/java/io/servicecomb/config/TestConfigurationSpringInitializer.java b/foundations/foundation-config/src/test/java/io/servicecomb/config/TestConfigurationSpringInitializer.java deleted file mode 100644 index 31790ca1cd0..00000000000 --- a/foundations/foundation-config/src/test/java/io/servicecomb/config/TestConfigurationSpringInitializer.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.config; - -import java.util.List; -import java.util.Map; - -import org.apache.commons.configuration.Configuration; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.springframework.core.Ordered; - -import com.netflix.config.ConfigurationManager; - -import io.servicecomb.config.archaius.sources.MicroserviceConfigLoader; -import mockit.Deencapsulation; - -public class TestConfigurationSpringInitializer { - @BeforeClass - public static void classSetup() { - ArchaiusUtils.resetConfig(); - } - - @AfterClass - public static void classTeardown() { - ArchaiusUtils.resetConfig(); - } - - @Test - public void testAll() { - ConfigurationSpringInitializer configurationSpringInitializer = new ConfigurationSpringInitializer(); - - Assert.assertEquals(Ordered.LOWEST_PRECEDENCE / 2, configurationSpringInitializer.getOrder()); - Assert.assertEquals(true, - Deencapsulation.getField(configurationSpringInitializer, "ignoreUnresolvablePlaceholders")); - - Object o = ConfigUtil.getProperty("zq"); - @SuppressWarnings("unchecked") - List> listO = (List>) o; - Assert.assertEquals(3, listO.size()); - Assert.assertEquals(null, ConfigUtil.getProperty("notExist")); - - MicroserviceConfigLoader loader = ConfigUtil.getMicroserviceConfigLoader(); - Assert.assertNotNull(loader); - - Configuration instance = ConfigurationManager.getConfigInstance(); - ConfigUtil.installDynamicConfig(); - // must not reinstall - Assert.assertEquals(instance, ConfigurationManager.getConfigInstance()); - } -} diff --git a/foundations/foundation-config/src/test/java/io/servicecomb/config/TestLastPropertyPlaceholderConfigurer.java b/foundations/foundation-config/src/test/java/io/servicecomb/config/TestLastPropertyPlaceholderConfigurer.java deleted file mode 100644 index 087854ac915..00000000000 --- a/foundations/foundation-config/src/test/java/io/servicecomb/config/TestLastPropertyPlaceholderConfigurer.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config; - -import java.io.IOException; -import java.util.Properties; - -import org.junit.Assert; -import org.junit.Test; -import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; -import org.springframework.context.EmbeddedValueResolverAware; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.stereotype.Component; -import org.springframework.util.StringValueResolver; - -public class TestLastPropertyPlaceholderConfigurer { - @Component - static class Bean extends PropertyPlaceholderConfigurer 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); - - Assert.assertEquals("aValue", bean.resolver.resolveStringValue("${a}")); - try { - bean.resolver.resolveStringValue("${b}"); - Assert.fail("must throw exception"); - } catch (IllegalArgumentException e) { - Assert.assertEquals("Could not resolve placeholder 'b' in string value \"${b}\"", e.getMessage()); - } - - context.close(); - } -} diff --git a/foundations/foundation-config/src/test/java/io/servicecomb/config/archaius/sources/TestMicroserviceConfigLoader.java b/foundations/foundation-config/src/test/java/io/servicecomb/config/archaius/sources/TestMicroserviceConfigLoader.java deleted file mode 100644 index 42f8050f4c4..00000000000 --- a/foundations/foundation-config/src/test/java/io/servicecomb/config/archaius/sources/TestMicroserviceConfigLoader.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config.archaius.sources; - -import static org.junit.Assert.assertEquals; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; -import java.util.List; -import java.util.stream.Collectors; - -import org.junit.Assert; -import org.junit.Test; - -public class TestMicroserviceConfigLoader { - - private final MicroserviceConfigLoader loader = new MicroserviceConfigLoader(); - - private ConfigModel createConfigModel(String protocol, int order, String file) throws MalformedURLException { - ConfigModel configModel = new ConfigModel(); - configModel.setUrl(new URL(protocol, null, file)); - configModel.setOrder(order); - return configModel; - } - - @Test - public void configsSortedByInsertionOrder() throws MalformedURLException { - loader.getConfigModels().add(createConfigModel("jar", 0, "c")); - loader.getConfigModels().add(createConfigModel("jar", 0, "b")); - loader.getConfigModels().add(createConfigModel("jar", 0, "a")); - - loader.sort(); - - assertEquals(urls("jar:c", "jar:b", "jar:a"), urlsOf(loader.getConfigModels())); - } - - @Test - public void configsSortedBySpecifiedOrder() throws MalformedURLException { - loader.getConfigModels().add(createConfigModel("jar", 2, "a")); - loader.getConfigModels().add(createConfigModel("jar", 1, "b")); - loader.getConfigModels().add(createConfigModel("jar", 0, "c")); - - loader.sort(); - - assertEquals(urls("jar:c", "jar:b", "jar:a"), urlsOf(loader.getConfigModels())); - } - - @Test - public void jarsAlwaysHaveHigherPriorityThanFiles() throws MalformedURLException { - loader.getConfigModels().add(createConfigModel("file", 0, "f2")); - loader.getConfigModels().add(createConfigModel("jar", 1, "j1")); - loader.getConfigModels().add(createConfigModel("file", 0, "f1")); - - loader.sort(); - - assertEquals(urls("jar:j1", "file:f2", "file:f1"), urlsOf(loader.getConfigModels())); - } - - private String urlsOf(List configModels) { - return String.join(",", - configModels - .stream() - .map(configModel -> configModel.getUrl().toString()) - .collect(Collectors.toList())); - } - - private String urls(String... urls) { - return String.join(",", (CharSequence[]) urls); - } - - @Test - public void testLoadEmptyYaml() throws IOException { - loader.load("empty.yaml"); - Assert.assertTrue(loader.getConfigModels().get(0).getConfig().isEmpty()); - } - - @Test - public void testLoadNotExistYaml() throws IOException { - URL url = URI.create("file:/notExist.yaml").toURL(); - try { - loader.load(url); - Assert.fail("must throw exception"); - } catch (FileNotFoundException e) { - Assert.assertTrue(true); - } - } -} diff --git a/foundations/foundation-config/src/test/java/io/servicecomb/config/archaius/sources/TestYAMLConfigurationSource.java b/foundations/foundation-config/src/test/java/io/servicecomb/config/archaius/sources/TestYAMLConfigurationSource.java deleted file mode 100644 index 80a2aba7aa2..00000000000 --- a/foundations/foundation-config/src/test/java/io/servicecomb/config/archaius/sources/TestYAMLConfigurationSource.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.config.archaius.sources; - -import static io.servicecomb.config.archaius.sources.ConfigSourceMaker.yamlConfigSource; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import java.net.URL; -import java.util.List; -import java.util.Map; - -import org.apache.commons.configuration.SystemConfiguration; -import org.junit.Assert; -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 io.servicecomb.config.archaius.scheduler.NeverStartPollingScheduler; - -/** - * Created by on 2017/1/5. - */ -public class TestYAMLConfigurationSource { - - @Test - public void testPullFromClassPath() throws Exception { - MicroserviceConfigurationSource configSource = yamlConfigSource(); - PollResult result = configSource.poll(true, null); - Map configMap = result.getComplete(); - assertNotNull(configMap); - assertEquals(20, configMap.size()); - assertNotNull(configMap.get("trace.handler.sampler.percent")); - assertEquals(0.5, configMap.get("trace.handler.sampler.percent")); - assertEquals("http://10.120.169.202:9980/", configMap.get("registry.client.serviceUrl.defaultZone")); - assertNull(configMap.get("eureka.client.serviceUrl.defaultZone")); - } - - @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("cse.configurationSource.additionalUrls", test1URL.toString() + "," + test2URL.toString()); - MicroserviceConfigurationSource configSource = yamlConfigSource(); - PollResult result = configSource.poll(true, null); - Map configMap = result.getComplete(); - - assertEquals(3, configSource.getConfigModels().size()); - assertNotNull(configMap); - assertEquals(31, configMap.size()); - assertNotNull(configMap.get("trace.handler.sampler.percent")); - assertEquals(0.5, configMap.get("trace.handler.sampler.percent")); - - System.getProperties().remove("cse.configurationSource.additionalUrls"); - } - - @Test - public void testFullOperation() { - // configuration from system properties - ConcurrentMapConfiguration configFromSystemProperties = - new ConcurrentMapConfiguration(new SystemConfiguration()); - // configuration from yaml file - DynamicConfiguration configFromYamlFile = - new DynamicConfiguration(yamlConfigSource(), 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"); - Assert.assertEquals(0.5, finalConfig.getDouble("trace.handler.sampler.percent"), 0.5); - // DynamicStringListProperty property = new DynamicStringListProperty("trace.handler.tlist", "|", DynamicStringListProperty.DEFAULT_DELIMITER); - // List ll = property.get(); - // for (String s : ll) { - // System.out.println(s); - // } - - Object o = finalConfig.getProperty("zq"); - @SuppressWarnings("unchecked") - List> listO = (List>) o; - Assert.assertEquals(3, listO.size()); - } -} 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 new file mode 100644 index 00000000000..38a8a4a8b5d --- /dev/null +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/BootStrapPropertiesTest.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.config; + +import static org.apache.servicecomb.foundation.test.scaffolding.AssertUtils.assertPrettyJson; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Map; +import java.util.Map.Entry; + +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; + +public class BootStrapPropertiesTest { + private Map readInstanceProperties(String yaml) { + Map properties = YAMLUtil.yaml2Properties(yaml); + 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); + + for (Entry entry : properties.entrySet()) { + Mockito.when(environment.getProperty(entry.getKey())).thenReturn(entry.getValue().toString()); + } + + return BootStrapProperties.readServiceInstanceProperties(environment); + } + + @Test + void should_be_empty_when_old_and_new_key_not_exists() { + Map properties = readInstanceProperties("k: v"); + + assertThat(properties).isEmpty(); + } + + @Test + void should_read_boolean_to_string() { + Map properties = readInstanceProperties(""" + servicecomb: + instance: + properties: + k: true"""); + + assertPrettyJson(properties).isEqualTo(""" + { + "k" : "true" + }"""); + } + + @Test + void should_read_number_to_string() { + 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: + 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: + 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 new file mode 100644 index 00000000000..3f72af9b2cb --- /dev/null +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/DynamicPropertiesTest.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.nextBoolean; +import static com.seanyinx.github.unit.scaffolding.Randomness.uniquify; +import static org.hamcrest.MatcherAssert.assertThat; +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.seanyinx.github.unit.scaffolding.Poller; +import com.seanyinx.github.unit.scaffolding.Randomness; + +public class DynamicPropertiesTest { + private static final String stringPropertyName = uniquify("stringPropertyName"); + + private static final String intPropertyName = uniquify("intPropertyName"); + + private static final String longPropertyName = uniquify("longPropertyName"); + + private static final String floatPropertyName = uniquify("floatPropertyName"); + + private static final String doublePropertyName = uniquify("doublePropertyName"); + + private static final String booleanPropertyName = uniquify("booleanPropertyName"); + + private static final String stringOldValue = uniquify("stringPropertyValue"); + + private static final int intOldValue = Randomness.nextInt(); + + private static final long longOldValue = Randomness.nextLong(); + + private static final float floatOldValue = Double.valueOf(Randomness.nextDouble()).floatValue(); + + private static final double doubleOldValue = Randomness.nextDouble(); + + private static final boolean booleanOldValue = nextBoolean(); + + private static final double ERROR = 0.0000001; + + private static Environment environment; + + private static DynamicProperties dynamicProperties; + + private final Poller poller = new Poller(2000, 100); + + private volatile String stringPropertyValue; + + private volatile int intPropertyValue; + + private volatile long longPropertyValue; + + private volatile double floatPropertyValue; + + private volatile double doublePropertyValue; + + private volatile boolean booleanPropertyValue; + + + @BeforeClass + public static void setUpClass() throws Exception { + environment = Mockito.mock(Environment.class); + writeInitialConfig(); + dynamicProperties = new DynamicPropertiesImpl(environment); + } + + @AfterClass + public static void tearDown() throws Exception { + + } + + private static void writeInitialConfig() throws Exception { + 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 + public void observesSpecifiedStringProperty() throws Exception { + String property = dynamicProperties.getStringProperty(stringPropertyName, null); + assertThat(property, is(stringOldValue)); + + property = dynamicProperties.getStringProperty(stringPropertyName, value -> stringPropertyValue = value, null); + assertThat(property, is(stringOldValue)); + + String newValue = uniquify("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)); + } + + @Test + public void observesSpecifiedIntProperty() throws Exception { + int property = dynamicProperties.getIntProperty(intPropertyName, 0); + assertThat(property, is(intOldValue)); + + property = dynamicProperties.getIntProperty(intPropertyName, value -> intPropertyValue = value, 0); + assertThat(property, is(intOldValue)); + + int newValue = Randomness.nextInt(); + + 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, 3L); + assertThat(property, is(longOldValue)); + + long newValue = Randomness.nextLong(); + + Mockito.when(environment.getProperty(longPropertyName, Long.class, 3L)).thenReturn(newValue); + HashMap updated = new HashMap<>(); + updated.put(longPropertyName, newValue); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); + + 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 + public void observesSpecifiedFloatProperty() throws Exception { + double property = dynamicProperties.getFloatProperty(floatPropertyName, 0); + assertThat(property, closeTo(floatOldValue, ERROR)); + + property = dynamicProperties.getFloatProperty(floatPropertyName, value -> floatPropertyValue = value, 0f); + assertThat(property, closeTo(floatOldValue, ERROR)); + + float newValue = Double.valueOf(Randomness.nextDouble()).floatValue(); + + 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); + } + + @Test + public void observesSpecifiedDoubleProperty() throws Exception { + double property = dynamicProperties.getDoubleProperty(doublePropertyName, 0); + assertThat(property, closeTo(doubleOldValue, ERROR)); + + property = dynamicProperties.getDoubleProperty(doublePropertyName, value -> doublePropertyValue = value, 0d); + assertThat(property, closeTo(doubleOldValue, ERROR)); + + double newValue = Randomness.nextDouble(); + + 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); + } + + @Test + public void observesSpecifiedBooleanProperty() throws Exception { + boolean property = dynamicProperties.getBooleanProperty(booleanPropertyName, false); + assertThat(property, is(booleanOldValue)); + + property = dynamicProperties.getBooleanProperty( + booleanPropertyName, + value -> booleanPropertyValue = value, + false); + assertThat(property, is(booleanOldValue)); + + boolean newValue = !booleanOldValue; + + 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/TestConfigMapping.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigMapping.java new file mode 100644 index 00000000000..1da5a2f5938 --- /dev/null +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestConfigMapping.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.config; + +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestConfigMapping { + @Test + public void testMapping() { + List value = ConfigMapping.map("SERVICECOMB_ENV"); + Assertions.assertEquals(2, value.size()); + Assertions.assertEquals("service_description.environment", value.get(0)); + Assertions.assertEquals("service_description.environment.old", value.get(1)); + + Map m = ConfigMapping.getMapping(); + Assertions.assertNotNull(m); + } + + @Test + public void testConvertedMap() { + String value = ConfigMapping.map("CSE_ENV_MAPPING"); + Map m = ConfigMapping.getMapping(); + Map m1 = ConfigMapping.getConvertedMap(m); + Assertions.assertEquals("servicecomb.testmapping.key", value); + Assertions.assertNotNull(m1); + } +} 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 new file mode 100644 index 00000000000..4abafe8765e --- /dev/null +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config; + +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestYAMLUtil { + public static class Person { + String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + public static class UnsafePerson { + String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + @Test + public void testSafeParser() { + Person person = YAMLUtil.parserObject("name: hello", Person.class); + Assertions.assertEquals("hello", person.getName()); + } + + @Test + public void testYamlConfig() { + RuntimeException runtimeException = Assertions.assertThrows(RuntimeException.class, () -> YAMLUtil.yaml2Properties("servicecomb.service.registry.enabled: {{true}}")); + Assertions.assertEquals("input cannot be convert to map", runtimeException.getMessage()); + } + + @Test + @SuppressWarnings("unchecked") + public void testListValue() { + Map result = YAMLUtil.yaml2Properties("hello: a,b"); + Assertions.assertEquals(result.size(), 1); + String listValue = (String) result.get("hello"); + Assertions.assertEquals(listValue, "a,b"); + } +} diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/file/TestMicroserviceConfigLoader.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/file/TestMicroserviceConfigLoader.java new file mode 100644 index 00000000000..0084976d004 --- /dev/null +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/file/TestMicroserviceConfigLoader.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.file; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestMicroserviceConfigLoader { + + private final MicroserviceConfigLoader loader = new MicroserviceConfigLoader(); + + private ConfigModel createConfigModel(String protocol, int order, String file) throws MalformedURLException { + ConfigModel configModel = new ConfigModel(); + configModel.setUrl(new URL(protocol, null, file)); + configModel.setOrder(order); + return configModel; + } + + @Test + public void configsSortedByInsertionOrder() throws MalformedURLException { + loader.getConfigModels().add(createConfigModel("jar", 0, "c")); + loader.getConfigModels().add(createConfigModel("jar", 0, "b")); + loader.getConfigModels().add(createConfigModel("jar", 0, "a")); + + loader.sort(); + + Assertions.assertEquals(urls("jar:c", "jar:b", "jar:a"), urlsOf(loader.getConfigModels())); + } + + @Test + public void configsSortedBySpecifiedOrder() throws MalformedURLException { + loader.getConfigModels().add(createConfigModel("jar", 1, "b")); + loader.getConfigModels().add(createConfigModel("jar", -10, "c")); + loader.getConfigModels().add(createConfigModel("jar", Integer.MAX_VALUE, "a")); + + loader.sort(); + + Assertions.assertEquals(urls("jar:c", "jar:b", "jar:a"), urlsOf(loader.getConfigModels())); + } + + @Test + public void jarsAlwaysHaveHigherPriorityThanFiles() throws MalformedURLException { + loader.getConfigModels().add(createConfigModel("file", 0, "f2")); + loader.getConfigModels().add(createConfigModel("jar", 1, "j1")); + loader.getConfigModels().add(createConfigModel("file", 0, "f1")); + + loader.sort(); + + Assertions.assertEquals(urls("jar:j1", "file:f2", "file:f1"), urlsOf(loader.getConfigModels())); + } + + private String urlsOf(List configModels) { + return String.join(",", + configModels + .stream() + .map(configModel -> configModel.getUrl().toString()) + .collect(Collectors.toList())); + } + + private String urls(String... urls) { + return String.join(",", (CharSequence[]) urls); + } + + @Test + public void testLoadEmptyYaml() throws IOException { + loader.load("empty.yaml"); + Assertions.assertTrue(loader.getConfigModels().get(0).getConfig().isEmpty()); + } + + @Test + public void testLoadNotExistYaml() throws IOException { + URL url = URI.create("file:/notExist.yaml").toURL(); + try { + loader.load(url); + Assertions.fail("must throw exception"); + } catch (FileNotFoundException e) { + Assertions.assertTrue(true); + } + } +} 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 new file mode 100644 index 00000000000..9e5915b0c20 --- /dev/null +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestConfigObjectFactory.java @@ -0,0 +1,532 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.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 { + public String strValue; + + private String strValue1; + + public int intValue; + + private int intValue1; + + public Integer intValueObj; + + private Integer intValueObj1; + + public long longValue; + + private long longValue1; + + public Long longValueObj; + + private Long longValueObj1; + + public float floatValue; + + private float floatValue1; + + public Float floatValueObj; + + private Float floatValueObj1; + + public double doubleValue; + + private double doubleValue1; + + public Double doubleValueObj; + + private Double doubleValueObj1; + + public boolean booleanValue; + + private boolean booleanValue1; + + public Boolean booleanValueObj; + + private Boolean booleanValueObj1; + + public String getStrValue1() { + return strValue1; + } + + public void setStrValue1(String strValue1) { + this.strValue1 = strValue1; + } + + public int getIntValue1() { + return intValue1; + } + + public void setIntValue1(int intValue1) { + this.intValue1 = intValue1; + } + + public Integer getIntValueObj1() { + return intValueObj1; + } + + public void setIntValueObj1(Integer intValueObj1) { + this.intValueObj1 = intValueObj1; + } + + public long getLongValue1() { + return longValue1; + } + + public void setLongValue1(long longValue1) { + this.longValue1 = longValue1; + } + + public Long getLongValueObj1() { + return longValueObj1; + } + + public void setLongValueObj1(Long longValueObj1) { + this.longValueObj1 = longValueObj1; + } + + public float getFloatValue1() { + return floatValue1; + } + + public void setFloatValue1(float floatValue1) { + this.floatValue1 = floatValue1; + } + + public Float getFloatValueObj1() { + return floatValueObj1; + } + + public void setFloatValueObj1(Float floatValueObj1) { + this.floatValueObj1 = floatValueObj1; + } + + public double getDoubleValue1() { + return doubleValue1; + } + + public void setDoubleValue1(double doubleValue1) { + this.doubleValue1 = doubleValue1; + } + + public Double getDoubleValueObj1() { + return doubleValueObj1; + } + + public void setDoubleValueObj1(Double doubleValueObj1) { + this.doubleValueObj1 = doubleValueObj1; + } + + public boolean isBooleanValue1() { + return booleanValue1; + } + + public void setBooleanValue1(boolean booleanValue1) { + this.booleanValue1 = booleanValue1; + } + + public Boolean getBooleanValueObj1() { + return booleanValueObj1; + } + + public void setBooleanValueObj1(Boolean booleanValueObj1) { + this.booleanValueObj1 = booleanValueObj1; + } + } + + @Test + public void noAnnotation_defaultValue() { + ConfigNoAnnotation config = priorityPropertyManager.createConfigObject(ConfigNoAnnotation.class); + + Assertions.assertNull(config.strValue); + Assertions.assertNull(config.getStrValue1()); + + Assertions.assertEquals(0, config.intValue); + Assertions.assertEquals(0, config.getIntValue1()); + Assertions.assertNull(config.intValueObj); + Assertions.assertNull(config.getIntValueObj1()); + + Assertions.assertEquals(0, config.longValue); + Assertions.assertEquals(0, config.getLongValue1()); + Assertions.assertNull(config.longValueObj); + Assertions.assertNull(config.getLongValueObj1()); + + Assertions.assertEquals(0, config.floatValue, 0); + Assertions.assertEquals(0, config.getFloatValue1(), 0); + Assertions.assertNull(config.floatValueObj); + Assertions.assertNull(config.getFloatValueObj1()); + + Assertions.assertEquals(0, config.doubleValue, 0); + Assertions.assertEquals(0, config.getDoubleValue1(), 0); + Assertions.assertNull(config.doubleValueObj); + Assertions.assertNull(config.getDoubleValueObj1()); + + Assertions.assertFalse(config.booleanValue); + Assertions.assertFalse(config.isBooleanValue1()); + Assertions.assertNull(config.booleanValueObj); + Assertions.assertNull(config.getBooleanValueObj1()); + } + + @Test + public void noAnnotation_initValue() { + 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); + + Assertions.assertEquals("strValue", config.strValue); + Assertions.assertEquals("strValue1", config.getStrValue1()); + + Assertions.assertEquals(1, config.intValue); + Assertions.assertEquals(2, config.getIntValue1()); + Assertions.assertEquals(3, (int) config.intValueObj); + Assertions.assertEquals(4, (int) config.getIntValueObj1()); + + Assertions.assertEquals(5, config.longValue); + Assertions.assertEquals(6, config.getLongValue1()); + Assertions.assertEquals(7, (long) config.longValueObj); + Assertions.assertEquals(8, (long) config.getLongValueObj1()); + + Assertions.assertEquals(9, config.floatValue, 0); + Assertions.assertEquals(10, config.getFloatValue1(), 0); + Assertions.assertEquals(11, config.floatValueObj, 0); + Assertions.assertEquals(12, config.getFloatValueObj1(), 0); + + Assertions.assertEquals(13, config.doubleValue, 0); + Assertions.assertEquals(14, config.getDoubleValue1(), 0); + Assertions.assertEquals(15, config.doubleValueObj, 0); + Assertions.assertEquals(16, config.getDoubleValueObj1(), 0); + + Assertions.assertTrue(config.booleanValue); + Assertions.assertTrue(config.isBooleanValue1()); + Assertions.assertTrue(config.booleanValueObj); + Assertions.assertTrue(config.getBooleanValueObj1()); + } + + @Test + public void noAnnotation_updateValue() { + ConfigNoAnnotation config = priorityPropertyManager.createConfigObject(ConfigNoAnnotation.class); + + 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()); + + Assertions.assertEquals(1, config.intValue); + Assertions.assertEquals(2, config.getIntValue1()); + Assertions.assertEquals(3, (int) config.intValueObj); + Assertions.assertEquals(4, (int) config.getIntValueObj1()); + + Assertions.assertEquals(5, config.longValue); + Assertions.assertEquals(6, config.getLongValue1()); + Assertions.assertEquals(7, (long) config.longValueObj); + Assertions.assertEquals(8, (long) config.getLongValueObj1()); + + Assertions.assertEquals(9, config.floatValue, 0); + Assertions.assertEquals(10, config.getFloatValue1(), 0); + Assertions.assertEquals(11, config.floatValueObj, 0); + Assertions.assertEquals(12, config.getFloatValueObj1(), 0); + + Assertions.assertEquals(13, config.doubleValue, 0); + Assertions.assertEquals(14, config.getDoubleValue1(), 0); + Assertions.assertEquals(15, config.doubleValueObj, 0); + Assertions.assertEquals(16, config.getDoubleValueObj1(), 0); + + Assertions.assertTrue(config.booleanValue); + Assertions.assertTrue(config.isBooleanValue1()); + Assertions.assertTrue(config.booleanValueObj); + Assertions.assertTrue(config.getBooleanValueObj1()); + } + + @InjectProperties(prefix = "root") + public static class ConfigWithAnnotation { + @InjectProperty(prefix = "override", keys = {"high", "low"}) + public String strValue; + + @InjectProperty(keys = "${key}.value") + public int intValue; + + @InjectProperty(keys = "${low-list}.a.${high-list}.b") + public long longValue; + + @InjectProperty(keys = "${full-list}") + public float floatValue; + + @InjectProperty(defaultValue = "abc") + public String strDef; + + @InjectProperty(defaultValue = "1") + public int intDef; + + @InjectProperty(defaultValue = "2") + public long longDef; + + @InjectProperty(defaultValue = "3") + public float floatDef; + + @InjectProperty(defaultValue = "4") + public double doubleDef; + + @InjectProperty(defaultValue = "true") + public boolean booleanDef; + } + + @Test + public void annotationDefault() { + ConfigWithAnnotation config = priorityPropertyManager.createConfigObject(ConfigWithAnnotation.class); + + Assertions.assertEquals("abc", config.strDef); + Assertions.assertEquals(1, config.intDef); + Assertions.assertEquals(2, config.longDef); + Assertions.assertEquals(3, config.floatDef, 0); + Assertions.assertEquals(4, config.doubleDef, 0); + Assertions.assertTrue(config.booleanDef); + } + + @Test + public void placeholder_multi_list() { + ConfigWithAnnotation config = priorityPropertyManager.createConfigObject(ConfigWithAnnotation.class, + "low-list", Arrays.asList("low-1", "low-2"), + "high-list", Arrays.asList("high-1", "high-2")); + // low-1.a.high-1.b + // low-1.a.high-2.b + // low-2.a.high-1.b + // low-2.a.high-2.b + + Assertions.assertEquals(0, config.longValue); + + 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); + + 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); + + 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); + + 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); + } + + @Test + public void placeholder_full_list() { + ConfigWithAnnotation config = priorityPropertyManager.createConfigObject(ConfigWithAnnotation.class, + "full-list", Arrays.asList("l1-1", "l1-2")); + + Assertions.assertEquals(0, 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)); + + Assertions.assertEquals(2F, config.floatValue, 0); + } + + @Test + public void placeholder_normal() { + ConfigWithAnnotation config = priorityPropertyManager.createConfigObject(ConfigWithAnnotation.class, "key", "k"); + + Assertions.assertEquals(0, config.intValue); + + 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); + } + + @Test + public void overridePrefix() { + ConfigWithAnnotation config = priorityPropertyManager.createConfigObject(ConfigWithAnnotation.class); + + 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); + + 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); + + 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); + + 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 new file mode 100644 index 00000000000..d6407785f65 --- /dev/null +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestPlaceholderResolver.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.inject; + +import java.util.Arrays; +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.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(); + + @BeforeAll + public static void setupClass() { + parameters.put("key", "value"); + parameters.put("varOfVar", Arrays.asList("${key}")); + + parameters.put("priority", "low"); + + parameters.put("low-list", Arrays.asList("low-1", "low-2")); + parameters.put("middle-list", Arrays.asList("middle-1", "middle-2")); + 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), + Matchers.contains("prefix${xxx}suffix")); + } + + @Test + public void empty() { + MatcherAssert.assertThat(resolver.replace("prefix${}suffix", parameters), + Matchers.contains("prefix${}suffix")); + } + + @Test + public void notComplete() { + MatcherAssert.assertThat(resolver.replace("prefix${suffix", parameters), + Matchers.contains("prefix${suffix")); + } + + @Test + public void normal() { + MatcherAssert.assertThat(resolver.replace("prefix.${key}.suffix", parameters), + Matchers.contains("prefix.value.suffix")); + } + + @Test + public void disable() { + MatcherAssert.assertThat(resolver.replace("prefix.\\${key}.suffix", parameters), + Matchers.contains("prefix.${key}.suffix")); + } + + @Test + public void varOfVar() { + MatcherAssert.assertThat(resolver.replace("prefix.${varOfVar}.suffix", parameters), + Matchers.contains("prefix.value.suffix")); + } + + @Test + public void list() { + MatcherAssert.assertThat(resolver.replace("prefix.${low-list}.suffix", parameters), + Matchers.contains("prefix.low-1.suffix", "prefix.low-2.suffix")); + } + + @Test + public void multi_list() { + MatcherAssert.assertThat(resolver.replace("prefix.${low-list}.${middle-list}.${high-list}.suffix", parameters), + Matchers.contains( + "prefix.low-1.middle-1.high-1.suffix", + "prefix.low-1.middle-1.high-2.suffix", + "prefix.low-1.middle-2.high-1.suffix", + "prefix.low-1.middle-2.high-2.suffix", + "prefix.low-2.middle-1.high-1.suffix", + "prefix.low-2.middle-1.high-2.suffix", + "prefix.low-2.middle-2.high-1.suffix", + "prefix.low-2.middle-2.high-2.suffix")); + } + + @Test + public void nested() { + MatcherAssert.assertThat(resolver.replace("prefix.${${priority}-list}.suffix", parameters), + Matchers.contains("prefix.low-1.suffix", "prefix.low-2.suffix")); + } + + @Test + public void mixed() { + 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}", + "prefix.low-1.value.high-2.suffix ${xxx}", + "prefix.low-2.value.high-2.suffix ${xxx}")); + } +} diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/parser/TestParser.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/parser/TestParser.java new file mode 100644 index 00000000000..69409347116 --- /dev/null +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/parser/TestParser.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.config.parser; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import org.junit.Test; + +public class TestParser { + @Test + public void testRawParser() { + Parser parser = Parser.findParser(Parser.CONTENT_TYPE_RAW); + assertThat(parser.parse("world", "hello", true)).containsKey("hello").containsValue("world"); + assertThat(parser.parse("world", "hello", false)).containsKey("hello").containsValue("world"); + } + + @Test + public void testPropertiesParser() { + Parser parser = Parser.findParser(Parser.CONTENT_TYPE_PROPERTIES); + assertThat(parser.parse("l1-1=3.0\n" + + "l1-2=2.0", "hello", true)).containsKeys("hello.l1-1", "hello.l1-2") + .containsValues("2.0", "3.0"); + assertThat(parser.parse("l1-1=3.0\n" + + "l1-2=2.0", "hello", false)).containsKeys("l1-1", "l1-2") + .containsValues("2.0", "3.0"); + } + + @Test + public void testYamlParser() { + Parser parser = Parser.findParser(Parser.CONTENT_TYPE_YAML); + assertThat(parser.parse("l1-1: 3.0\n" + + "l1-2: 2.0", "hello", true)).containsKeys("hello.l1-1", "hello.l1-2") + .containsValues("2.0", "3.0"); + assertThat(parser.parse("l1-1: 3.0\n" + + "l1-2: 2.0", "false", false)).containsKeys("l1-1", "l1-2") + .containsValues("2.0", "3.0"); + } + + @Test + public void testInvalidParser() { + Throwable exception = catchThrowable(() -> Parser.findParser("unknown")); + assertThat(exception).isInstanceOf(IllegalArgumentException.class); + } +} 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 new file mode 100644 index 00000000000..85b3185d251 --- /dev/null +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityProperty.java @@ -0,0 +1,229 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestPriorityProperty extends TestPriorityPropertyBase { + String high = "ms.schema.op"; + + String middle = "ms.schema"; + + String low = "ms"; + + String[] keys = {high, middle, low}; + + @Test + public void testLong() { + PriorityProperty config = propertyFactory.getOrCreate(Long.class, -1L, -2L, keys); + Assertions.assertEquals(-2L, (long) config.getValue()); + + updateLong(low, 1L, config); + Assertions.assertEquals(1L, (long) config.getValue()); + + updateLong(middle, 2L, config); + Assertions.assertEquals(2L, (long) config.getValue()); + + updateLong(high, 3L, config); + Assertions.assertEquals(3L, (long) config.getValue()); + + updateLong(middle, null, config); + Assertions.assertEquals(3L, (long) config.getValue()); + + updateLong(middle, 2L, config); + updateLong(high, null, config); + Assertions.assertEquals(2L, (long) config.getValue()); + + updateLong(middle, null, config); + Assertions.assertEquals(1L, (long) config.getValue()); + + updateLong(low, null, config); + Assertions.assertEquals(-2L, (long) config.getValue()); + } + + @Test + public void testInt() { + PriorityProperty config = propertyFactory.getOrCreate(Integer.class, -1, -2, keys); + Assertions.assertEquals(-2L, (int) config.getValue()); + + updateInt(low, 1, config); + Assertions.assertEquals(1, (int) config.getValue()); + + updateInt(middle, 2, config); + Assertions.assertEquals(2, (int) config.getValue()); + + updateInt(high, 3, config); + Assertions.assertEquals(3, (int) config.getValue()); + + updateInt(middle, null, config); + Assertions.assertEquals(3, (int) config.getValue()); + updateInt(middle, 2, config); + + updateInt(high, null, config); + Assertions.assertEquals(2, (int) config.getValue()); + + updateInt(middle, null, config); + Assertions.assertEquals(1, (int) config.getValue()); + + 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()); + + updateStr(low, "1", config); + Assertions.assertEquals("1", config.getValue()); + + updateStr(middle, "2", config); + Assertions.assertEquals("2", config.getValue()); + + updateStr(high, "3", config); + Assertions.assertEquals("3", config.getValue()); + + updateStr(middle, null, config); + Assertions.assertEquals("3", config.getValue()); + + updateStr(middle, "2", config); + updateStr(high, null, config); + Assertions.assertEquals("2", config.getValue()); + + updateStr(middle, null, config); + Assertions.assertEquals("1", config.getValue()); + + updateStr(low, null, config); + Assertions.assertEquals("def", config.getValue()); + } + + @Test + public void testBoolean() { + PriorityProperty config = propertyFactory.getOrCreate(Boolean.class, null, false, keys); + Assertions.assertFalse(config.getValue()); + + updateBoolean(low, true, config); + Assertions.assertTrue(config.getValue()); + + updateBoolean(middle, false, config); + Assertions.assertFalse(config.getValue()); + + updateBoolean(high, true, config); + Assertions.assertTrue(config.getValue()); + + updateBoolean(middle, false, config); + Assertions.assertTrue(config.getValue()); + + updateBoolean(middle, false, config); + updateBoolean(high, null, config); + Assertions.assertFalse(config.getValue()); + + updateBoolean(middle, null, config); + Assertions.assertTrue(config.getValue()); + + updateBoolean(low, null, config); + Assertions.assertFalse(config.getValue()); + } + + @Test + public void testDouble() { + PriorityProperty config = propertyFactory.getOrCreate(Double.class, null, -2.0, keys); + Assertions.assertEquals(-2, config.getValue(), 0); + + updateDouble(low, 1D, config); + Assertions.assertEquals(1, config.getValue(), 0); + + updateDouble(middle, 2D, config); + Assertions.assertEquals(2, config.getValue(), 0); + + updateDouble(high, 3D, config); + Assertions.assertEquals(3, config.getValue(), 0); + + updateDouble(middle, null, config); + Assertions.assertEquals(3, config.getValue(), 0); + + updateDouble(middle, 2D, config); + updateDouble(high, null, config); + Assertions.assertEquals(2, config.getValue(), 0); + + updateDouble(middle, null, config); + Assertions.assertEquals(1, config.getValue(), 0); + + updateDouble(low, null, config); + Assertions.assertEquals(-2, config.getValue(), 0); + } + + @Test + public void testFloat() { + PriorityProperty config = propertyFactory.getOrCreate(Float.class, null, -2.0f, keys); + Assertions.assertEquals(-2, config.getValue(), 0); + + updateFloat(low, 1F, config); + Assertions.assertEquals(1, config.getValue(), 0); + + updateFloat(middle, 2F, config); + Assertions.assertEquals(2, config.getValue(), 0); + + updateFloat(high, 3F, config); + Assertions.assertEquals(3, config.getValue(), 0); + + updateFloat(middle, null, config); + Assertions.assertEquals(3, config.getValue(), 0); + + updateFloat(middle, 2F, config); + updateFloat(high, null, config); + Assertions.assertEquals(2, config.getValue(), 0); + + updateFloat(middle, null, config); + Assertions.assertEquals(1, config.getValue(), 0); + + updateFloat(low, null, config); + Assertions.assertEquals(-2, config.getValue(), 0); + } +} 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 new file mode 100644 index 00000000000..375a755ec03 --- /dev/null +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyBase.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.config.priority; + +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() { + environment = Mockito.mock(Environment.class); + LegacyPropertyFactory.setEnvironment(environment); + + propertyFactory = new PriorityPropertyFactory(environment); + priorityPropertyManager = new PriorityPropertyManager(new ConfigObjectFactory(propertyFactory)); + } + + @AfterEach + public void teardown() { + } +} diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyFactory.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyFactory.java new file mode 100644 index 00000000000..1e33e3af684 --- /dev/null +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyFactory.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.config.priority; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class TestPriorityPropertyFactory extends TestPriorityPropertyBase { + @Test + void should_not_create_multiple_instances_for_same_parameter() { + PriorityProperty p1 = propertyFactory.getOrCreate(int.class, null, 0, "high", "low"); + PriorityProperty p2 = propertyFactory.getOrCreate(int.class, null, 0, "high", "low"); + + assertThat(p1).isSameAs(p2); + assertThat(propertyFactory.getProperties().count()).isEqualTo(1); + } + + @Test + void should_create_different_instances_for_different_parameter() { + PriorityProperty p1 = propertyFactory.getOrCreate(int.class, null, 0, "high", "low"); + PriorityProperty p2 = propertyFactory.getOrCreate(long.class, null, 0L, "high", "low"); + + assertThat(p1).isNotSameAs(p2); + assertThat(propertyFactory.getProperties().count()).isEqualTo(2); + } +} 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 new file mode 100644 index 00000000000..291d8614562 --- /dev/null +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/priority/TestPriorityPropertyManager.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.apache.servicecomb.config.inject.InjectProperties; +import org.apache.servicecomb.config.inject.InjectProperty; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestPriorityPropertyManager extends TestPriorityPropertyBase { + String high = "ms.schema.op"; + + String middle = "ms.schema"; + + String low = "ms"; + + String[] keys = {high, middle, low}; + + @InjectProperties(prefix = "root") + public static class ConfigWithAnnotation { + @InjectProperty(prefix = "override", keys = {"high", "low"}, defaultValue = "abc") + public String strValue; + } + + private void waitKeyForGC(PriorityPropertyManager priorityPropertyManager) { + long maxTime = 10000; + long currentTime = System.currentTimeMillis(); + while (System.currentTimeMillis() - currentTime < maxTime) { + if (priorityPropertyManager.getConfigObjectMap().isEmpty()) { + break; + } + System.runFinalization(); + System.gc(); + try { + Thread.yield(); + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + @Test + public void testConfigurationsAreGCCollected() { + long timeBegin = System.currentTimeMillis(); + + for (int i = 0; i < 100; i++) { + for (int j = 0; j < 100; j++) { + 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()); + } + } + + waitKeyForGC(priorityPropertyManager); + + Assertions.assertTrue(priorityPropertyManager.getConfigObjectMap().isEmpty()); + System.out.println("Token : " + (System.currentTimeMillis() - timeBegin)); + } +} diff --git a/foundations/foundation-config/src/test/resources/META-INF/services/io.servicecomb.config.spi.ConfigCenterConfigurationSource b/foundations/foundation-config/src/test/resources/META-INF/services/io.servicecomb.config.spi.ConfigCenterConfigurationSource deleted file mode 100644 index 6a86a79f6bb..00000000000 --- a/foundations/foundation-config/src/test/resources/META-INF/services/io.servicecomb.config.spi.ConfigCenterConfigurationSource +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -io.servicecomb.config.MapBasedConfigurationSource \ No newline at end of file diff --git a/foundations/foundation-config/src/test/resources/empty.yaml b/foundations/foundation-config/src/test/resources/empty.yaml index e69de29bb2d..fa7a54b8349 100644 --- a/foundations/foundation-config/src/test/resources/empty.yaml +++ b/foundations/foundation-config/src/test/resources/empty.yaml @@ -0,0 +1,16 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- diff --git a/foundations/foundation-config/src/test/resources/log4j.properties b/foundations/foundation-config/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/foundations/foundation-config/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/foundations/foundation-config/src/test/resources/m1.yaml b/foundations/foundation-config/src/test/resources/m1.yaml index 86ae8c5bbe2..933c0b551ef 100644 --- a/foundations/foundation-config/src/test/resources/m1.yaml +++ b/foundations/foundation-config/src/test/resources/m1.yaml @@ -1,2 +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. +## --------------------------------------------------------------------------- + zq: - test: okok \ No newline at end of file + test: okok diff --git a/foundations/foundation-config/src/test/resources/mapping.yaml b/foundations/foundation-config/src/test/resources/mapping.yaml new file mode 100644 index 00000000000..66d6b0d08e1 --- /dev/null +++ b/foundations/foundation-config/src/test/resources/mapping.yaml @@ -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-config-order: 0 + +CSE_ENV_MAPPING: servicecomb.testmapping.key +MY_SERVICES_ENDPOINT: + - servicecomb.service.mapping.address + - servicecomb.service1.mapping.address +SERVICECOMB_ENV: + - service_description.environment + - 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 f456bbd549c..43a9078fba0 100644 --- a/foundations/foundation-config/src/test/resources/microservice.yaml +++ b/foundations/foundation-config/src/test/resources/microservice.yaml @@ -1,3 +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. +## --------------------------------------------------------------------------- + trace: handler: enabled: false @@ -43,9 +60,10 @@ cse: service_description: name: testDemo - -servicecomb: - cse: - servicecomb: - file: value - \ No newline at end of file +holder: b,c,d +test: + commonSeparatedString: a,b,c + commonSeparatedStringHolder: ${holder} + stringArray: + - m + - n diff --git a/foundations/foundation-config/src/test/resources/test1.yaml b/foundations/foundation-config/src/test/resources/test1.yaml index c9609ca6182..e171487901f 100644 --- a/foundations/foundation-config/src/test/resources/test1.yaml +++ b/foundations/foundation-config/src/test/resources/test1.yaml @@ -1,4 +1,21 @@ -cse-config-order: "1" +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: "1" trace: handler: @@ -21,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} @@ -35,7 +52,7 @@ hsa: rpc: serviceLevel: interface dsf: -# ip: "10.57.65.225" +# ip: "10.57.65.225" port: 7450 eureka: @@ -45,4 +62,4 @@ eureka: leaseExpirationDurationInSeconds: 5 client: serviceUrl: - defaultZone: http://10.120.169.202:9980/ \ No newline at end of file + defaultZone: http://10.120.169.202:9980/ diff --git a/foundations/foundation-config/src/test/resources/test2.yaml b/foundations/foundation-config/src/test/resources/test2.yaml index fddfdf68604..dc00542b274 100644 --- a/foundations/foundation-config/src/test/resources/test2.yaml +++ b/foundations/foundation-config/src/test/resources/test2.yaml @@ -1,8 +1,25 @@ -cse-config-order: 0 +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: 0 dsf: serializer: kryo: enabled: true referencesRequired: false - registrationRequired: false \ No newline at end of file + registrationRequired: false diff --git a/foundations/foundation-metrics/pom.xml b/foundations/foundation-metrics/pom.xml index 26c0a115270..d893011c4d4 100644 --- a/foundations/foundation-metrics/pom.xml +++ b/foundations/foundation-metrics/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - foundations - 0.3.0-SNAPSHOT - - foundation-metrics + 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 + foundations + 3.4.0-SNAPSHOT + + foundation-metrics + Java Chassis::Foundations::Metrics - - - io.servicecomb - foundation-common - - - com.netflix.archaius - archaius-core - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + + + org.apache.servicecomb + foundation-common + + + io.micrometer + micrometer-core + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.jmockit + jmockit + test + + diff --git a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/Metrics.java b/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/Metrics.java deleted file mode 100644 index 94d282c603b..00000000000 --- a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/Metrics.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics; - -import java.util.HashMap; -import java.util.Map; - -import io.servicecomb.foundation.common.CommonThread; -import io.servicecomb.foundation.metrics.performance.PerfStat; -import io.servicecomb.foundation.metrics.performance.PerfStatMonitorMgr; -import io.servicecomb.foundation.metrics.performance.PerfStatSuccFail; - -/** - * Metrics - * core layer performance logger - * - */ -public class Metrics extends CommonThread { - // 每个线程只在本线程内部做统计,不涉及多线程并发 - // 每个周期,统计线程会对所有线程做一次汇总,并与前一周期结果做对比,得出本周期的统计数据 - protected static final ThreadLocal> LOCAL_PERF_STAT_MAP = new ThreadLocal<>(); - - private static PerfStatMonitorMgr perfMonitorMgr = new PerfStatMonitorMgr(); - - public static void onCycle() throws Exception { - long msNow = System.currentTimeMillis(); - - synchronized (perfMonitorMgr) { - perfMonitorMgr.onCycle(msNow, MetricsConfig.getMsCycle()); - } - } - - public static long getMsTick() { - return MetricsThread.getMsTick(); - } - - public static void registerPerfStat(PerfStat perfStat, int index) { - synchronized (perfMonitorMgr) { - perfMonitorMgr.registerPerfStat(perfStat, index); - } - } - - public static PerfStatSuccFail getOrCreateLocalPerfStat(String name, int index) { - Map map = LOCAL_PERF_STAT_MAP.get(); - if (map == null) { - map = new HashMap<>(); - LOCAL_PERF_STAT_MAP.set(map); - } - - PerfStatSuccFail perfStat = map.get(name); - if (perfStat == null) { - perfStat = new PerfStatSuccFail(name); - // System.out.println(" *************************create stat " + - // name); - map.put(name, perfStat); - - registerPerfStat(perfStat, index); - } - - return perfStat; - } - - public static Map getMonitorPerfStat() { - synchronized (perfMonitorMgr) { - return perfMonitorMgr.getMonitorPerfStat(); - } - } -} diff --git a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/MetricsConfig.java b/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/MetricsConfig.java deleted file mode 100644 index f2839dcf84b..00000000000 --- a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/MetricsConfig.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics; - -import org.springframework.beans.factory.InitializingBean; - -import com.netflix.config.DynamicPropertyFactory; - -public class MetricsConfig implements InitializingBean { - - private static final int DEFAULT_METRICS_CYCLE = 60000; - - public static int getMsCycle() { - return DynamicPropertyFactory.getInstance() - .getIntProperty("cse.metrics.cycle.ms", DEFAULT_METRICS_CYCLE) - .get(); - } - - public static boolean isEnable() { - return DynamicPropertyFactory.getInstance().getBooleanProperty("cse.metrics.enabled", true).get(); - } - - @Override - public void afterPropertiesSet() throws Exception { - if (!isEnable()) { - return; - } - - new MetricsThread().start(); - } -} diff --git a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/MetricsThread.java b/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/MetricsThread.java deleted file mode 100644 index 035b534e967..00000000000 --- a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/MetricsThread.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.foundation.common.CommonThread; - -public class MetricsThread extends CommonThread { - private static final Logger LOGGER = LoggerFactory.getLogger(MetricsThread.class); - - private static final long SECOND_MILLS = 1000; - - // 从进程启动开始,每秒加1000 - // 这不是一个精确值,用于不关注精度的超时检测 - private static long msTick = 0; - - public MetricsThread() { - setName("metrics"); - } - - @Override - public void run() { - while (isRunning()) { - waitOneCycle(); - - try { - Metrics.onCycle(); - } catch (Exception e) { - LOGGER.info(e.getMessage()); - } - } - } - - private void waitOneCycle() { - long msLastCycle = msTick; - for (;;) { - try { - sleep(SECOND_MILLS); - } catch (InterruptedException e) { - } - msTick += SECOND_MILLS; - - if (msTick - msLastCycle >= MetricsConfig.getMsCycle()) { - break; - } - } - } - - public static long getMsTick() { - return msTick; - } -} diff --git a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfResult.java b/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfResult.java deleted file mode 100644 index 2e24032a41d..00000000000 --- a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfResult.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -/** - * PerfResult - * - * - */ -public class PerfResult { - private String name; - - private long callCount; - - private long msgCount; - - private long avgCallCount; - - private double msAvgLatency; - - private long[] msLatencySegments; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public long getCallCount() { - return callCount; - } - - public void setCallCount(long callCount) { - this.callCount = callCount; - } - - public long getMsgCount() { - return msgCount; - } - - public void setMsgCount(long msgCount) { - this.msgCount = msgCount; - } - - public long getAvgCallCount() { - return avgCallCount; - } - - public void setAvgCallCount(long avgCallCount) { - this.avgCallCount = avgCallCount; - } - - public double getMsAvgLatency() { - return msAvgLatency; - } - - public void setMsAvgLatency(double msAvgLatency) { - this.msAvgLatency = msAvgLatency; - } - - public long[] getMsLatencySegments() { - return msLatencySegments; - } - - public void setMsLatencySegments(long[] msLatencySegments) { - this.msLatencySegments = msLatencySegments; - } - - public String segmentsToString(String fmt) { - StringBuilder sb = new StringBuilder(); - for (long segCount : msLatencySegments) { - sb.append(String.format(fmt, segCount)); - } - return sb.toString(); - } -} diff --git a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStat.java b/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStat.java deleted file mode 100644 index 9c0f04cb9b1..00000000000 --- a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStat.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -import java.util.List; - -/** - * PerfStat - * - * - */ -public interface PerfStat { - String getName(); - - List getPerfStatDataList(); - - void mergeFrom(PerfStat otherPerfStat); - - void calc(long msNow, List perfResultList); - - void calc(PerfStat lastCycle, long msCycle, List perfResultList); -} diff --git a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatContext.java b/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatContext.java deleted file mode 100644 index 9f78567938f..00000000000 --- a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatContext.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -/** - * PerfStatContext - * - * - */ -public class PerfStatContext { - // 调用开始时间,用于统计 - protected long callBegin; - - // 本次统计涉及消息数量 - private int msgCount; - - public PerfStatContext() { - reset(); - } - - public long getLatency() { - return System.currentTimeMillis() - callBegin; - } - - public int getMsgCount() { - return msgCount; - } - - public void setMsgCount(int msgCount) { - this.msgCount = msgCount; - } - - public void reset() { - callBegin = System.currentTimeMillis(); - msgCount = 0; - } -} diff --git a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatData.java b/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatData.java deleted file mode 100644 index 69fbff00825..00000000000 --- a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatData.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -import java.util.Arrays; - -/** - * PerfStatData - * - * - */ -public class PerfStatData { - private static long processBegin = System.currentTimeMillis(); - - private static long[] segmentDef; - - private static final long[] SEGMENT_BOUNDRYS = new long[] {20, 100, 300, 500}; - - static { - setSegmentDef(SEGMENT_BOUNDRYS); - } - - //CodeDEX 不允许public static 非final的成员 - private static String strSegmentDef; - - private String name; - - private long callCount; - - private long msgCount; - - private long msLatency; - - private long[] msLatencySegments; - - private static final int MILLI_COUNT_IN_SECOND = 1000; - - public PerfStatData(String name) { - msLatencySegments = new long[segmentDef.length + 1]; - this.name = name; - } - - public static void setSegmentDef(long[] segmentDef) { - PerfStatData.segmentDef = segmentDef; - - StringBuilder sb = new StringBuilder(); - long last = 0; - for (long def : segmentDef) { - sb.append(String.format("%-10s", String.format("[%d,%d)", last, def))); - last = def; - } - sb.append(String.format("%-10s", String.format("[%d,...)", last))); - strSegmentDef = sb.toString(); - } - - public static String getStrSegmentDef() { - return strSegmentDef; - } - - public String getName() { - return name; - } - - public long getCallCount() { - return callCount; - } - - public long getMsgCount() { - return msgCount; - } - - public long getMsLatency() { - return msLatency; - } - - public long[] getMsLatencySegments() { - return msLatencySegments; - } - - protected int findSegmentIdx(long latency) { - long lastDef = 0; - for (int idx = 0; idx < segmentDef.length; idx++) { - long def = segmentDef[idx]; - if (latency >= lastDef && latency < def) { - return idx; - } - - lastDef = def; - } - - return segmentDef.length; - } - - public void add(int count, long latency) { - this.callCount++; - this.msgCount += count; - this.msLatency += latency; - - int segmentIdx = findSegmentIdx(latency); - this.msLatencySegments[segmentIdx]++; - } - - public void add(PerfStatContext context) { - add(context.getMsgCount(), context.getLatency()); - } - - public void mergeFrom(PerfStatData other) { - callCount += other.callCount; - msgCount += other.msgCount; - msLatency += other.msLatency; - for (int idx = 0; idx < msLatencySegments.length; idx++) { - msLatencySegments[idx] += other.msLatencySegments[idx]; - } - } - - public PerfResult calc(long msNow) { - PerfResult perf = new PerfResult(); - perf.setName(" all " + name + " :"); - perf.setCallCount(callCount); - perf.setMsgCount(msgCount); - perf.setAvgCallCount( - callCount * MILLI_COUNT_IN_SECOND / (msNow - processBegin > 0 ? msNow - processBegin : 1)); - perf.setMsAvgLatency((callCount != 0) ? (double) msLatency / callCount : 0); - perf.setMsLatencySegments(msLatencySegments); - return perf; - } - - public PerfResult calc(PerfStatData lastCycle, long msCycle) { - PerfResult perf = new PerfResult(); - - long diffCount = callCount - lastCycle.callCount; - perf.setName(" cycle " + name + ":"); - perf.setCallCount(diffCount); - perf.setMsgCount(msgCount - lastCycle.msgCount); - perf.setAvgCallCount(diffCount * MILLI_COUNT_IN_SECOND / msCycle); - perf.setMsAvgLatency((diffCount != 0) ? (double) (msLatency - lastCycle.msLatency) / diffCount : 0); - - long[] clone = Arrays.copyOf(msLatencySegments, msLatencySegments.length); - long[] lastCycleSegments = lastCycle.getMsLatencySegments(); - for (int idx = 0; idx < clone.length; idx++) { - clone[idx] -= lastCycleSegments[idx]; - } - perf.setMsLatencySegments(clone); - return perf; - } -} diff --git a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatImpl.java b/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatImpl.java deleted file mode 100644 index d74da57cdb6..00000000000 --- a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatImpl.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -import java.util.ArrayList; -import java.util.List; - -/** - * PerfStatImpl - * - * - */ -public class PerfStatImpl implements PerfStat { - // 接口或是场景名称 - private String name; - - private List dataList = new ArrayList<>(); - - public PerfStatImpl(String name) { - this.name = name; - } - - public PerfStatImpl(String name, PerfStatData data) { - this.name = name; - addPerfStatData(data); - } - - public void addPerfStatData(PerfStatData data) { - dataList.add(data); - } - - @Override - public String getName() { - return name; - } - - @Override - public List getPerfStatDataList() { - return dataList; - } - - @Override - public void mergeFrom(PerfStat otherPerfStat) { - name = otherPerfStat.getName(); - List otherDataList = otherPerfStat.getPerfStatDataList(); - if (dataList.isEmpty()) { - for (int idx = 0; idx < otherDataList.size(); idx++) { - PerfStatData otherData = otherDataList.get(idx); - dataList.add(new PerfStatData(otherData.getName())); - } - } - - for (int idx = 0; idx < otherDataList.size(); idx++) { - dataList.get(idx).mergeFrom(otherDataList.get(idx)); - } - } - - @Override - public void calc(long msNow, List perfResultList) { - for (PerfStatData data : dataList) { - perfResultList.add(data.calc(msNow)); - } - } - - @Override - public void calc(PerfStat lastCycle, long msCycle, List perfResultList) { - if (lastCycle == null) { - return; - } - - List lastCycleDataList = lastCycle.getPerfStatDataList(); - for (int idx = 0; idx < dataList.size(); idx++) { - PerfStatData data = dataList.get(idx); - PerfStatData lastCycleData = lastCycleDataList.get(idx); - - perfResultList.add(data.calc(lastCycleData, msCycle)); - } - } -} diff --git a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatMonitor.java b/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatMonitor.java deleted file mode 100644 index 66069ea6e2f..00000000000 --- a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatMonitor.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -import java.util.ArrayList; -import java.util.List; - -/** - * PerfStatMonitor - * - * - */ -public class PerfStatMonitor { - // 各线程内部的统计数据 - private List threadStats = new ArrayList<>(); - - private String name; - - private int index; - - // 每周期,对threadStats进行汇总,结果保存在这里 - private PerfStat sumStat; - - // 每周期计算产生的结果 - private List perfResultList; - - public PerfStatMonitor(String name, int index) { - this.name = name; - this.index = index; - } - - public String getName() { - return name; - } - - public int getIndex() { - return index; - } - - public void addThreadStat(PerfStat threadStat) { - threadStats.add(threadStat); - } - - public void calcCycle(long msNow, long msCycle) { - PerfStat newSumStat = new PerfStatImpl(null); - for (PerfStat threadStat : threadStats) { - newSumStat.mergeFrom(threadStat); - } - - perfResultList = new ArrayList<>(); - newSumStat.calc(msNow, perfResultList); - newSumStat.calc(sumStat, msCycle, perfResultList); - - sumStat = newSumStat; - } - - public void format(StringBuilder sb, String fmt) { - for (PerfResult result : perfResultList) { - String msg = String.format(result.getName() + fmt, - result.getCallCount(), - result.getMsgCount(), - result.getAvgCallCount(), - result.getMsAvgLatency(), - result.segmentsToString("%-10d")); - sb.append(msg); - } - } - - public PerfStat getPerfStat() { - return sumStat; - } - - public List getPerfResultList() { - return perfResultList; - } -} diff --git a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatMonitorMgr.java b/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatMonitorMgr.java deleted file mode 100644 index 6fecffc7286..00000000000 --- a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatMonitorMgr.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * PerfStatMonitorMgr - * - * - */ -public class PerfStatMonitorMgr { - private static final Logger LOGGER = LoggerFactory.getLogger(PerfStatMonitorMgr.class); - - private Map monitorMap = new HashMap<>(); - - private List monitorList = new ArrayList<>(); - - private String header = String.format( - " call count msg count avg tps avg latency(ms) |%s", - PerfStatData.getStrSegmentDef()); - - private String statFmt = "%-16d %-16d %-10d %-16.3f %s\n"; - - public void registerPerfStat(PerfStat perfStat, int index) { - String name = perfStat.getName(); - PerfStatMonitor monitor = monitorMap.get(name); - if (monitor == null) { - monitor = new PerfStatMonitor(name, index); - monitorMap.put(name, monitor); - - monitorList.add(monitor); - - monitorList.sort(new Comparator() { - - @Override - public int compare(PerfStatMonitor o1, PerfStatMonitor o2) { - return o1.getIndex() - o2.getIndex(); - } - }); - } - - monitor.addThreadStat(perfStat); - } - - public void onCycle(long msNow, long msCycle) { - StringBuilder sb = new StringBuilder(); - sb.append("Cycle stat output:\n" + header + "\n"); - for (PerfStatMonitor monitor : monitorList) { - monitor.calcCycle(msNow, msCycle); - - sb.append(" " + monitor.getName() + ":\n"); - monitor.format(sb, statFmt); - } - - LOGGER.info(sb.toString()); - } - - public Map getMonitorPerfStat() { - return monitorList.stream().collect(Collectors.toMap(m -> m.getName(), m -> m.getPerfStat())); - } - - public List getMonitorList() { - return monitorList; - } -} diff --git a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatSuccFail.java b/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatSuccFail.java deleted file mode 100644 index 38427705370..00000000000 --- a/foundations/foundation-metrics/src/main/java/io/servicecomb/foundation/metrics/performance/PerfStatSuccFail.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -/** - * PerfStatSuccFail - * - * - */ -public class PerfStatSuccFail extends PerfStatImpl { - - private PerfStatData succ = new PerfStatData("succ"); - - private PerfStatData fail = new PerfStatData("fail"); - - public PerfStatSuccFail(String name) { - super(name); - - addPerfStatData(succ); - addPerfStatData(fail); - } - - public void add(boolean isSucc, int msgCount, long latency) { - PerfStatData statData = succ; - if (!isSucc) { - msgCount = 0; - statData = fail; - } - statData.add(msgCount, latency); - } - - public void add(boolean isSucc, PerfStatContext context) { - add(isSucc, context.getMsgCount(), context.getLatency()); - } -} 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 new file mode 100644 index 00000000000..86529a36e41 --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +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.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 final MetricsBootstrapConfig config; + + private MeterRegistry meterRegistry; + + private EventBus eventBus; + + private ScheduledExecutorService executorService; + + 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("metrics-poller-%d") + .build()); + + metricsInitializers.forEach(initializer -> initializer.init(this.meterRegistry, eventBus, config)); + startPoll(); + } + + public void shutdown() { + if (executorService != null) { + executorService.shutdown(); + } + + Collections.reverse(metricsInitializers); + metricsInitializers.forEach(MetricsInitializer::destroy); + } + + protected void startPoll() { + executorService.scheduleAtFixedRate(this::pollMeters, + 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 { + 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 new file mode 100644 index 00000000000..53ab9a6d6ff --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrapConfig.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.metrics; + +import org.springframework.core.env.Environment; + +public class MetricsBootstrapConfig { + public static final String METRICS_WINDOW_TIME = "servicecomb.metrics.window_time"; + + 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; + + private String latencyDistribution; + + private int minScopeLength; + + private Environment environment; + + public MetricsBootstrapConfig(Environment environment) { + this.environment = environment; + msPollInterval = + 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 new file mode 100644 index 00000000000..3cb0b8f95f0 --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.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.foundation.metrics; + +import com.google.common.eventbus.EventBus; + +import io.micrometer.core.instrument.MeterRegistry; + +public interface MetricsInitializer { + default int getOrder() { + return 0; + } + + 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 new file mode 100644 index 00000000000..0814f96149f --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/PolledEvent.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.foundation.metrics; + +import java.util.List; + +import io.micrometer.core.instrument.Meter; + + +public class PolledEvent { + private List meters; + + public PolledEvent(List meters) { + this.meters = meters; + } + + public List getMeters() { + return meters; + } + + public void setMeters(List meters) { + this.meters = meters; + } +} 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 new file mode 100644 index 00000000000..930288aa5f3 --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthCheckResult.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.foundation.metrics.health; + +public class HealthCheckResult { + private boolean healthy; + + private String information; + + private String extraData; + + private long timestamp; + + public boolean isHealthy() { + return healthy; + } + + public String getInformation() { + return information; + } + + public String getExtraData() { + return extraData; + } + + public long getTimestamp() { + return timestamp; + } + + public HealthCheckResult() { + } + + public HealthCheckResult(boolean healthy, String information, String extraData) { + this(); + this.healthy = healthy; + this.information = information; + this.extraData = extraData; + this.timestamp = System.currentTimeMillis(); + } +} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthChecker.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthChecker.java new file mode 100644 index 00000000000..4dd016f3ad5 --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthChecker.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.foundation.metrics.health; + +public interface HealthChecker { + String getName(); + + HealthCheckResult check(); +} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthCheckerManager.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthCheckerManager.java new file mode 100644 index 00000000000..5e635f354a3 --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/health/HealthCheckerManager.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.foundation.metrics.health; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; + +public class HealthCheckerManager { + private final Map healthCheckers; + + private static final HealthCheckerManager INSTANCE = new HealthCheckerManager(); + + public static HealthCheckerManager getInstance() { + return INSTANCE; + } + + private HealthCheckerManager() { + this.healthCheckers = new ConcurrentHashMap<>(); + List checkers = SPIServiceUtils.getAllService(HealthChecker.class); + for (HealthChecker checker : checkers) { + register(checker); + } + } + + public void register(HealthChecker checker) { + healthCheckers.put(checker.getName(), checker); + } + + public void unregister(String name) { + healthCheckers.remove(name); + } + + public Map check() { + return healthCheckers.entrySet().stream().collect(Collectors.toMap(Entry::getKey, e -> e.getValue().check())); + } +} 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 new file mode 100644 index 00000000000..d464697024f --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +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<>(); + + /** + * + * @param config scope definition, time unit is milliseconds, eg:0,1,10 + */ + public LatencyDistributionConfig(String config) { + if (StringUtils.isEmpty(config)) { + return; + } + config = config.trim() + "," + MAX_LATENCY; + String[] array = config.split("\\s*,+\\s*"); + try { + for (int idx = 0; idx < array.length - 1; idx++) { + long msMin = Long.parseLong(array[idx]); + long msMax = Long.parseLong(array[idx + 1]); + if (msMin >= msMax) { + String msg = String.format("invalid latency scope, min=%s, max=%s.", array[idx], array[idx + 1]); + throw new IllegalStateException(msg); + } + + LatencyScopeConfig latencyScopeConfig = new LatencyScopeConfig(msMin, msMax); + scopeConfigs.add(latencyScopeConfig); + } + } catch (Throwable e) { + LOGGER.error("Failed to parse latencyDistributionConfig, value={}", config, e); + throw e; + } + } + + public List getScopeConfigs() { + return scopeConfigs; + } +} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyScopeConfig.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyScopeConfig.java new file mode 100644 index 00000000000..0beeb7cfee9 --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyScopeConfig.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.foundation.metrics.meter; + +public class LatencyScopeConfig { + // [min, max) + // even max equals Long.MAX_VALUE, still not include it + // because it will never happened in real cases + private final long msMin; + + private final long msMax; + + public LatencyScopeConfig(long msMin, long msMax) { + this.msMin = msMin; + this.msMax = msMax; + } + + public long getMsMin() { + return msMin; + } + + public long getMsMax() { + return msMax; + } + + @Override + public String toString() { + return "LatencyScopeConfig[" + msMin + ", " + msMax + ')'; + } +} 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 new file mode 100644 index 00000000000..a846d08179d --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/PeriodMeter.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.foundation.metrics.meter; + +public interface PeriodMeter { + void poll(long msNow, long secondInterval); +} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/DefaultTagFinder.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/DefaultTagFinder.java new file mode 100644 index 00000000000..f29227c6a72 --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/DefaultTagFinder.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.foundation.metrics.publish; + + +import io.micrometer.core.instrument.Tag; + +public class DefaultTagFinder implements TagFinder { + private final String tagKey; + + private boolean skipOnNull; + + public DefaultTagFinder(String tagKey) { + this.tagKey = tagKey; + } + + public DefaultTagFinder(String tagKey, boolean skipOnNull) { + this.tagKey = tagKey; + this.skipOnNull = skipOnNull; + } + + @Override + public boolean skipOnNull() { + return skipOnNull; + } + + @Override + public String getTagKey() { + return tagKey; + } + + @Override + public Tag find(Iterable tags) { + for (Tag tag : tags) { + if (tag.getKey().equals(tagKey)) { + return tag; + } + } + + return null; + } +} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementGroupConfig.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementGroupConfig.java new file mode 100644 index 00000000000..cec6db25eb4 --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementGroupConfig.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.foundation.metrics.publish; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class MeasurementGroupConfig { + // key is measurement id name + private final Map> groups = new HashMap<>(); + + public MeasurementGroupConfig() { + } + + public MeasurementGroupConfig(String idName, Object... tagNameOrFinders) { + addGroup(idName, tagNameOrFinders); + } + + public void addGroup(String idName, Object... tagNameOrFinders) { + groups.put(idName, + Arrays + .asList(tagNameOrFinders) + .stream() + .map(TagFinder::build) + .collect(Collectors.toList())); + } + + public List findTagFinders(String idName) { + return groups.get(idName); + } +} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementNode.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementNode.java new file mode 100644 index 00000000000..8ff19a15345 --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementNode.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.LinkedHashMap; +import java.util.List; +import java.util.Map; + +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, Id id, Map children) { + this.name = name; + this.id = id; + this.children = children; + } + + public String getName() { + return this.name; + } + + public Id getId() { + return id; + } + + public Map getChildren() { + return children; + } + + public MeasurementNode findChild(String childName) { + if (children == null) { + return null; + } + return children.get(childName); + } + + public MeasurementNode findChild(String... childNames) { + MeasurementNode node = this; + for (String childName : childNames) { + if (node == null) { + return null; + } + + node = node.findChild(childName); + } + return node; + } + + public MeasurementNode addChild(String childName, Id id, Measurement measurement) { + if (children == null) { + children = new LinkedHashMap<>(); + } + + MeasurementNode node = children.computeIfAbsent(childName, name -> new MeasurementNode(name, id, null)); + + if (measurement != null) { + node.addMeasurement(measurement); + } + + return node; + } + + public List getMeasurements() { + return measurements; + } + + public void addMeasurement(Measurement measurement) { + measurements.add(measurement); + } + + public double summary() { + double result = 0; + for (Measurement measurement : measurements) { + result += measurement.getValue(); + } + + return result; + } + + @Override + public int compareTo(MeasurementNode o) { + return this.name.compareTo(o.name); + } +} 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/TagFinder.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/TagFinder.java new file mode 100644 index 00000000000..e1f3c8b402c --- /dev/null +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/TagFinder.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.foundation.metrics.publish; + +import io.micrometer.core.instrument.Tag; + +public interface TagFinder { + static TagFinder build(Object obj) { + if (String.class.isInstance(obj)) { + return new DefaultTagFinder((String) obj); + } + + if (TagFinder.class.isInstance(obj)) { + return (TagFinder) obj; + } + + throw new IllegalArgumentException( + "only support String or TagFinder, but got " + + (obj == null ? "null" : obj.getClass().getName())); + } + + default boolean skipOnNull() { + return false; + } + + String getTagKey(); + + // read target tag from tags + // return directly or do some change and then return + Tag find(Iterable tags); +} diff --git a/foundations/foundation-metrics/src/main/resources/META-INF/spring/metrics.bean.xml b/foundations/foundation-metrics/src/main/resources/META-INF/spring/metrics.bean.xml deleted file mode 100644 index 49b330acffa..00000000000 --- a/foundations/foundation-metrics/src/main/resources/META-INF/spring/metrics.bean.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/TestMetrics.java b/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/TestMetrics.java deleted file mode 100644 index 00e819d6db1..00000000000 --- a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/TestMetrics.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.foundation.metrics.performance.PerfStatSuccFail; - -public class TestMetrics { - - @Test - public void testOnCycle() throws Exception { - PerfStatSuccFail oPerfStatSuccFail = Metrics.getOrCreateLocalPerfStat("test", 1); - Metrics.onCycle(); - Assert.assertEquals(0, Metrics.getMsTick()); - Assert.assertEquals(1, Metrics.getMonitorPerfStat().size()); - Assert.assertEquals("test", oPerfStatSuccFail.getName()); - Assert.assertEquals(2, oPerfStatSuccFail.getPerfStatDataList().size()); - } -} diff --git a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/TestMetricsConfig.java b/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/TestMetricsConfig.java deleted file mode 100644 index 00cdac2a669..00000000000 --- a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/TestMetricsConfig.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestMetricsConfig { - - MetricsConfig oMetricsConfig = null; - - @Before - public void setUp() throws Exception { - oMetricsConfig = new MetricsConfig(); - } - - @After - public void tearDown() throws Exception { - oMetricsConfig = null; - } - - @Test - public void test() { - Assert.assertEquals(60000, MetricsConfig.getMsCycle()); - Assert.assertEquals(true, MetricsConfig.isEnable()); - } -} diff --git a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/TestMetricsThread.java b/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/TestMetricsThread.java deleted file mode 100644 index 308c6f7d231..00000000000 --- a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/TestMetricsThread.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestMetricsThread { - - MetricsThread oMetricsThread = null; - - @Before - public void setUp() throws Exception { - oMetricsThread = new MetricsThread(); - } - - @After - public void tearDown() throws Exception { - oMetricsThread.shutdown(); - oMetricsThread = null; - } - - @Test - public void test() { - oMetricsThread.start(); - Assert.assertEquals(true, oMetricsThread.isRunning()); - oMetricsThread.shutdown(); - Assert.assertEquals(false, oMetricsThread.isRunning()); - } -} diff --git a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfResult.java b/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfResult.java deleted file mode 100644 index 1fbfe8c84dc..00000000000 --- a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfResult.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestPerfResult { - - PerfResult oPerfResult = null; - - @Before - public void setUp() throws Exception { - oPerfResult = new PerfResult(); - } - - @After - public void tearDown() throws Exception { - oPerfResult = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oPerfResult.getName()); - Assert.assertEquals(0, oPerfResult.getCallCount()); - Assert.assertEquals(0, oPerfResult.getMsAvgLatency(), 0); - Assert.assertEquals(0, oPerfResult.getAvgCallCount()); - Assert.assertNull(oPerfResult.getMsLatencySegments()); - Assert.assertEquals(0, oPerfResult.getMsgCount()); - } - - @Test - public void testIntializedValues() { - initializeObject(); //Initialize the object. - Assert.assertEquals("testPerf", oPerfResult.getName()); - Assert.assertEquals(1, oPerfResult.getCallCount()); - Assert.assertEquals(56, oPerfResult.getMsAvgLatency(), 0); - Assert.assertEquals(2, oPerfResult.getAvgCallCount()); - Assert.assertEquals(2, oPerfResult.getMsLatencySegments().length); - Assert.assertEquals(10, oPerfResult.getMsgCount()); - Assert.assertEquals("testStringtestString", oPerfResult.segmentsToString("testString")); - } - - private void initializeObject() { - long[] oLongLatencySegment = new long[] {123, 154}; - oPerfResult.setAvgCallCount(2); - oPerfResult.setCallCount(1); - oPerfResult.setMsAvgLatency(56); - oPerfResult.setMsgCount(10); - oPerfResult.setName("testPerf"); - oPerfResult.setMsLatencySegments(oLongLatencySegment); - } -} diff --git a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatContext.java b/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatContext.java deleted file mode 100644 index 38419ec43fa..00000000000 --- a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatContext.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import mockit.Mock; -import mockit.MockUp; - -public class TestPerfStatContext { - - PerfStatContext oPerfStatContext = null; - - @Before - public void setUp() throws Exception { - oPerfStatContext = new PerfStatContext(); - } - - @After - public void tearDown() throws Exception { - oPerfStatContext = null; - } - - @Test - public void testDefaultValues() { - Assert.assertEquals(0, oPerfStatContext.getMsgCount()); - Assert.assertTrue(oPerfStatContext.getLatency() >= 0); - } - - @Test - public void testIntializedValues() throws InterruptedException { - new MockUp() { - int count = 0; - - @Mock - public long currentTimeMillis() { - if (count == 0) { - count++; - return 10; - } else { - return 20; - } - } - }; - PerfStatContext oPerfStatContext = new PerfStatContext(); - oPerfStatContext.setMsgCount(10); - Assert.assertEquals(10, oPerfStatContext.getMsgCount()); - Assert.assertEquals(10, oPerfStatContext.getLatency()); - } -} diff --git a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatData.java b/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatData.java deleted file mode 100644 index 41603fe1562..00000000000 --- a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatData.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestPerfStatData { - - PerfStatData oPerfStatData = null; - - @Before - public void setUp() throws Exception { - oPerfStatData = new PerfStatData("testData"); - } - - @After - public void tearDown() throws Exception { - oPerfStatData = null; - } - - @Test - public void testDefaultValues() { - Assert.assertEquals("testData", oPerfStatData.getName()); - Assert.assertEquals(0, oPerfStatData.getCallCount()); - Assert.assertEquals(0, oPerfStatData.getMsLatency()); - Assert.assertNotNull(oPerfStatData.getMsLatencySegments()); - Assert.assertEquals(0, oPerfStatData.getMsgCount()); - Assert.assertNotNull(PerfStatData.getStrSegmentDef()); - } - - @Test - public void testAdd() { - oPerfStatData.add(10, 100); - Assert.assertEquals(10, oPerfStatData.getMsgCount()); - Assert.assertEquals(100, oPerfStatData.getMsLatency()); - - //Test Add function with PerfStatContext - PerfStatContext oPerfStatContext = new PerfStatContext(); - oPerfStatContext.setMsgCount(30); - oPerfStatData.add(oPerfStatContext); - Assert.assertEquals(40, oPerfStatData.getMsgCount()); - } - - @Test - public void testMergeFrom() { - oPerfStatData.mergeFrom(new PerfStatData("anotherData")); - Assert.assertEquals(0, oPerfStatData.getMsgCount()); - Assert.assertEquals(0, oPerfStatData.getCallCount()); - Assert.assertEquals(0, oPerfStatData.getMsLatency()); - } - - @Test - public void testCalc() { - PerfResult oPerfResult = oPerfStatData.calc(System.currentTimeMillis() + 18989); - Assert.assertEquals(" all testData :", oPerfResult.getName()); - Assert.assertEquals(0, oPerfResult.getCallCount()); - Assert.assertEquals(0, oPerfResult.getMsgCount()); - - //test calc with another PerfStatData - oPerfResult = oPerfStatData.calc(new PerfStatData("anotherData"), System.currentTimeMillis() + 18989); - Assert.assertEquals(" cycle testData:", oPerfResult.getName()); - Assert.assertEquals(0, oPerfResult.getCallCount()); - Assert.assertEquals(0, oPerfResult.getMsgCount()); - } -} diff --git a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatImpl.java b/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatImpl.java deleted file mode 100644 index 0eb948e0291..00000000000 --- a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatImpl.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestPerfStatImpl { - - PerfStatImpl oPerfStatImpl = null; - - PerfStatSuccFail oPerfStatSuccFail = null; - - @Before - public void setUp() throws Exception { - oPerfStatImpl = new PerfStatImpl("testData"); - oPerfStatSuccFail = new PerfStatSuccFail("testMergeFrom"); - } - - @After - public void tearDown() throws Exception { - oPerfStatImpl = null; - } - - @Test - public void testMergeFrom() { - oPerfStatImpl.mergeFrom(oPerfStatSuccFail); - Assert.assertEquals("testMergeFrom", oPerfStatImpl.getName()); - Assert.assertEquals(2, oPerfStatImpl.getPerfStatDataList().size()); - } - - @Test - public void testCalc() { - oPerfStatImpl = new PerfStatImpl("testConstructor", new PerfStatData("test")); - PerfResult oPerfResult = new PerfResult(); - oPerfResult.setName("test"); - List oPerfResultList = new ArrayList<>(); - oPerfResultList.add(oPerfResult); - oPerfStatImpl.calc(System.currentTimeMillis(), oPerfResultList); - Assert.assertEquals(2, oPerfResultList.size()); - - //Testing Calc with null PerfStat - oPerfStatImpl.calc(null, 20, oPerfResultList); - Assert.assertEquals(2, oPerfResultList.size()); //The list size does not increase - - //Testing Calc with PerfStat - oPerfStatImpl.calc(oPerfStatSuccFail, 20, oPerfResultList); - Assert.assertEquals(3, oPerfResultList.size()); - } -} diff --git a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatMonitor.java b/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatMonitor.java deleted file mode 100644 index 0d7c47f3be2..00000000000 --- a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatMonitor.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestPerfStatMonitor { - - PerfStatMonitor oPerfStatMonitor = null; - - PerfStatSuccFail oPerfStatSuccFail = null; - - @Before - public void setUp() throws Exception { - oPerfStatMonitor = new PerfStatMonitor("testMonitor", 0); - oPerfStatSuccFail = new PerfStatSuccFail("testMergeFrom"); - } - - @After - public void tearDown() throws Exception { - oPerfStatMonitor = null; - } - - @Test - public void testCalcCycle() { - Assert.assertEquals("testMonitor", oPerfStatMonitor.getName()); - Assert.assertEquals(0, oPerfStatMonitor.getIndex()); - oPerfStatMonitor.addThreadStat(oPerfStatSuccFail); - oPerfStatMonitor.calcCycle(System.currentTimeMillis(), 20); - Assert.assertEquals("testMergeFrom", oPerfStatMonitor.getPerfStat().getName()); - Assert.assertEquals(2, oPerfStatMonitor.getPerfResultList().size()); - - //Test Format - StringBuilder oBuilder = new StringBuilder(); - oPerfStatMonitor.format(oBuilder, "Test"); - Assert.assertEquals(" all succ :Test all fail :Test", oBuilder.toString()); - } -} diff --git a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatMonitorMgr.java b/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatMonitorMgr.java deleted file mode 100644 index 939b522a5cc..00000000000 --- a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatMonitorMgr.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestPerfStatMonitorMgr { - - PerfStatMonitorMgr oPerfStatMonitorMgr = null; - - PerfStatSuccFail oPerfStatSuccFail = null; - - @Before - public void setUp() throws Exception { - oPerfStatMonitorMgr = new PerfStatMonitorMgr(); - oPerfStatSuccFail = new PerfStatSuccFail("testMergeFrom"); - } - - @After - public void tearDown() throws Exception { - oPerfStatMonitorMgr = null; - oPerfStatSuccFail = null; - } - - @Test - public void testRegisterPerfStat() { - oPerfStatMonitorMgr.registerPerfStat(oPerfStatSuccFail, 0); - Assert.assertEquals(1, oPerfStatMonitorMgr.getMonitorList().size()); - } - - @Test - public void testOnCycle() { - oPerfStatMonitorMgr.registerPerfStat(oPerfStatSuccFail, 0); - oPerfStatMonitorMgr.onCycle(System.currentTimeMillis(), 10); - Assert.assertEquals(1, oPerfStatMonitorMgr.getMonitorPerfStat().size()); - } -} diff --git a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatSuccFail.java b/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatSuccFail.java deleted file mode 100644 index 039cce02354..00000000000 --- a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/performance/TestPerfStatSuccFail.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.metrics.performance; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestPerfStatSuccFail { - - PerfStatSuccFail oPerfStatSuccFail = null; - - @Before - public void setUp() throws Exception { - oPerfStatSuccFail = new PerfStatSuccFail("testSuccFail"); - } - - @After - public void tearDown() throws Exception { - oPerfStatSuccFail = null; - } - - @Test - public void testAdd() { - oPerfStatSuccFail.add(true, new PerfStatContext()); - Assert.assertEquals(2, oPerfStatSuccFail.getPerfStatDataList().size()); - - //Test io.servicecomb.foundation.metrics.performance.PerfStatSuccFail.add(boolean, int, long) - oPerfStatSuccFail.add(false, 10, 100); - Assert.assertEquals(2, oPerfStatSuccFail.getPerfStatDataList().size()); - } -} 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 new file mode 100644 index 00000000000..231b0099ed4 --- /dev/null +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +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.ScheduledExecutorService; + +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.google.common.eventbus.EventBus; + +import io.micrometer.core.instrument.MeterRegistry; +import mockit.Deencapsulation; + +public class TestMetricsBootstrap { + 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(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + initList.add(this); + } + }; + + bootstrap.setMetricsInitializers(Arrays.asList(metricsInitializer, metricsInitializer)); + + bootstrap.start(eventBus); + bootstrap.shutdown(); + + MatcherAssert.assertThat(initList, Matchers.contains(metricsInitializer, metricsInitializer)); + } + + @Test + public void shutdown() { + ScheduledExecutorService scheduledExecutorService = Mockito.mock(ScheduledExecutorService.class); + List destroyList = new ArrayList<>(); + MetricsInitializer initializer1 = new MetricsInitializer() { + @Override + public int getOrder() { + return 1; + } + + @Override + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + } + + @Override + public void destroy() { + destroyList.add(this); + } + }; + + MetricsInitializer initializer2 = new MetricsInitializer() { + @Override + public int getOrder() { + return 2; + } + + @Override + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + } + + @Override + public void destroy() { + destroyList.add(this); + } + }; + bootstrap.setMetricsInitializers(Arrays.asList(initializer1, initializer2)); + Deencapsulation.setField(bootstrap, "executorService", scheduledExecutorService); + + bootstrap.shutdown(); + + MatcherAssert.assertThat(destroyList, Matchers.contains(initializer2, initializer1)); + } + + @Test + public void shutdown_notStart() { + Assertions.assertNull(Deencapsulation.getField(bootstrap, "executorService")); + + // should not throw exception + bootstrap.shutdown(); + } +} diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/health/TestHealthCheckerManager.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/health/TestHealthCheckerManager.java new file mode 100644 index 00000000000..fad079aaed5 --- /dev/null +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/health/TestHealthCheckerManager.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.foundation.metrics.health; + +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class TestHealthCheckerManager { + + private final HealthChecker good = new HealthChecker() { + @Override + public String getName() { + return "testBad"; + } + + @Override + public HealthCheckResult check() { + return new HealthCheckResult(false, "bad", "bad component"); + } + }; + + private final HealthChecker bad = new HealthChecker() { + @Override + public String getName() { + return "testGood"; + } + + @Override + public HealthCheckResult check() { + return new HealthCheckResult(true, "good", "good component"); + } + }; + + @BeforeEach + public void reset() { + HealthCheckerManager.getInstance().unregister(good.getName()); + HealthCheckerManager.getInstance().unregister(bad.getName()); + } + + @Test + public void checkResultCount_None() { + Map results = HealthCheckerManager.getInstance().check(); + Assertions.assertEquals(0, results.size()); + } + + @Test + public void checkResultCount_One() { + HealthCheckerManager.getInstance().register(good); + Map results = HealthCheckerManager.getInstance().check(); + Assertions.assertEquals(1, results.size()); + } + + @Test + public void checkResultCount_Both() { + HealthCheckerManager.getInstance().register(good); + HealthCheckerManager.getInstance().register(bad); + Map results = HealthCheckerManager.getInstance().check(); + Assertions.assertEquals(2, results.size()); + } + + @Test + public void checkGoodResult() { + HealthCheckerManager.getInstance().register(good); + HealthCheckerManager.getInstance().register(bad); + HealthCheckResult result = HealthCheckerManager.getInstance().check().get("testGood"); + Assertions.assertTrue(result.isHealthy()); + Assertions.assertEquals("good", result.getInformation()); + Assertions.assertEquals("good component", result.getExtraData()); + } + + @Test + public void checkBadResult() { + HealthCheckerManager.getInstance().register(good); + HealthCheckerManager.getInstance().register(bad); + HealthCheckResult result = HealthCheckerManager.getInstance().check().get("testBad"); + Assertions.assertFalse(result.isHealthy()); + Assertions.assertEquals("bad", result.getInformation()); + Assertions.assertEquals("bad component", result.getExtraData()); + } +} diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestLatencyDistributionConfig.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestLatencyDistributionConfig.java new file mode 100644 index 00000000000..5144896e015 --- /dev/null +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestLatencyDistributionConfig.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.foundation.metrics.meter; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestLatencyDistributionConfig { + + @Test + public void testValidProperty() { + String validProperty1 = "0,1,2,10"; + String validProperty2 = "0,1, 2 , 10 "; + String validProperty3 = "0,1,2,10,"; + + LatencyDistributionConfig config1 = new LatencyDistributionConfig(validProperty1); + LatencyDistributionConfig config2 = new LatencyDistributionConfig(validProperty2); + LatencyDistributionConfig config3 = new LatencyDistributionConfig(validProperty3); + + Assertions.assertEquals(4, config1.getScopeConfigs().size()); + Assertions.assertEquals(4, config2.getScopeConfigs().size()); + Assertions.assertEquals(4, config3.getScopeConfigs().size()); + } + + @Test + public void testInValidProperty1() { + IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { + LatencyDistributionConfig latencyDistributionConfig = new LatencyDistributionConfig("2,1,10"); + Assertions.assertEquals(0, latencyDistributionConfig.getScopeConfigs().size()); + }); + Assertions.assertEquals("invalid latency scope, min=2, max=1.", exception.getMessage()); + } + + @Test + public void testInValidProperty2() { + NumberFormatException exception = Assertions.assertThrows(NumberFormatException.class, () -> { + LatencyDistributionConfig latencyDistributionConfig = new LatencyDistributionConfig("a,1,10"); + Assertions.assertEquals(0, latencyDistributionConfig.getScopeConfigs().size()); + }); + Assertions.assertEquals("For input string: \"a\"", exception.getMessage()); + } +} 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 new file mode 100644 index 00000000000..46220069b97 --- /dev/null +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestDefaultTagFinder.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.foundation.metrics.publish.spectator; + +import java.util.Arrays; +import java.util.List; + +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"); + + @Test + public void getTagKey() { + Assertions.assertEquals("key", finder.getTagKey()); + } + + @Test + public void readSucc() { + Tag tag = Tag.of("key", "value"); + List tags = Arrays.asList(Tag.of("t1", "t1v"), + tag); + + Assertions.assertSame(tag, finder.find(tags)); + } + + @Test + public void readFail() { + 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 new file mode 100644 index 00000000000..60d77b4c75a --- /dev/null +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementGroupConfig.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.foundation.metrics.publish.spectator; + +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; + +import mockit.Deencapsulation; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestMeasurementGroupConfig { + MeasurementGroupConfig config = new MeasurementGroupConfig(); + + Map> groups = Deencapsulation.getField(config, "groups"); + + @Test + public void defaultConstruct() { + Assertions.assertTrue(groups.isEmpty()); + } + + @Test + public void constructAddGroup() { + config = new MeasurementGroupConfig("id", "tag1"); + groups = Deencapsulation.getField(config, "groups"); + + MatcherAssert.assertThat(groups.keySet(), Matchers.contains("id")); + MatcherAssert.assertThat(groups.get("id").stream().map(TagFinder::getTagKey).toArray(), + Matchers.arrayContaining("tag1")); + } + + @Test + public void addGroup() { + config.addGroup("id1", "tag1.1", "tag1.2"); + config.addGroup("id2", "tag2.1", "tag2.2"); + + 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")); + } + + @Test + public void findTagReaders() { + config.addGroup("id1", "tag1.1", "tag1.2"); + config.addGroup("id2", "tag2.1", "tag2.2"); + + MatcherAssert.assertThat(config.findTagFinders("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 new file mode 100644 index 00000000000..be7681ae5cd --- /dev/null +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 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; + +public class TestMeasurementNode { + Id id = new Id("name", Tags.empty(), null, null, Type.OTHER); + + MeasurementNode node = new MeasurementNode("name", id, null); + + @Test + public void getName() { + Assertions.assertEquals("name", node.getName()); + } + + @Test + public void getChildren() { + Map children = new HashMap<>(); + node = new MeasurementNode("name", id, children); + + Assertions.assertSame(children, node.getChildren()); + } + + @Test + public void findChild_noChildren() { + Assertions.assertNull(node.findChild("child")); + } + + @Test + public void findChild_multiLevel_noMiddleChildren(@Mocked Measurement 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", 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", id, measurement); + MeasurementNode c2 = node.addChild("c2", id, measurement); + + Assertions.assertSame(c1, node.findChild("c1")); + Assertions.assertSame(c2, node.findChild("c2")); + } + + @Test + public void getMeasurements(@Mocked Measurement measurement) { + node.addMeasurement(measurement); + + MatcherAssert.assertThat(node.getMeasurements(), Matchers.contains(measurement)); + } + + @Test + public void summary(@Mocked Measurement measurement) { + new Expectations() { + { + measurement.getValue(); + result = 10; + } + }; + node.addMeasurement(measurement); + node.addMeasurement(measurement); + + Assertions.assertEquals(20, node.summary(), 0); + } +} 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 new file mode 100644 index 00000000000..d21354be5b2 --- /dev/null +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementTree.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.metrics.publish.spectator; + +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 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(); + + MeterRegistry registry = new SimpleMeterRegistry(); + + Timer timer; + + @Before + public void setup() { + timer = registry.timer("id", + "g1", + "g1v", + "g2", + "g2v", + "t3", + "t3v", + "t4", + "t4v"); + registry.counter("id_notCare"); + } + + @Test + public void from() { + timer.record(10, TimeUnit.SECONDS); + timer.record(2, TimeUnit.SECONDS); + + 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.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); + } + + @Test + public void from_withSkipOnNull() { + try { + MeasurementGroupConfig config = new MeasurementGroupConfig("id", new DefaultTagFinder("notExist", true)); + tree.from(registry.getMeters().iterator(), config); + } catch (Exception e) { + Assertions.fail("should not throw exception"); + } + } + + @Test + public void from_failed() { + IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { + MeasurementGroupConfig config = new MeasurementGroupConfig("id", "notExist"); + tree.from(registry.getMeters().iterator(), config); + }); + 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 new file mode 100644 index 00000000000..2a0d960637a --- /dev/null +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestTagFinder.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.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; + +public class TestTagFinder { + + @Test + public void buildFromString() { + String name = "key"; + TagFinder finder = TagFinder.build(name); + + Assertions.assertEquals(name, finder.getTagKey()); + Assertions.assertEquals(DefaultTagFinder.class, finder.getClass()); + } + + @Test + public void buildFromTagFinder() { + TagFinder finder = new DefaultTagFinder("key"); + Assertions.assertSame(finder, TagFinder.build(finder)); + DefaultTagFinder tagFinder = new DefaultTagFinder("key", true); + Assertions.assertSame(tagFinder, TagFinder.build(tagFinder)); + } + + @Test + public void buildFromInvalidType() { + IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> TagFinder.build(1)); + Assertions.assertEquals("only support String or TagFinder, but got " + Integer.class.getName(), exception.getMessage()); + } + + @Test + public void buildFromNull() { + IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> TagFinder.build(null)); + Assertions.assertEquals("only support String or TagFinder, but got null", exception.getMessage()); + } +} diff --git a/foundations/foundation-metrics/src/test/resources/log4j.properties b/foundations/foundation-metrics/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/foundations/foundation-metrics/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/foundations/foundation-protobuf/README.md b/foundations/foundation-protobuf/README.md new file mode 100644 index 00000000000..c225e89dc87 --- /dev/null +++ b/foundations/foundation-protobuf/README.md @@ -0,0 +1,153 @@ +# 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: + +| | 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 | +| support "packed" | yes | yes | no | no | +| support "map" | yes | yes | not compatible | no | +| support "any" | yes | yes | not compatible | no | +| support "any" not defined in IDL | yes
extend based on "any" mechanism | no | not compatible | no | +| support field: `List>` | yes | no | not compatible | no | +| support field: `List>` | yes | no | not compatible | no | +| support field: `Map>` | yes | no | not compatible | no | +| support field: `Map>` | yes | no | not compatible | no | +| support field: array | yes | no | not compatible | no | +| support generic POJO type, eg:`CustomGeneric` | yes | no | no | no | +| serialize/deserialize based on IDL | yes | no | no | yes | +| 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 +```java +ProtoMapperFactory factory = new ProtoMapperFactory(); +``` +## Load proto definition + create mapper instance for each proto definition +- load from classpath +```java +ProtoMapper protoMapper = factory.createFromName("protobuf.proto"); +``` +- load from proto content +```java +ProtoMapper protoMapper = factory.createFromContent(protoContent); +``` +## Serialize +serializer is reusable and thread safe +Assuming you have a proto definition +```proto +message User { + string name = 1; +} +``` +and a POJO class +```java +public class User { + private String name; + // getter and setter +} +``` +RootSerializer serializer = protoMapper.createRootSerializer("User", User.class); + +User user = new User(); +user.setName("userName"); +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 +```java +RootDeserializer pojoDeserializer = protoMapper.createRootDeserializer("User", User.class); +RootDeserializer> mapDeserializer = protoMapper.createRootDeserializer("User", Map.class); + +User user = pojoDeserializer.deserialize(bytes); +Map map = mapDeserializer.deserialize(bytes); +``` + +# Performance +``` +1.protobuf + in our real scenes + business model never bind to transport, and can switch between different transports dynamically + that means if we choose standard protobuf, must build protobuf models from business models each time + so should be much slower than the test results +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 +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 new file mode 100644 index 00000000000..cb8662c7bfb --- /dev/null +++ b/foundations/foundation-protobuf/pom.xml @@ -0,0 +1,102 @@ + + + + + + foundations + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + foundation-protobuf + Java Chassis::Foundations::Protobuf + + + + org.apache.servicecomb + foundation-common + + + io.protostuff + protostuff-parser + + + io.protostuff + protostuff-runtime + + + io.protostuff + protostuff-core + + + com.google.code.findbugs + jsr305 + + + + + com.google.protobuf + protobuf-java + test + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-protobuf + test + + + org.apache.servicecomb + foundation-test-scaffolding + + + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + ${protobuf-maven-plugin.version} + + com.google.protobuf:protoc:${protoc3-maven-plugin.version}:exe:${os.detected.classifier} + true + grpc-java + io.grpc:protoc-gen-grpc-java:${protoc-gen-grpc-java-plugin.version}:exe:${os.detected.classifier} + src/test/resources + + **/jacksonRoot.proto + **/method.proto + **/model.proto + + + + + generate-test-sources + + test-compile + + + + + + + + diff --git a/foundations/foundation-protobuf/src/main/java/io/protostuff/ByteArrayInputEx.java b/foundations/foundation-protobuf/src/main/java/io/protostuff/ByteArrayInputEx.java new file mode 100644 index 00000000000..f496a709426 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/io/protostuff/ByteArrayInputEx.java @@ -0,0 +1,594 @@ +//======================================================================== +//Copyright 2007-2010 David Yu dyuproject@gmail.com +//------------------------------------------------------------------------ +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +//http://www.apache.org/licenses/LICENSE-2.0 +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +//======================================================================== + +package io.protostuff; + +import static io.protostuff.WireFormat.TAG_TYPE_BITS; +import static io.protostuff.WireFormat.WIRETYPE_END_GROUP; +import static io.protostuff.WireFormat.WIRETYPE_FIXED32; +import static io.protostuff.WireFormat.WIRETYPE_FIXED64; +import static io.protostuff.WireFormat.WIRETYPE_LENGTH_DELIMITED; +import static io.protostuff.WireFormat.WIRETYPE_START_GROUP; +import static io.protostuff.WireFormat.WIRETYPE_VARINT; +import static io.protostuff.WireFormat.getTagFieldNumber; +import static io.protostuff.WireFormat.getTagWireType; +import static io.protostuff.WireFormat.makeTag; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import com.fasterxml.jackson.databind.util.ArrayBuilders; + +import io.protostuff.StringSerializer.STRING; + +/** + * Forked and modified from protostuff + * + * Reads and decodes protocol buffer message fields from an internal byte array buffer. This object is re-usable via + * doing a reset on the byte array position and length. This is used internally by {@link IOUtil} where it catches + * {@link ArrayIndexOutOfBoundsException} when a message is truncated. + * + * @author David Yu + * @created Jun 22, 2010 + */ +public final class ByteArrayInputEx implements InputEx { + private final byte[] buffer; + + private int offset, limit, lastTag = 0; + + private int packedLimit = 0; + + private ArrayBuilders arrayBuilders; + + public ByteArrayInputEx(byte[] buffer) { + this(buffer, 0, buffer.length); + } + + public ByteArrayInputEx(byte[] buffer, int offset, int len) { + this.buffer = buffer; + this.offset = offset; + this.limit = offset + len; + } + + /** + * Return true if currently reading packed field + */ + public boolean isCurrentFieldPacked() { + return packedLimit != 0 && packedLimit != offset; + } + + /** + * Attempt to read a field tag, returning zero if we have reached EOF. Protocol message parsers use this to read + * tags, since a protocol message may legally end wherever a tag occurs, and zero is not a valid tag number. + */ + public int readTag() throws IOException { + if (offset == limit) { + lastTag = 0; + return 0; + } + + final int tag = readRawVarint32(); + if (tag >>> TAG_TYPE_BITS == 0) { + // If we actually read zero, that's not a valid tag. + throw ProtobufException.invalidTag(); + } + lastTag = tag; + return tag; + } + + /** + * Verifies that the last call to readTag() returned the given tag value. This is used to verify that a nested group + * ended with the correct end tag. + * + * @throws ProtobufException + * {@code value} does not match the last tag. + */ + public void checkLastTagWas(final int value) throws ProtobufException { + if (lastTag != value) { + throw ProtobufException.invalidEndTag(); + } + } + + /** + * Reads and discards a single field, given its tag value. + * + * @return {@code false} if the tag is an endgroup tag, in which case nothing is skipped. Otherwise, returns + * {@code true}. + */ + public boolean skipField(final int tag) throws IOException { + switch (getTagWireType(tag)) { + case WIRETYPE_VARINT: + readInt32(); + return true; + case WIRETYPE_FIXED64: + readRawLittleEndian64(); + return true; + case WIRETYPE_LENGTH_DELIMITED: + final int size = readRawVarint32(); + if (size < 0) { + throw ProtobufException.negativeSize(); + } + offset += size; + return true; + case WIRETYPE_START_GROUP: + skipMessage(); + checkLastTagWas(makeTag(getTagFieldNumber(tag), WIRETYPE_END_GROUP)); + return true; + case WIRETYPE_END_GROUP: + return false; + case WIRETYPE_FIXED32: + readRawLittleEndian32(); + return true; + default: + throw ProtobufException.invalidWireType(); + } + } + + /** + * Reads and discards an entire message. This will read either until EOF or until an endgroup tag, whichever comes + * first. + */ + public void skipMessage() throws IOException { + while (true) { + final int tag = readTag(); + if (tag == 0 || !skipField(tag)) { + return; + } + } + } + + @Override + public void handleUnknownField(int fieldNumber) throws IOException { + skipField(lastTag); + } + + @Override + public int readFieldNumber() throws IOException { + if (offset == limit) { + lastTag = 0; + return 0; + } + + // are we reading packed field? + if (isCurrentFieldPacked()) { + if (packedLimit < offset) { + throw ProtobufException.misreportedSize(); + } + + // Return field number while reading packed field + return lastTag >>> TAG_TYPE_BITS; + } + + packedLimit = 0; + final int tag = readRawVarint32(); + final int fieldNumber = tag >>> TAG_TYPE_BITS; + if (fieldNumber == 0) { + // If we actually read zero, that's not a valid tag. + throw ProtobufException.invalidTag(); + } + + lastTag = tag; + return fieldNumber; + } + + /** + * Check if this field have been packed into a length-delimited field. If so, update internal state to reflect that + * packed fields are being read. + * + */ + private void checkIfPackedField() throws IOException { + // Do we have the start of a packed field? + if (packedLimit == 0 && getTagWireType(lastTag) == WIRETYPE_LENGTH_DELIMITED) { + final int length = readRawVarint32(); + if (length < 0) { + throw ProtobufException.negativeSize(); + } + + if (offset + length > limit) { + throw ProtobufException.misreportedSize(); + } + + this.packedLimit = this.offset + length; + } + } + + /** + * Read a {@code double} field value from the internal buffer. + */ + @Override + public double readDouble() { + return Double.longBitsToDouble(readRawLittleEndian64()); + } + + /** + * Read a {@code float} field value from the internal buffer. + */ + @Override + public float readFloat() { + return Float.intBitsToFloat(readRawLittleEndian32()); + } + + /** + * Read a {@code uint64} field value from the internal buffer. + */ + @Override + public long readUInt64() throws IOException { + return readRawVarint64(); + } + + /** + * Read an {@code int64} field value from the internal buffer. + */ + @Override + public long readInt64() throws IOException { + return readRawVarint64(); + } + + /** + * Read an {@code int32} field value from the internal buffer. + */ + @Override + public int readInt32() throws IOException { + return readRawVarint32(); + } + + /** + * Read a {@code fixed64} field value from the internal buffer. + */ + @Override + public long readFixed64() { + return readRawLittleEndian64(); + } + + /** + * Read a {@code fixed32} field value from the internal buffer. + */ + @Override + public int readFixed32() { + return readRawLittleEndian32(); + } + + /** + * Read a {@code bool} field value from the internal buffer. + */ + @Override + public boolean readBool() { + return buffer[offset++] != 0; + } + + /** + * Read a {@code uint32} field value from the internal buffer. + */ + @Override + public int readUInt32() throws IOException { + return readRawVarint32(); + } + + /** + * Read an enum field value from the internal buffer. Caller is responsible for converting the numeric value to an + * actual enum. + */ + @Override + public int readEnum() throws IOException { + return readRawVarint32(); + } + + /** + * Read an {@code sfixed32} field value from the internal buffer. + */ + @Override + public int readSFixed32() { + return readRawLittleEndian32(); + } + + /** + * Read an {@code sfixed64} field value from the internal buffer. + */ + @Override + public long readSFixed64() { + return readRawLittleEndian64(); + } + + /** + * Read an {@code sint32} field value from the internal buffer. + */ + @Override + public int readSInt32() throws IOException { + final int n = readRawVarint32(); + return (n >>> 1) ^ -(n & 1); + } + + /** + * Read an {@code sint64} field value from the internal buffer. + */ + @Override + public long readSInt64() throws IOException { + final long n = readRawVarint64(); + return (n >>> 1) ^ -(n & 1); + } + + @Override + public String readString() throws IOException { + final int length = readRawVarint32(); + if (length < 0) { + throw ProtobufException.negativeSize(); + } + + if (offset + length > limit) { + throw ProtobufException.misreportedSize(); + } + + final int offset = this.offset; + + this.offset += length; + + return STRING.deser(buffer, offset, length); + } + + @Override + public ByteString readBytes() throws IOException { + return ByteString.wrap(readByteArray()); + } + + @Override + public byte[] readByteArray() throws IOException { + final int length = readRawVarint32(); + if (length < 0) { + throw ProtobufException.negativeSize(); + } + + if (offset + length > limit) { + throw ProtobufException.misreportedSize(); + } + + final byte[] copy = new byte[length]; + System.arraycopy(buffer, offset, copy, 0, length); + + offset += length; + + return copy; + } + + @Override + public T mergeObject(T value, final SchemaReader schema) throws IOException { + final int length = readRawVarint32(); + if (length < 0) { + throw ProtobufException.negativeSize(); + } + + // save old limit + final int oldLimit = this.limit; + + this.limit = offset + length; + + if (value == null) { + value = schema.newMessage(); + } + schema.mergeFrom(this, value); + checkLastTagWas(0); + + // restore old limit + this.limit = oldLimit; + + return value; + } + + /** + * Reads a var int 32 from the internal byte buffer. + */ + public int readRawVarint32() throws IOException { + byte tmp = buffer[offset++]; + if (tmp >= 0) { + return tmp; + } + int result = tmp & 0x7f; + if ((tmp = buffer[offset++]) >= 0) { + result |= tmp << 7; + } else { + result |= (tmp & 0x7f) << 7; + if ((tmp = buffer[offset++]) >= 0) { + result |= tmp << 14; + } else { + result |= (tmp & 0x7f) << 14; + if ((tmp = buffer[offset++]) >= 0) { + result |= tmp << 21; + } else { + result |= (tmp & 0x7f) << 21; + result |= (tmp = buffer[offset++]) << 28; + if (tmp < 0) { + // Discard upper 32 bits. + for (int i = 0; i < 5; i++) { + if (buffer[offset++] >= 0) { + return result; + } + } + throw ProtobufException.malformedVarint(); + } + } + } + } + return result; + } + + /** + * Reads a var int 64 from the internal byte buffer. + */ + public long readRawVarint64() throws IOException { + final byte[] buffer = this.buffer; + int offset = this.offset; + + int shift = 0; + long result = 0; + while (shift < 64) { + final byte b = buffer[offset++]; + result |= (long) (b & 0x7F) << shift; + if ((b & 0x80) == 0) { + this.offset = offset; + return result; + } + shift += 7; + } + throw ProtobufException.malformedVarint(); + } + + /** + * Read a 32-bit little-endian integer from the internal buffer. + */ + public int readRawLittleEndian32() { + final byte[] buffer = this.buffer; + int offset = this.offset; + + final byte b1 = buffer[offset++]; + final byte b2 = buffer[offset++]; + final byte b3 = buffer[offset++]; + final byte b4 = buffer[offset++]; + + this.offset = offset; + + return (((int) b1 & 0xff)) | + (((int) b2 & 0xff) << 8) | + (((int) b3 & 0xff) << 16) | + (((int) b4 & 0xff) << 24); + } + + /** + * Read a 64-bit little-endian integer from the internal byte buffer. + */ + public long readRawLittleEndian64() { + final byte[] buffer = this.buffer; + int offset = this.offset; + + final byte b1 = buffer[offset++]; + final byte b2 = buffer[offset++]; + final byte b3 = buffer[offset++]; + final byte b4 = buffer[offset++]; + final byte b5 = buffer[offset++]; + final byte b6 = buffer[offset++]; + final byte b7 = buffer[offset++]; + final byte b8 = buffer[offset++]; + + this.offset = offset; + + return (((long) b1 & 0xff)) | + (((long) b2 & 0xff) << 8) | + (((long) b3 & 0xff) << 16) | + (((long) b4 & 0xff) << 24) | + (((long) b5 & 0xff) << 32) | + (((long) b6 & 0xff) << 40) | + (((long) b7 & 0xff) << 48) | + (((long) b8 & 0xff) << 56); + } + + /** + * Reads a byte array/ByteBuffer value. + */ + @Override + public ByteBuffer readByteBuffer() throws IOException { + return ByteBuffer.wrap(readByteArray()); + } + + @Override + public ArrayBuilders getArrayBuilders() { + if (arrayBuilders == null) { + arrayBuilders = new ArrayBuilders(); + } + return arrayBuilders; + } + + @Override + public int readPackedInt32() throws IOException { + checkIfPackedField(); + return readRawVarint32(); + } + + @Override + public int readPackedUInt32() throws IOException { + checkIfPackedField(); + return readRawVarint32(); + } + + @Override + public int readPackedSInt32() throws IOException { + checkIfPackedField(); + final int n = readRawVarint32(); + return (n >>> 1) ^ -(n & 1); + } + + @Override + public int readPackedFixed32() throws IOException { + checkIfPackedField(); + return readRawLittleEndian32(); + } + + @Override + public int readPackedSFixed32() throws IOException { + checkIfPackedField(); + return readRawLittleEndian32(); + } + + @Override + public long readPackedInt64() throws IOException { + checkIfPackedField(); + return readRawVarint64(); + } + + @Override + public long readPackedUInt64() throws IOException { + checkIfPackedField(); + return readRawVarint64(); + } + + @Override + public long readPackedSInt64() throws IOException { + checkIfPackedField(); + final long n = readRawVarint64(); + return (n >>> 1) ^ -(n & 1); + } + + @Override + public long readPackedFixed64() throws IOException { + checkIfPackedField(); + return readRawLittleEndian64(); + } + + @Override + public long readPackedSFixed64() throws IOException { + checkIfPackedField(); + return readRawLittleEndian64(); + } + + @Override + public float readPackedFloat() throws IOException { + checkIfPackedField(); + return Float.intBitsToFloat(readRawLittleEndian32()); + } + + @Override + public double readPackedDouble() throws IOException { + checkIfPackedField(); + return Double.longBitsToDouble(readRawLittleEndian64()); + } + + @Override + public boolean readPackedBool() throws IOException { + checkIfPackedField(); + return buffer[offset++] != 0; + } + + @Override + public int readPackedEnum() throws IOException { + checkIfPackedField(); + return readRawVarint32(); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/io/protostuff/InputEx.java b/foundations/foundation-protobuf/src/main/java/io/protostuff/InputEx.java new file mode 100644 index 00000000000..c2dbe1b30d3 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/io/protostuff/InputEx.java @@ -0,0 +1,164 @@ +//======================================================================== +//Copyright 2007-2009 David Yu dyuproject@gmail.com +//------------------------------------------------------------------------ +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +//http://www.apache.org/licenses/LICENSE-2.0 +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +//======================================================================== + +package io.protostuff; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import com.fasterxml.jackson.databind.util.ArrayBuilders; + +/** + * Forked and modified from protostuff
+ * + * An Input lets an application read primitive data types and objects from a source of data. + * + * @author David Yu + * @created Nov 9, 2009 + */ +public interface InputEx { + + /** + * The underlying implementation should handle the unknown field. + */ + void handleUnknownField(int fieldNumber) throws IOException; + + /** + * Reads field number. + */ + int readFieldNumber() throws IOException; + + /** + * Reads a variable int field value. + */ + int readInt32() throws IOException; + + /** + * Reads an unsigned int field value. + */ + int readUInt32() throws IOException; + + /** + * Reads a signed int field value. + */ + int readSInt32() throws IOException; + + /** + * Reads a fixed int(4 bytes) field value. + */ + int readFixed32() throws IOException; + + /** + * Reads a signed+fixed int(4 bytes) field value. + */ + int readSFixed32() throws IOException; + + /** + * Reads a variable long field value. + */ + long readInt64() throws IOException; + + /** + * Reads an unsigned long field value. + */ + long readUInt64() throws IOException; + + /** + * Reads a signed long field value. + */ + long readSInt64() throws IOException; + + /** + * Reads a fixed long(8 bytes) field value. + */ + long readFixed64() throws IOException; + + /** + * Reads a signed+fixed long(8 bytes) field value. + */ + long readSFixed64() throws IOException; + + /** + * Reads a float field value. + */ + float readFloat() throws IOException; + + /** + * Reads a double field value. + */ + double readDouble() throws IOException; + + /** + * Reads a boolean field value. + */ + boolean readBool() throws IOException; + + /** + * Reads an enum(its number) field value. + */ + int readEnum() throws IOException; + + /** + * Reads a {@link String} field value. + */ + String readString() throws IOException; + + /** + * Reads a {@link ByteString} field value. + */ + ByteString readBytes() throws IOException; + + /** + * Reads a byte array field value. + */ + byte[] readByteArray() throws IOException; + + ByteBuffer readByteBuffer() throws IOException; + + /** + * Merges an object(with schema) field value. The provided {@link Schema schema} handles the deserialization for the + * object. + */ + T mergeObject(T value, SchemaReader schema) throws IOException; + + ArrayBuilders getArrayBuilders(); + + int readPackedInt32() throws IOException; + + int readPackedUInt32() throws IOException; + + int readPackedSInt32() throws IOException; + + int readPackedFixed32() throws IOException; + + int readPackedSFixed32() throws IOException; + + long readPackedInt64() throws IOException; + + long readPackedUInt64() throws IOException; + + long readPackedSInt64() throws IOException; + + long readPackedFixed64() throws IOException; + + long readPackedSFixed64() throws IOException; + + float readPackedFloat() throws IOException; + + double readPackedDouble() throws IOException; + + boolean readPackedBool() throws IOException; + + int readPackedEnum() throws IOException; +} diff --git a/foundations/foundation-protobuf/src/main/java/io/protostuff/OutputEx.java b/foundations/foundation-protobuf/src/main/java/io/protostuff/OutputEx.java new file mode 100644 index 00000000000..aefd1bfcc7d --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/io/protostuff/OutputEx.java @@ -0,0 +1,210 @@ +//======================================================================== +//Copyright 2007-2009 David Yu dyuproject@gmail.com +//------------------------------------------------------------------------ +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +//http://www.apache.org/licenses/LICENSE-2.0 +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +//======================================================================== + +package io.protostuff; + +import static io.protostuff.ProtobufOutputEx.encodeZigZag64; + +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + *
+ * Forked and modified from protostuff
+ * difference for scalar types:
+ *                   OutputEx         OutputEx
+ * writeXXX          all scenes     repeated not packed, write with tag, not ignore default value
+ * writePackedXXX    ---            repeated packed, write without tag, not ignore default value
+ * writeScalarXXX    ---            not repeated field, not write default value
+ * 
+ * + * An OutputEx lets an application write primitive data types and objects to a sink of data. + * + * @author David Yu + * @created Nov 9, 2009 + */ +public interface OutputEx { + + /** + * Writes a variable int field. + */ + void writeInt32(int tag, int tagSize, int value) throws IOException; + + /** + * Writes an unsigned int field. + */ + void writeUInt32(int tag, int tagSize, int value) throws IOException; + + /** + * Writes a signed int field. + */ + void writeSInt32(int tag, int tagSize, int value) throws IOException; + + /** + * Writes a fixed int(4 bytes) field. + */ + void writeFixed32(int tag, int tagSize, int value) throws IOException; + + /** + * Writes a signed+fixed int(4 bytes) field. + */ + void writeSFixed32(int tag, int tagSize, int value) throws IOException; + + /** + * Writes a variable long field. + */ + void writeInt64(int tag, int tagSize, long value) throws IOException; + + /** + * Writes an unsigned long field. + */ + void writeUInt64(int tag, int tagSize, long value) throws IOException; + + /** + * Writes a signed long field. + */ + void writeSInt64(int tag, int tagSize, long value) throws IOException; + + /** + * Writes a fixed long(8 bytes) field. + */ + void writeFixed64(int tag, int tagSize, long value) throws IOException; + + /** + * Writes a signed+fixed long(8 bytes) field. + */ + void writeSFixed64(int tag, int tagSize, long value) throws IOException; + + /** + * Writes a float field. + */ + void writeFloat(int tag, int tagSize, float value) throws IOException; + + /** + * Writes a double field. + */ + void writeDouble(int tag, int tagSize, double value) throws IOException; + + /** + * Writes a boolean field. + */ + void writeBool(int tag, int tagSize, boolean value) throws IOException; + + /** + * Writes a enum(its number) field. + */ + void writeEnum(int tag, int tagSize, int value) throws IOException; + + /** + * Writes a String field. + */ + void writeString(int tag, int tagSize, String value) throws IOException; + + /** + * Writes a ByteString(wraps byte array) field. + */ + void writeBytes(int tag, int tagSize, ByteString value) throws IOException; + + /** + * Writes a byte array field. + */ + void writeByteArray(int tag, int tagSize, byte[] value) throws IOException; + + /** + * Writes a binary or a pre-encoded utf8 string. + */ + void writeByteRange(boolean utf8String, int tag, int tagSize, byte[] value, int offset, int length) + throws IOException; + + /** + * Writes an object(using its schema) field. + */ + void writeObject(int tag, int tagSize, T value, SchemaWriter schemaWriter) throws IOException; + + void writeBytes(int tag, int tagSize, ByteBuffer value) throws IOException; + + byte[] toByteArray(); + + void writePackedInt32(int value) throws IOException; + + void writeScalarInt32(int tag, int tagSize, int value) throws IOException; + + default void writePackedInt64(long value) throws IOException { + writePackedUInt64(value); + } + + void writeScalarInt64(int tag, int tagSize, long value) throws IOException; + + void writePackedUInt32(int value) throws IOException; + + void writeScalarUInt32(int tag, int tagSize, int value) throws IOException; + + void writePackedUInt64(long value) throws IOException; + + void writeScalarUInt64(int tag, int tagSize, long value) throws IOException; + + void writePackedSInt32(int value) throws IOException; + + void writeScalarSInt32(int tag, int tagSize, int value) throws IOException; + + default void writePackedSInt64(long value) throws IOException { + writePackedUInt64(encodeZigZag64(value)); + } + + void writeScalarSInt64(int tag, int tagSize, long value) throws IOException; + + void writePackedFixed32(int value) throws IOException; + + void writeScalarFixed32(int tag, int tagSize, int value) throws IOException; + + void writePackedFixed64(long value) throws IOException; + + void writeScalarFixed64(int tag, int tagSize, long value) throws IOException; + + default void writePackedSFixed32(int value) throws IOException { + writePackedFixed32(value); + } + + void writeScalarSFixed32(int tag, int tagSize, int value) throws IOException; + + default void writePackedSFixed64(long value) throws IOException { + writePackedFixed64(value); + } + + void writeScalarSFixed64(int tag, int tagSize, long value) throws IOException; + + default void writePackedFloat(float value) throws IOException { + writePackedFixed32(Float.floatToRawIntBits(value)); + } + + void writeScalarFloat(int tag, int tagSize, float value) throws IOException; + + default void writePackedDouble(double value) throws IOException { + writePackedFixed64(Double.doubleToRawLongBits(value)); + } + + void writeScalarDouble(int tag, int tagSize, double value) throws IOException; + + void writePackedBool(boolean value) throws IOException; + + void writeScalarBool(int tag, int tagSize, boolean value) throws IOException; + + default void writePackedEnum(int value) throws IOException { + writePackedInt32(value); + } + + void writeScalarEnum(int tag, int tagSize, int value) throws IOException; + + void writeScalarString(int tag, int tagSize, String value) throws IOException; +} diff --git a/foundations/foundation-protobuf/src/main/java/io/protostuff/ProtobufOutputEx.java b/foundations/foundation-protobuf/src/main/java/io/protostuff/ProtobufOutputEx.java new file mode 100644 index 00000000000..8c47ab44271 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/io/protostuff/ProtobufOutputEx.java @@ -0,0 +1,854 @@ +//======================================================================== +//Copyright 2007-2010 David Yu dyuproject@gmail.com +//------------------------------------------------------------------------ +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +//http://www.apache.org/licenses/LICENSE-2.0 +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +//======================================================================== + +package io.protostuff; + +import static io.protostuff.StringSerializer.writeUTF8VarDelimited; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +/** + * Forked and modified from protostuff
+ * + * Protobuf serialization where the messages must be fully buffered on memory before it can be written to the socket ( + * {@link OutputStream}). + * + * @author David Yu + * @created May 18, 2010 + */ +public final class ProtobufOutputEx extends WriteSession implements OutputEx { + public static final int LITTLE_ENDIAN_32_SIZE = 4, LITTLE_ENDIAN_64_SIZE = 8; + + public ProtobufOutputEx() { + super(LinkedBuffer.allocate()); + } + + public ProtobufOutputEx(LinkedBuffer buffer) { + super(buffer); + } + + public ProtobufOutputEx(LinkedBuffer buffer, int nextBufferSize) { + super(buffer, nextBufferSize); + } + + /** + * Resets this output for re-use. + */ + @Override + public ProtobufOutputEx clear() { + super.clear(); + return this; + } + + @Override + public void writeInt32(int tag, int tagSize, int value) { + if (value < 0) { + tail = writeTagAndRawVarInt64(tag, tagSize, value, this, tail); + return; + } + + tail = writeTagAndRawVarInt32(tag, tagSize, value, this, tail); + } + + @Override + public void writeUInt32(int tag, int tagSize, int value) { + tail = writeTagAndRawVarInt32(tag, tagSize, value, this, tail); + } + + @Override + public void writeSInt32(int tag, int tagSize, int value) { + tail = writeTagAndRawVarInt32(tag, tagSize, encodeZigZag32(value), this, tail); + } + + @Override + public void writeFixed32(int tag, int tagSize, int value) { + tail = writeTagAndRawLittleEndian32(tag, tagSize, value, this, tail); + } + + @Override + public void writeSFixed32(int tag, int tagSize, int value) { + tail = writeTagAndRawLittleEndian32(tag, tagSize, value, this, tail); + } + + @Override + public void writeInt64(int tag, int tagSize, long value) { + tail = writeTagAndRawVarInt64(tag, tagSize, value, this, tail); + } + + @Override + public void writeUInt64(int tag, int tagSize, long value) { + tail = writeTagAndRawVarInt64(tag, tagSize, value, this, tail); + } + + @Override + public void writeSInt64(int tag, int tagSize, long value) { + tail = writeTagAndRawVarInt64(tag, tagSize, encodeZigZag64(value), this, tail); + } + + @Override + public void writeFixed64(int tag, int tagSize, long value) { + tail = writeTagAndRawLittleEndian64(tag, tagSize, value, this, tail); + } + + @Override + public void writeSFixed64(int tag, int tagSize, long value) { + tail = writeTagAndRawLittleEndian64(tag, tagSize, value, this, tail); + } + + @Override + public void writeFloat(int tag, int tagSize, float value) { + tail = writeTagAndRawLittleEndian32(tag, tagSize, Float.floatToRawIntBits(value), this, tail); + } + + @Override + public void writeDouble(int tag, int tagSize, double value) { + tail = writeTagAndRawLittleEndian64(tag, tagSize, Double.doubleToRawLongBits(value), this, tail); + } + + @Override + public void writeBool(int tag, int tagSize, boolean value) { + tail = writeTagAndRawVarInt32(tag, tagSize, value ? 1 : 0, this, tail); + } + + @Override + public void writeEnum(int tag, int tagSize, int number) { + writeInt32(tag, tagSize, number); + } + + @Override + public void writeString(int tag, int tagSize, String value) { + tail = writeUTF8VarDelimited( + value, + this, + writeRawVarInt32(tag, this, tail)); + } + + @Override + public void writeBytes(int tag, int tagSize, ByteString value) { + writeByteArray(tag, tagSize, value.getBytes()); + } + + @Override + public void writeByteArray(int tag, int tagSize, byte[] bytes) { + tail = writeTagAndByteArray( + tag, tagSize, + bytes, 0, bytes.length, + this, + tail); + } + + @Override + public void writeByteRange(boolean utf8String, int tag, int tagSize, byte[] value, int offset, int length) { + tail = writeTagAndByteArray( + tag, tagSize, + value, offset, length, + this, + tail); + } + + @Override + public void writeObject(final int tag, int tagSize, final T value, final SchemaWriter schemaWriter) + throws IOException { + final LinkedBuffer lastBuffer; + + // write the tag + if (tagSize == 1 && tail.offset != tail.buffer.length) { + lastBuffer = tail; + size++; + lastBuffer.buffer[lastBuffer.offset++] = (byte) tag; + } else { + tail = lastBuffer = writeRawVarInt32(tag, this, tail); + } + + final int lastOffset = tail.offset, lastSize = size; + + if (lastOffset == lastBuffer.buffer.length) { + // not enough size for the 1-byte delimiter + final LinkedBuffer nextBuffer = new LinkedBuffer(nextBufferSize); + // new buffer for the content + tail = nextBuffer; + + schemaWriter.writeTo(this, value); + + final int msgSize = size - lastSize; + + final byte[] delimited = new byte[computeRawVarint32Size(msgSize)]; + writeRawVarInt32(msgSize, delimited, 0); + + size += delimited.length; + + // wrap the byte array (delimited) and insert between the two buffers + new LinkedBuffer(delimited, 0, delimited.length, lastBuffer).next = nextBuffer; + return; + } + + // we have enough space for the 1-byte delim + lastBuffer.offset++; + size++; + + schemaWriter.writeTo(this, value); + + final int msgSize = size - lastSize - 1; + + // optimize for small messages + if (msgSize < 128) { + // fits + lastBuffer.buffer[lastOffset] = (byte) msgSize; + return; + } + + // split into two buffers + + // the second buffer (contains the message contents) + final LinkedBuffer view = new LinkedBuffer(lastBuffer.buffer, + lastOffset + 1, lastBuffer.offset); + + if (lastBuffer == tail) { + tail = view; + } else { + view.next = lastBuffer.next; + } + + // the first buffer (contains the tag) + lastBuffer.offset = lastOffset; + + final byte[] delimited = new byte[computeRawVarint32Size(msgSize)]; + writeRawVarInt32(msgSize, delimited, 0); + + // add the difference + size += (delimited.length - 1); + + // wrap the byte array (delimited) and insert between the two buffers + new LinkedBuffer(delimited, 0, delimited.length, lastBuffer).next = view; + } + + /* + * Write the nested message encoded as group. + * + * void writeObjectEncodedAsGroup(final int fieldNumber, final T value, final SchemaEx schema, final boolean + * repeated) throws IOException { tail = writeRawVarInt32( makeTag(fieldNumber, WIRETYPE_START_GROUP), this, tail); + * + * schema.writeTo(this, value); + * + * tail = writeRawVarInt32( makeTag(fieldNumber, WIRETYPE_END_GROUP), this, tail); } + */ + + /* ----------------------------------------------------------------- */ + + /** + * Returns the buffer encoded with the variable int 32. + */ + public static LinkedBuffer writeRawVarInt32(int value, final WriteSession session, + LinkedBuffer lb) { + final int size = computeRawVarint32Size(value); + + if (lb.offset + size > lb.buffer.length) { + lb = new LinkedBuffer(session.nextBufferSize, lb); + } + + final byte[] buffer = lb.buffer; + int offset = lb.offset; + lb.offset += size; + session.size += size; + + if (size == 1) { + buffer[offset] = (byte) value; + } else { + for (int i = 0, last = size - 1; i < last; i++, value >>>= 7) { + buffer[offset++] = (byte) ((value & 0x7F) | 0x80); + } + + buffer[offset] = (byte) value; + } + + return lb; + } + + /** + * Returns the buffer encoded with the tag and byte array + */ + public static LinkedBuffer writeTagAndByteArray(int tag, int tagSize, final byte[] value, + int offset, int valueLen, + final WriteSession session, LinkedBuffer lb) { + if (valueLen == 0) { + // write only the tag and delimiter + return writeTagAndRawVarInt32(tag, tagSize, valueLen, session, lb); + } + + lb = writeTagAndRawVarInt32(tag, tagSize, valueLen, session, lb); + + session.size += valueLen; + + final int available = lb.buffer.length - lb.offset; + if (valueLen > available) { + if (available + session.nextBufferSize < valueLen) { + // too large ... so we wrap and insert (zero-copy) + if (available == 0) { + // buffer was actually full ... return a fresh buffer + return new LinkedBuffer(session.nextBufferSize, + new LinkedBuffer(value, offset, offset + valueLen, lb)); + } + + // continue with the existing byte array of the previous buffer + return new LinkedBuffer(lb, + new LinkedBuffer(value, offset, offset + valueLen, lb)); + } + + // copy what can fit + System.arraycopy(value, offset, lb.buffer, lb.offset, available); + + lb.offset += available; + + // grow + lb = new LinkedBuffer(session.nextBufferSize, lb); + + final int leftover = valueLen - available; + + // copy what's left + System.arraycopy(value, offset + available, lb.buffer, 0, leftover); + + lb.offset += leftover; + + return lb; + } + + // it fits + System.arraycopy(value, offset, lb.buffer, lb.offset, valueLen); + + lb.offset += valueLen; + + return lb; + } + + /** + * Returns the buffer encoded with the tag and var int 32 + */ + public static LinkedBuffer writeTagAndRawVarInt32(int tag, int tagSize, int value, + final WriteSession session, LinkedBuffer lb) { + final int size = computeRawVarint32Size(value); + final int totalSize = tagSize + size; + + if (lb.offset + totalSize > lb.buffer.length) { + lb = new LinkedBuffer(session.nextBufferSize, lb); + } + + final byte[] buffer = lb.buffer; + int offset = lb.offset; + lb.offset += totalSize; + session.size += totalSize; + + if (tagSize == 1) { + buffer[offset++] = (byte) tag; + } else { + for (int i = 0, last = tagSize - 1; i < last; i++, tag >>>= 7) { + buffer[offset++] = (byte) ((tag & 0x7F) | 0x80); + } + + buffer[offset++] = (byte) tag; + } + + if (size == 1) { + buffer[offset] = (byte) value; + } else { + for (int i = 0, last = size - 1; i < last; i++, value >>>= 7) { + buffer[offset++] = (byte) ((value & 0x7F) | 0x80); + } + + buffer[offset] = (byte) value; + } + + return lb; + } + + /** + * Returns the buffer encoded with the tag and var int 64 + */ + public static LinkedBuffer writeTagAndRawVarInt64(int tag, int tagSize, long value, + final WriteSession session, LinkedBuffer lb) { + final int size = computeRawVarint64Size(value); + final int totalSize = tagSize + size; + + if (lb.offset + totalSize > lb.buffer.length) { + lb = new LinkedBuffer(session.nextBufferSize, lb); + } + + final byte[] buffer = lb.buffer; + int offset = lb.offset; + lb.offset += totalSize; + session.size += totalSize; + + if (tagSize == 1) { + buffer[offset++] = (byte) tag; + } else { + for (int i = 0, last = tagSize - 1; i < last; i++, tag >>>= 7) { + buffer[offset++] = (byte) ((tag & 0x7F) | 0x80); + } + + buffer[offset++] = (byte) tag; + } + + if (size == 1) { + buffer[offset] = (byte) value; + } else { + for (int i = 0, last = size - 1; i < last; i++, value >>>= 7) { + buffer[offset++] = (byte) (((int) value & 0x7F) | 0x80); + } + + buffer[offset] = (byte) value; + } + + return lb; + } + + /** + * Returns the buffer encoded with the tag and little endian 32 + */ + public static LinkedBuffer writeTagAndRawLittleEndian32(int tag, int tagSize, int value, + final WriteSession session, LinkedBuffer lb) { + final int totalSize = tagSize + LITTLE_ENDIAN_32_SIZE; + + if (lb.offset + totalSize > lb.buffer.length) { + lb = new LinkedBuffer(session.nextBufferSize, lb); + } + + final byte[] buffer = lb.buffer; + int offset = lb.offset; + lb.offset += totalSize; + session.size += totalSize; + + if (tagSize == 1) { + buffer[offset++] = (byte) tag; + } else { + for (int i = 0, last = tagSize - 1; i < last; i++, tag >>>= 7) { + buffer[offset++] = (byte) ((tag & 0x7F) | 0x80); + } + + buffer[offset++] = (byte) tag; + } + + writeRawLittleEndian32(value, buffer, offset); + + return lb; + } + + /** + * Returns the buffer encoded with the tag and little endian 64 + */ + public static LinkedBuffer writeTagAndRawLittleEndian64(int tag, int tagSize, long value, + final WriteSession session, LinkedBuffer lb) { + final int totalSize = tagSize + LITTLE_ENDIAN_64_SIZE; + + if (lb.offset + totalSize > lb.buffer.length) { + lb = new LinkedBuffer(session.nextBufferSize, lb); + } + + final byte[] buffer = lb.buffer; + int offset = lb.offset; + lb.offset += totalSize; + session.size += totalSize; + + if (tagSize == 1) { + buffer[offset++] = (byte) tag; + } else { + for (int i = 0, last = tagSize - 1; i < last; i++, tag >>>= 7) { + buffer[offset++] = (byte) ((tag & 0x7F) | 0x80); + } + + buffer[offset++] = (byte) tag; + } + + writeRawLittleEndian64(value, buffer, offset); + + return lb; + } + + /** Encode and write a varint to the byte array */ + public static void writeRawVarInt32(int value, final byte[] buf, int offset) throws IOException { + while (true) { + if ((value & ~0x7F) == 0) { + buf[offset] = (byte) value; + return; + } else { + buf[offset++] = (byte) ((value & 0x7F) | 0x80); + value >>>= 7; + } + } + } + + /** + * Writes the encoded little endian 32 and returns the bytes written + */ + public static int writeRawLittleEndian32(int value, byte[] buffer, int offset) { + if (buffer.length - offset < LITTLE_ENDIAN_32_SIZE) { + throw new IllegalArgumentException("buffer capacity not enough."); + } + + buffer[offset++] = (byte) (value & 0xFF); + buffer[offset++] = (byte) (value >> 8 & 0xFF); + buffer[offset++] = (byte) (value >> 16 & 0xFF); + buffer[offset] = (byte) (value >> 24 & 0xFF); + + return LITTLE_ENDIAN_32_SIZE; + } + + /** + * Writes the encoded little endian 64 and returns the bytes written + */ + public static int writeRawLittleEndian64(long value, byte[] buffer, int offset) { + if (buffer.length - offset < LITTLE_ENDIAN_64_SIZE) { + throw new IllegalArgumentException("buffer capacity not enough."); + } + + buffer[offset++] = (byte) (value & 0xFF); + buffer[offset++] = (byte) (value >> 8 & 0xFF); + buffer[offset++] = (byte) (value >> 16 & 0xFF); + buffer[offset++] = (byte) (value >> 24 & 0xFF); + buffer[offset++] = (byte) (value >> 32 & 0xFF); + buffer[offset++] = (byte) (value >> 40 & 0xFF); + buffer[offset++] = (byte) (value >> 48 & 0xFF); + buffer[offset] = (byte) (value >> 56 & 0xFF); + + return LITTLE_ENDIAN_64_SIZE; + } + + /* METHODS FROM CodedOutput */ + + // Protocol Buffers - Google's data interchange format + // Copyright 2008 Google Inc. All rights reserved. + // http://code.google.com/p/protobuf/ + // + // 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. + + /** + * Compute the number of bytes that would be needed to encode a varint. {@code value} is treated as unsigned, so it + * won't be sign-extended if negative. + */ + public static int computeRawVarint32Size(final int value) { + if ((value & (0xffffffff << 7)) == 0) { + return 1; + } + if ((value & (0xffffffff << 14)) == 0) { + return 2; + } + if ((value & (0xffffffff << 21)) == 0) { + return 3; + } + if ((value & (0xffffffff << 28)) == 0) { + return 4; + } + return 5; + } + + /** + * Compute the number of bytes that would be needed to encode a varint. + */ + public static int computeRawVarint64Size(final long value) { + if ((value & (0xffffffffffffffffL << 7)) == 0) { + return 1; + } + if ((value & (0xffffffffffffffffL << 14)) == 0) { + return 2; + } + if ((value & (0xffffffffffffffffL << 21)) == 0) { + return 3; + } + if ((value & (0xffffffffffffffffL << 28)) == 0) { + return 4; + } + if ((value & (0xffffffffffffffffL << 35)) == 0) { + return 5; + } + if ((value & (0xffffffffffffffffL << 42)) == 0) { + return 6; + } + if ((value & (0xffffffffffffffffL << 49)) == 0) { + return 7; + } + if ((value & (0xffffffffffffffffL << 56)) == 0) { + return 8; + } + if ((value & (0xffffffffffffffffL << 63)) == 0) { + return 9; + } + return 10; + } + + /** + * Encode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers into values that can be efficiently encoded + * with varint. (Otherwise, negative values must be sign-extended to 64 bits to be varint encoded, thus always + * taking 10 bytes on the wire.) + * + * @param n + * A signed 32-bit integer. + * @return An unsigned 32-bit integer, stored in a signed int because Java has no explicit unsigned support. + */ + public static int encodeZigZag32(final int n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 31); + } + + /** + * Encode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers into values that can be efficiently encoded + * with varint. (Otherwise, negative values must be sign-extended to 64 bits to be varint encoded, thus always + * taking 10 bytes on the wire.) + * + * @param n + * A signed 64-bit integer. + * @return An unsigned 64-bit integer, stored in a signed int because Java has no explicit unsigned support. + */ + public static long encodeZigZag64(final long n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 63); + } + + /** + * Writes a ByteBuffer field. + */ + @Override + public void writeBytes(int tag, int tagSize, ByteBuffer value) { + writeByteRange(false, tag, tagSize, value.array(), value.arrayOffset() + value.position(), value.remaining()); + } + + public void toOutputStream(OutputStream outputStream) throws IOException { + LinkedBuffer.writeTo(outputStream, head); + } + + @Override + public void writePackedInt32(int value) { + if (value >= 0) { + tail = writeRawVarInt32(value, this, tail); + return; + } + + writePackedUInt64(value); + } + + @Override + public void writeScalarInt32(int tag, int tagSize, int value) { + if (value != 0) { + writeInt32(tag, tagSize, value); + } + } + + @Override + public void writeScalarInt64(int tag, int tagSize, long value) { + if (value != 0) { + writeInt64(tag, tagSize, value); + } + } + + @Override + public void writePackedUInt32(int value) { + tail = writeRawVarInt32(value, this, tail); + } + + @Override + public void writeScalarUInt32(int tag, int tagSize, int value) { + if (value != 0) { + writeUInt32(tag, tagSize, value); + } + } + + @Override + public void writePackedUInt64(long value) { + final int size = computeRawVarint64Size(value); + + if (tail.offset + size > tail.buffer.length) { + tail = new LinkedBuffer(this.nextBufferSize, tail); + } + + final byte[] buffer = tail.buffer; + int offset = tail.offset; + tail.offset += size; + this.size += size; + + if (size == 1) { + buffer[offset] = (byte) value; + } else { + for (int i = 0, last = size - 1; i < last; i++, value >>>= 7) { + buffer[offset++] = (byte) (((int) value & 0x7F) | 0x80); + } + + buffer[offset] = (byte) value; + } + } + + @Override + public void writeScalarUInt64(int tag, int tagSize, long value) { + if (value != 0) { + writeUInt64(tag, tagSize, value); + } + } + + @Override + public void writePackedSInt32(int value) { + tail = writeRawVarInt32(encodeZigZag32(value), this, tail); + } + + @Override + public void writeScalarSInt32(int tag, int tagSize, int value) { + if (value != 0) { + writeSInt32(tag, tagSize, value); + } + } + + @Override + public void writeScalarSInt64(int tag, int tagSize, long value) { + if (value != 0) { + writeSInt64(tag, tagSize, value); + } + } + + @Override + public void writePackedFixed32(int value) { + final int size = LITTLE_ENDIAN_32_SIZE; + + if (tail.offset + size > tail.buffer.length) { + tail = new LinkedBuffer(this.nextBufferSize, tail); + } + + final byte[] buffer = tail.buffer; + int offset = tail.offset; + tail.offset += size; + this.size += size; + + buffer[offset++] = (byte) (value & 0xFF); + buffer[offset++] = (byte) (value >> 8 & 0xFF); + buffer[offset++] = (byte) (value >> 16 & 0xFF); + buffer[offset] = (byte) (value >> 24 & 0xFF); + } + + @Override + public void writeScalarFixed32(int tag, int tagSize, int value) { + if (value != 0) { + writeFixed32(tag, tagSize, value); + } + } + + @Override + public void writePackedFixed64(long value) { + final int size = LITTLE_ENDIAN_64_SIZE; + + if (tail.offset + size > tail.buffer.length) { + tail = new LinkedBuffer(this.nextBufferSize, tail); + } + + final byte[] buffer = tail.buffer; + int offset = tail.offset; + tail.offset += size; + this.size += size; + + buffer[offset++] = (byte) (value & 0xFF); + buffer[offset++] = (byte) (value >> 8 & 0xFF); + buffer[offset++] = (byte) (value >> 16 & 0xFF); + buffer[offset++] = (byte) (value >> 24 & 0xFF); + buffer[offset++] = (byte) (value >> 32 & 0xFF); + buffer[offset++] = (byte) (value >> 40 & 0xFF); + buffer[offset++] = (byte) (value >> 48 & 0xFF); + buffer[offset] = (byte) (value >> 56 & 0xFF); + } + + @Override + public void writeScalarFixed64(int tag, int tagSize, long value) { + if (value != 0) { + writeFixed64(tag, tagSize, value); + } + } + + @Override + public void writeScalarSFixed32(int tag, int tagSize, int value) { + if (value != 0) { + writeSFixed32(tag, tagSize, value); + } + } + + @Override + public void writeScalarSFixed64(int tag, int tagSize, long value) { + if (value != 0) { + writeSFixed64(tag, tagSize, value); + } + } + + @Override + public void writeScalarFloat(int tag, int tagSize, float value) { + if (value != 0) { + writeFloat(tag, tagSize, value); + } + } + + @Override + public void writeScalarDouble(int tag, int tagSize, double value) { + if (value != 0) { + writeDouble(tag, tagSize, value); + } + } + + @Override + public void writePackedBool(boolean value) { + final int size = 1; + + if (tail.offset + size > tail.buffer.length) { + tail = new LinkedBuffer(this.nextBufferSize, tail); + } + + final byte[] buffer = tail.buffer; + int offset = tail.offset; + tail.offset += size; + this.size += size; + + buffer[offset] = (byte) (value ? 1 : 0); + } + + @Override + public void writeScalarBool(int tag, int tagSize, boolean value) { + if (value) { + writeBool(tag, tagSize, value); + } + } + + @Override + public void writeScalarEnum(int tag, int tagSize, int value) { + writeScalarInt32(tag, tagSize, value); + } + + public void writeScalarString(int tag, int tagSize, String value) { + writeString(tag, tagSize, value); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaEx.java b/foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaEx.java new file mode 100644 index 00000000000..15c22e7fd51 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaEx.java @@ -0,0 +1,40 @@ +//======================================================================== +//Copyright 2007-2009 David Yu dyuproject@gmail.com +//------------------------------------------------------------------------ +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +//http://www.apache.org/licenses/LICENSE-2.0 +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +//======================================================================== + +package io.protostuff; + +/** + * Forked and modified from protostuff + * + * Handles the serialization and deserialization of a message/object tied to this. + *

+ * Basically, any object can be serialized via protobuf. As long as its schema is provided, it does not need to + * implement {@link Message}. This was designed with "unobtrusive" in mind. The goal was to be able to + * serialize/deserialize any existing object without having to touch its source. This will enable you to customize the + * serialization of objects from 3rd party libraries. + * + * @author David Yu + * @created Nov 9, 2009 + */ +public interface SchemaEx extends SchemaWriter, SchemaReader { + /** + * Returns the simple name of the message tied to this schema. Allows custom schemas to provide a custom name other + * than typeClass().getSimpleName(); + */ + default String messageName() { + throw new UnsupportedOperationException(); + } + + void init(); +} diff --git a/foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaReader.java b/foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaReader.java new file mode 100644 index 00000000000..c77db0c9730 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaReader.java @@ -0,0 +1,28 @@ +//======================================================================== +//Copyright 2007-2009 David Yu dyuproject@gmail.com +//------------------------------------------------------------------------ +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +//http://www.apache.org/licenses/LICENSE-2.0 +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +//======================================================================== + +package io.protostuff; + +import java.io.IOException; + +public interface SchemaReader { + /** + * Creates the message/object tied to this schema. + */ + default T newMessage() { + return null; + } + + void mergeFrom(InputEx input, T message) throws IOException; +} diff --git a/foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaWriter.java b/foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaWriter.java new file mode 100644 index 00000000000..b7842bb01ae --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaWriter.java @@ -0,0 +1,21 @@ +//======================================================================== +//Copyright 2007-2009 David Yu dyuproject@gmail.com +//------------------------------------------------------------------------ +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +//http://www.apache.org/licenses/LICENSE-2.0 +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +//======================================================================== + +package io.protostuff; + +import java.io.IOException; + +public interface SchemaWriter { + void writeTo(OutputEx output, T value) throws IOException; +} diff --git a/foundations/foundation-protobuf/src/main/java/io/protostuff/package-info.java b/foundations/foundation-protobuf/src/main/java/io/protostuff/package-info.java new file mode 100644 index 00000000000..fad90514b8d --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/io/protostuff/package-info.java @@ -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. + */ + +/** + *

+ * classes in protostuff are "final" or not all suitable for ServiceComb
+ *   1.final
+ *     io.protostuff.ProtobufOutputEx
+ *     io.protostuff.ByteArrayInput
+ *   2.not suitable
+ *     1) io.protostuff.OutputEx
+ *        fieldNumber changed to tag, avoid makeTag every time
+ *        not support write packed value
+ *        not support ignore default scalar value
+ *     2) io.protostuff.SchemaEx
+ *        make it simpler
+ *     3) codec of map is not compatible to protobuf
+ * so we must copy and modified them
+ * 
+ */ + +package io.protostuff; diff --git a/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/ArrayFieldMapEx.java b/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/ArrayFieldMapEx.java new file mode 100644 index 00000000000..b36a176dad4 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/ArrayFieldMapEx.java @@ -0,0 +1,94 @@ +//======================================================================== +//Copyright 2007-2010 David Yu dyuproject@gmail.com +//------------------------------------------------------------------------ +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +//http://www.apache.org/licenses/LICENSE-2.0 +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +//======================================================================== + +package io.protostuff.runtime; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Forked and modified from protostuff + * + * Field mapping implemented on top of java array for lookup by number. + * + * This is the most efficient implementation for almost all cases. But + * it should not be used when field numbers are sparse and especially + * when max field number is big - as this mapping internally uses array + * of integers with size equal to max field number. In latter case + * {@code HashFieldMapEx} should be used. + * + * @see HashFieldMapEx + * + * @author Kostiantyn Shchepanovskyi + */ +public final class ArrayFieldMapEx implements FieldMapEx { + private final List> fields; + + private final FieldSchema[] fieldsByNumber; + + private final Map> fieldsByName; + + @SuppressWarnings("unchecked") + public ArrayFieldMapEx(Collection> fields, int lastFieldNumber) { + fieldsByName = new HashMap<>(); + fieldsByNumber = (FieldSchema[]) new FieldSchema[lastFieldNumber + 1]; + for (FieldSchema f : fields) { + FieldSchema last = this.fieldsByName.put(f.name, f); + if (last != null) { + throw new IllegalStateException(last + " and " + f + " cannot have the same name."); + } + if (fieldsByNumber[f.getFieldNumber()] != null) { + throw new IllegalStateException( + fieldsByNumber[f.getFieldNumber()] + " and " + f + " cannot have the same number."); + } + + fieldsByNumber[f.getFieldNumber()] = f; + } + + List> fieldList = new ArrayList<>(fields.size()); + for (FieldSchema field : fieldsByNumber) { + if (field != null) { + fieldList.add(field); + } + } + this.fields = Collections.unmodifiableList(fieldList); + } + + @Override + public FieldSchema getFieldByNumber(int n) { + return n < fieldsByNumber.length ? fieldsByNumber[n] : null; + } + + @Override + public FieldSchema getFieldByName(String fieldName) { + return fieldsByName.get(fieldName); + } + + /** + * Returns the message's total number of fields. + */ + @Override + public int getFieldCount() { + return fields.size(); + } + + @Override + public List> getFields() { + return fields; + } +} diff --git a/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldMapEx.java b/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldMapEx.java new file mode 100644 index 00000000000..e0234493e27 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldMapEx.java @@ -0,0 +1,55 @@ +//======================================================================== +//Copyright 2007-2010 David Yu dyuproject@gmail.com +//------------------------------------------------------------------------ +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +//http://www.apache.org/licenses/LICENSE-2.0 +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +//======================================================================== + +package io.protostuff.runtime; + +import java.util.Collection; +import java.util.List; + +/** + * Forked and modified from protostuff + * + * Interface for map of fields - defines how to you get field by name or number (tag). + * + * @author Kostiantyn Shchepanovskyi + */ +public interface FieldMapEx { + int MIN_TAG_FOR_HASH_FIELD_MAP = 100; + + static boolean preferHashFieldMap(int fieldCount, int lastFieldNumber) { + return lastFieldNumber > MIN_TAG_FOR_HASH_FIELD_MAP && lastFieldNumber >= 2 * fieldCount; + } + + static FieldMapEx createFieldMap(Collection> fields) { + int lastFieldNumber = 0; + for (FieldSchema field : fields) { + if (field.getFieldNumber() > lastFieldNumber) { + lastFieldNumber = field.getFieldNumber(); + } + } + if (preferHashFieldMap(fields.size(), lastFieldNumber)) { + return new HashFieldMapEx<>(fields); + } + // array field map should be more efficient + return new ArrayFieldMapEx<>(fields, lastFieldNumber); + } + + FieldSchema getFieldByNumber(int n); + + FieldSchema getFieldByName(String fieldName); + + int getFieldCount(); + + List> getFields(); +} diff --git a/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldSchema.java b/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldSchema.java new file mode 100644 index 00000000000..e0ed7bb3b5f --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldSchema.java @@ -0,0 +1,96 @@ +//======================================================================== +//Copyright 2007-2009 David Yu dyuproject@gmail.com +//------------------------------------------------------------------------ +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +//http://www.apache.org/licenses/LICENSE-2.0 +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +//======================================================================== + +package io.protostuff.runtime; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.OutputEx; +import io.protostuff.ProtobufOutputEx; +import io.protostuff.WireFormat; +import io.protostuff.compiler.model.Field; + +/** + * Forked and modified from protostuff + * + * Represents a field of a message/pojo. + */ +public abstract class FieldSchema { + protected final Field protoField; + + protected final String name; + + protected final int fieldNumber; + + protected final int tag; + + protected final int tagSize; + + protected final boolean packed; + + protected final JavaType javaType; + + protected final boolean primitive; + + public FieldSchema(Field protoField, JavaType javaType) { + this.protoField = protoField; + this.name = protoField.getName(); + this.fieldNumber = protoField.getTag(); + this.packed = ProtoUtils.isPacked(protoField); + + int wireType = packed && protoField.isRepeated() ? WireFormat.WIRETYPE_LENGTH_DELIMITED + : FieldTypeUtils.convert(protoField.getType()).wireType; + this.tag = WireFormat.makeTag(fieldNumber, wireType); + this.tagSize = ProtobufOutputEx.computeRawVarint32Size(tag); + + this.javaType = javaType; + this.primitive = javaType.isPrimitive(); + } + + public int getFieldNumber() { + return fieldNumber; + } + + public Field getProtoField() { + return protoField; + } + + public int mergeFrom(InputEx input, T message) throws IOException { + throw new UnsupportedOperationException(); + } + + public boolean isPrimitive() { + return primitive; + } + + public void getAndWriteTo(OutputEx output, T message) throws IOException { + throw new UnsupportedOperationException(); + } + + /** + * write from map + * + * @param output + * @param value field value, will not be null + * @throws IOException + */ + public void writeTo(OutputEx output, Object value) throws IOException { + throw new UnsupportedOperationException(); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldTypeUtils.java b/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldTypeUtils.java new file mode 100644 index 00000000000..c9a141b7373 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/FieldTypeUtils.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 io.protostuff.runtime; + +import io.protostuff.WireFormat.FieldType; +import io.protostuff.compiler.model.ScalarFieldType; + +public final class FieldTypeUtils { + public static FieldType convert(io.protostuff.compiler.model.FieldType fieldType) { + if (fieldType.isEnum()) { + return FieldType.ENUM; + } + + if (fieldType.isScalar()) { + switch ((ScalarFieldType) fieldType) { + case INT32: + return FieldType.INT32; + case INT64: + return FieldType.INT64; + case UINT32: + return FieldType.UINT32; + case UINT64: + return FieldType.UINT64; + case SINT32: + return FieldType.SINT32; + case SINT64: + return FieldType.SINT64; + case FIXED32: + return FieldType.FIXED32; + case FIXED64: + return FieldType.FIXED64; + case SFIXED32: + return FieldType.SFIXED32; + case SFIXED64: + return FieldType.SFIXED64; + case FLOAT: + return FieldType.FLOAT; + case DOUBLE: + return FieldType.DOUBLE; + case BOOL: + return FieldType.BOOL; + case STRING: + return FieldType.STRING; + case BYTES: + return FieldType.BYTES; + default: + throw new IllegalStateException("bug: miss process of " + fieldType); + } + } + + if (fieldType.isMessage()) { + return FieldType.MESSAGE; + } + + throw new IllegalStateException("bug: miss process of " + fieldType); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/HashFieldMapEx.java b/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/HashFieldMapEx.java new file mode 100644 index 00000000000..d810233ed05 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/HashFieldMapEx.java @@ -0,0 +1,86 @@ +//======================================================================== +//Copyright 2007-2010 David Yu dyuproject@gmail.com +//------------------------------------------------------------------------ +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +//http://www.apache.org/licenses/LICENSE-2.0 +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +//======================================================================== + +package io.protostuff.runtime; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Forked and modified from protostuff + * + * Field mapping implemented on top of hash for field lookup by number. + * + * This is the less efficient than {@code ArrayFieldMapEx} for almost all cases. + * But in case when field numbers are sparse and especially when max field + * number is big - this mapping should be used. + * + * @see ArrayFieldMapEx + * + * @author Kostiantyn Shchepanovskyi + */ +public final class HashFieldMapEx implements FieldMapEx { + private final List> fields; + + private final Map> fieldsByNumber; + + private final Map> fieldsByName; + + public HashFieldMapEx(Collection> fields) { + fieldsByName = new HashMap<>(); + fieldsByNumber = new HashMap<>(); + for (FieldSchema f : fields) { + if (fieldsByName.containsKey(f.name)) { + FieldSchema prev = fieldsByName.get(f.name); + throw new IllegalStateException(prev + " and " + f + " cannot have the same name."); + } + if (fieldsByNumber.containsKey(f.fieldNumber)) { + FieldSchema prev = fieldsByNumber.get(f.fieldNumber); + throw new IllegalStateException(prev + " and " + f + " cannot have the same number."); + } + this.fieldsByNumber.put(f.fieldNumber, f); + this.fieldsByName.put(f.name, f); + } + + List> fieldList = new ArrayList<>(fields.size()); + fieldList.addAll(fields); + fieldList.sort(Comparator.comparingInt(FieldSchema::getFieldNumber)); + this.fields = Collections.unmodifiableList(fieldList); + } + + @Override + public FieldSchema getFieldByNumber(int n) { + return fieldsByNumber.get(n); + } + + @Override + public FieldSchema getFieldByName(String fieldName) { + return fieldsByName.get(fieldName); + } + + @Override + public int getFieldCount() { + return fields.size(); + } + + @Override + public List> getFields() { + return fields; + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/ProtoMapper.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/ProtoMapper.java new file mode 100644 index 00000000000..7a5e6950019 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/ProtoMapper.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf; + +import java.lang.reflect.Type; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.protobuf.internal.bean.BeanDescriptorManager; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.DeserializerSchemaManager; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.SerializerSchemaManager; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.protostuff.compiler.model.Message; +import io.protostuff.compiler.model.Proto; +import io.protostuff.compiler.model.Service; +import io.protostuff.compiler.model.ServiceMethod; + +public class ProtoMapper { + private final ObjectMapper jsonMapper; + + private final BeanDescriptorManager beanDescriptorManager; + + private final Proto proto; + + private final SerializerSchemaManager serializerSchemaManager; + + private final DeserializerSchemaManager deserializerSchemaManager; + + // key is message canonical name + // this allowed developer to control any type deserializer + // otherwise any type will be deserialized to LinkedHashMap + private final Map anyTypes = new ConcurrentHashMapEx<>(); + + protected ProtoMapper(ObjectMapper jsonMapper, BeanDescriptorManager beanDescriptorManager, Proto proto) { + this.jsonMapper = jsonMapper; + this.beanDescriptorManager = beanDescriptorManager; + this.proto = proto; + + serializerSchemaManager = new SerializerSchemaManager(this); + deserializerSchemaManager = new DeserializerSchemaManager(this); + } + + public Proto getProto() { + return proto; + } + + public SerializerSchemaManager getSerializerSchemaManager() { + return serializerSchemaManager; + } + + public DeserializerSchemaManager getDeserializerSchemaManager() { + return deserializerSchemaManager; + } + + public ObjectMapper getJsonMapper() { + return jsonMapper; + } + + public BeanDescriptorManager getBeanDescriptorManager() { + return beanDescriptorManager; + } + + public Map getAnyTypes() { + return anyTypes; + } + + public void addAnyType(String canonicalName, Type type) { + anyTypes.put(canonicalName, TypeFactory.defaultInstance().constructType(type)); + } + + public Message getMessageFromCanonicaName(String messageCanonicalName) { + for (Message message : proto.getMessages()) { + if (message.getCanonicalName().equals(messageCanonicalName)) { + return message; + } + } + + return null; + } + + public Message getRequestMessage(String operationId) { + Service service = proto.getServices().get(0); + ServiceMethod serviceMethod = service.getMethod(operationId); + if (serviceMethod == null) { + throw new IllegalArgumentException("operation not found, operation id=" + operationId); + } + return serviceMethod.getArgType(); + } + + public Message getResponseMessage(String operationId) { + Service service = proto.getServices().get(0); + ServiceMethod serviceMethod = service.getMethod(operationId); + return serviceMethod.getReturnType(); + } + + public synchronized RootSerializer createRootSerializer(String shortMessageName, Type type) { + Message message = proto.getMessage(shortMessageName); + if (message == null) { + throw new IllegalStateException("can not find proto message to create serializer, name=" + shortMessageName); + } + + return createRootSerializer(message, type); + } + + public synchronized RootSerializer createRootSerializer(Message message, Type type) { + return serializerSchemaManager.createRootSerializer(message, type); + } + + public synchronized RootDeserializer createRootDeserializer(String shortMessageName, Type type) { + Message message = proto.getMessage(shortMessageName); + if (message == null) { + throw new IllegalStateException("can not find proto message to create deserializer, name=" + shortMessageName); + } + + return createRootDeserializer(message, type); + } + + public synchronized RootDeserializer createRootDeserializer(Message message, Type type) { + return deserializerSchemaManager.createRootDeserializer(message, type); + } + + public synchronized RootDeserializer createRootDeserializer(Message message, Map types) { + return deserializerSchemaManager.createRootDeserializer(message, types); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/ProtoMapperFactory.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/ProtoMapperFactory.java new file mode 100644 index 00000000000..3081ffe4c5e --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/ProtoMapperFactory.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.foundation.protobuf; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.BeanDescriptorManager; +import org.apache.servicecomb.foundation.protobuf.internal.parser.ProtoParser; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.protostuff.compiler.model.Proto; + +public class ProtoMapperFactory { + // 1.to support "any" type + // 2.to find bean properties + private final ObjectMapper jsonMapper = new ObjectMapper(); + + private final BeanDescriptorManager beanDescriptorManager; + + private final ProtoParser protoParser = new ProtoParser(); + + public ProtoMapperFactory() { + jsonMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + jsonMapper.setSerializationInclusion(Include.NON_NULL); + + beanDescriptorManager = new BeanDescriptorManager(jsonMapper.getSerializationConfig()); + } + + public ObjectMapper getJsonMapper() { + return jsonMapper; + } + + public BeanDescriptorManager getBeanDescriptorManager() { + return beanDescriptorManager; + } + + public ProtoMapper createFromContent(String content) { + Proto proto = protoParser.parseFromContent(content); + return create(proto); + } + + public ProtoMapper createFromName(String name) { + Proto proto = protoParser.parse(name); + return create(proto); + } + + public ProtoMapper create(Proto proto) { + return new ProtoMapper(jsonMapper, beanDescriptorManager, proto); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/RootDeserializer.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/RootDeserializer.java new file mode 100644 index 00000000000..8afee6c729c --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/RootDeserializer.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.foundation.protobuf; + +import java.io.IOException; + +import io.protostuff.ByteArrayInputEx; +import io.protostuff.InputEx; +import io.protostuff.SchemaEx; + +public class RootDeserializer { + protected SchemaEx schema; + + public RootDeserializer(SchemaEx schema) { + this.schema = schema; + } + + public SchemaEx getSchema() { + return schema; + } + + public void setSchema(SchemaEx schema) { + this.schema = schema; + } + + @SuppressWarnings("unchecked") + public T deserialize(byte[] bytes) throws IOException { + InputEx input = new ByteArrayInputEx(bytes); + T instance = schema.newMessage(); + schema.mergeFrom(input, instance); + return instance; + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/RootSerializer.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/RootSerializer.java new file mode 100644 index 00000000000..f4fd245576a --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/RootSerializer.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.foundation.protobuf; + +import java.io.IOException; +import java.io.OutputStream; + +import io.protostuff.ProtobufOutputEx; +import io.protostuff.SchemaEx; + +public class RootSerializer { + private final SchemaEx schema; + + public RootSerializer(SchemaEx schema) { + this.schema = schema; + } + + public byte[] serialize(Object value) throws IOException { + ProtobufOutputEx output = new ProtobufOutputEx(); + if (value != null) { + schema.writeTo(output, value); + } + return output.toByteArray(); + } + + public void serialize(OutputStream outputStream, Object value) throws IOException { + ProtobufOutputEx output = new ProtobufOutputEx(); + if (value != null) { + schema.writeTo(output, value); + } + output.toOutputStream(outputStream); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/ProtoConst.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/ProtoConst.java new file mode 100644 index 00000000000..117d82d1300 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/ProtoConst.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.foundation.protobuf.internal; + +import java.util.ArrayList; +import java.util.LinkedHashMap; + +import org.apache.servicecomb.foundation.protobuf.internal.parser.ProtoParser; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.protostuff.compiler.model.Message; +import io.protostuff.compiler.model.Proto; + +public final class ProtoConst { + private ProtoConst() { + } + + public static final String ANNOTATION_WRAP_ARGUMENTS = "@WrapArguments"; + + public static final String ANNOTATION_WRAP_PROPERTY = "@WrapProperty"; + + public static String ANNOTATION_RPC = "@Rpc"; + + public static final String PACK_SCHEMA = "type.googleapis.com/"; + + public static final String JSON_SCHEMA = "json/"; + + public static final String JSON_ID_NAME = "@type"; + + public static final JavaType MAP_TYPE = TypeFactory.defaultInstance().constructType(LinkedHashMap.class); + + public static final JavaType LIST_TYPE = TypeFactory.defaultInstance().constructType(ArrayList.class); + + public static final JavaType OBJECT_TYPE = TypeFactory.defaultInstance().constructType(Object.class); + + public static final Proto ANY_PROTO; + + public static final Message ANY; + + public static final Proto EMPTY_PROTO; + + public static final Message EMPTY; + + static { + ProtoParser protoParser = new ProtoParser(); + + ANY_PROTO = protoParser.parse("google/protobuf/any.proto"); + ANY = ANY_PROTO.getMessage("Any"); + + EMPTY_PROTO = protoParser.parse("google/protobuf/empty.proto"); + EMPTY = EMPTY_PROTO.getMessage("Empty"); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/ProtoUtils.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/ProtoUtils.java new file mode 100644 index 00000000000..2d5c7e0c748 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/ProtoUtils.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.foundation.protobuf.internal; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.compiler.model.DynamicMessage; +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.FieldContainer; +import io.protostuff.compiler.model.Message; +import io.protostuff.compiler.model.ScalarFieldType; +import io.protostuff.compiler.model.Type; + +public final class ProtoUtils { + private ProtoUtils() { + } + + public static boolean isEmptyMessage(Message protoField) { + return protoField.getCanonicalName().equals(ProtoConst.EMPTY.getCanonicalName()); + } + + public static boolean isAnyMessage(Message protoField) { + return protoField.getCanonicalName().equals(ProtoConst.ANY.getCanonicalName()); + } + + public static boolean isAnyField(Field protoField) { + return protoField.getType().getCanonicalName().equals(ProtoConst.ANY.getCanonicalName()); + } + + public static boolean isWrapArguments(FieldContainer fieldContainer) { + return fieldContainer.getCommentLines().contains(ProtoConst.ANNOTATION_WRAP_ARGUMENTS); + } + + public static boolean isWrapProperty(FieldContainer fieldContainer) { + return fieldContainer.getCommentLines().contains(ProtoConst.ANNOTATION_WRAP_PROPERTY); + } + + /** + * all supported type, default to packed + * @param protoField + * @return + */ + public static boolean isSupportPacked(Field protoField) { + if (protoField.getType().isEnum()) { + return true; + } + + if (protoField.getType().isScalar()) { + ScalarFieldType scalarFieldType = (ScalarFieldType) protoField.getType(); + return scalarFieldType != ScalarFieldType.STRING && scalarFieldType != ScalarFieldType.BYTES; + } + + return false; + } + + public static boolean isPacked(Field protoField) { + DynamicMessage.Value dynamicMessageValue = protoField.getOptions().get("packed"); + if (dynamicMessageValue != null) { + return dynamicMessageValue.getBoolean(); + } + + return isSupportPacked(protoField); + } + + public static void throwNotSupportWrite(Field protoField, Object value) throws IllegalStateException { + throwNotSupportWrite(protoField, value.getClass()); + } + + public static void throwNotSupportWrite(Field protoField, Class cls) throws IllegalStateException { + throw new IllegalStateException( + String.format("not support serialize from %s to proto %s, field=%s:%s", + cls.getName(), + protoField.getTypeName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + public static void throwNotSupportMerge(Field protoField, JavaType javaType) throws IllegalStateException { + throw new IllegalStateException( + String.format("not support deserialize proto %s as %s, field=%s:%s", + protoField.getTypeName(), + javaType.toCanonical(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + public static void throwNotSupportNullElement(Field protoField) throws IllegalStateException { + throw new IllegalStateException( + String.format("not support serialize null element, field=%s:%s", + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/BeanDescriptor.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/BeanDescriptor.java new file mode 100644 index 00000000000..f6e15e03363 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/BeanDescriptor.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.bean; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; + +public class BeanDescriptor { + private static final Logger LOGGER = LoggerFactory.getLogger(BeanDescriptor.class); + + private JavaType javaType; + + private final Map propertyDescriptors = new HashMap<>(); + + public JavaType getJavaType() { + return javaType; + } + + public Map getPropertyDescriptors() { + return propertyDescriptors; + } + + public void init(SerializationConfig serializationConfig, JavaType javaType) { + this.javaType = javaType; + + BeanDescription beanDescription = serializationConfig.introspect(javaType); + for (BeanPropertyDefinition propertyDefinition : beanDescription.findProperties()) { + PropertyDescriptor propertyDescriptor = new PropertyDescriptor(); + propertyDescriptor.setName(propertyDefinition.getName()); + propertyDescriptor.setJavaType(propertyDefinition.getPrimaryType()); + + try { + propertyDescriptor.setGetter(initGetter(propertyDefinition)); + } catch (Throwable e) { + LOGGER.error("failed to init getter for field {}:{}", javaType.getRawClass().getName(), + propertyDefinition.getName(), e); + } + + try { + propertyDescriptor.setSetter(initSetter(propertyDefinition)); + } catch (Throwable e) { + LOGGER.error("failed to init setter for field {}:{}", javaType.getRawClass().getName(), + propertyDefinition.getName(), e); + } + + propertyDescriptors.put(propertyDefinition.getName(), propertyDescriptor); + } + } + + @SuppressWarnings("unchecked") + public static T initGetter(BeanPropertyDefinition propertyDefinition) { + if (propertyDefinition.hasGetter()) { + return LambdaMetafactoryUtils.createGetter(propertyDefinition.getGetter().getAnnotated()); + } + + if (propertyDefinition.hasField() && propertyDefinition.getField().isPublic()) { + return (T) LambdaMetafactoryUtils.createGetter(propertyDefinition.getField().getAnnotated()); + } + + return null; + } + + protected Object initSetter(BeanPropertyDefinition propertyDefinition) { + if (propertyDefinition.hasSetter()) { + return LambdaMetafactoryUtils.createSetter(propertyDefinition.getSetter().getAnnotated()); + } + + if (propertyDefinition.hasField() && propertyDefinition.getField().isPublic()) { + return LambdaMetafactoryUtils.createSetter(propertyDefinition.getField().getAnnotated()); + } + + return null; + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/BeanDescriptorManager.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/BeanDescriptorManager.java new file mode 100644 index 00000000000..11a1aa221ec --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/BeanDescriptorManager.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.bean; + +import java.lang.reflect.Type; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.type.TypeFactory; + +public class BeanDescriptorManager { + private final SerializationConfig serializationConfig; + + private final Map beanDescriptors = new ConcurrentHashMapEx<>(); + + public BeanDescriptorManager(SerializationConfig serializationConfig) { + this.serializationConfig = serializationConfig; + } + + public BeanDescriptor getOrCreateBeanDescriptor(Type type) { + return beanDescriptors.computeIfAbsent(type, this::createBeanDescriptor); + } + + protected BeanDescriptor createBeanDescriptor(Type type) { + return createBeanDescriptor(TypeFactory.defaultInstance().constructType(type)); + } + + protected BeanDescriptor createBeanDescriptor(JavaType javaType) { + BeanDescriptor beanDescriptor = new BeanDescriptor(); + beanDescriptor.init(serializationConfig, javaType); + return beanDescriptor; + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/PropertyDescriptor.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/PropertyDescriptor.java new file mode 100644 index 00000000000..fa4cd6243b0 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/PropertyDescriptor.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.foundation.protobuf.internal.bean; + +import com.fasterxml.jackson.databind.JavaType; + +public class PropertyDescriptor { + private String name; + + private JavaType javaType; + + private Object getter; + + private Object setter; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public JavaType getJavaType() { + return javaType; + } + + public void setJavaType(JavaType javaType) { + this.javaType = javaType; + } + + @SuppressWarnings("unchecked") + public T getGetter() { + return (T) getter; + } + + public void setGetter(Object getter) { + this.getter = getter; + } + + @SuppressWarnings("unchecked") + public T getSetter() { + return (T) setter; + } + + public void setSetter(Object setter) { + this.setter = setter; + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/PropertyWrapper.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/PropertyWrapper.java new file mode 100644 index 00000000000..3dc2634378a --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/bean/PropertyWrapper.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.foundation.protobuf.internal.bean; + +public class PropertyWrapper { + private T value; + + public T getValue() { + return value; + } + + public void setValue(T value) { + this.value = value; + } +} 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 new file mode 100644 index 00000000000..89bf2b43282 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ContentFileReader.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.foundation.protobuf.internal.parser; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; + +import io.protostuff.compiler.parser.FileReader; + +public class ContentFileReader implements FileReader { + private final FileReader importReader; + + private final String content; + + private boolean contentRead; + + public ContentFileReader(FileReader importReader, String content) { + this.importReader = importReader; + this.content = content; + } + + @Override + public CharStream read(String contentOrName) { + if (!contentRead) { + contentRead = true; + return CharStreams.fromString(this.content); + } + + return importReader.read(contentOrName); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ProtoParser.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ProtoParser.java new file mode 100644 index 00000000000..e25e9b3f0aa --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ProtoParser.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.foundation.protobuf.internal.parser; + +import java.util.Collections; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +import io.protostuff.compiler.ParserModule; +import io.protostuff.compiler.model.Proto; +import io.protostuff.compiler.parser.FileDescriptorLoader; +import io.protostuff.compiler.parser.FileReader; +import io.protostuff.compiler.parser.FileReaderFactory; +import io.protostuff.compiler.parser.ProtoContext; + +/** + * can be reused + */ +public class ProtoParser { + private static final String DEFAULT_PROTO_NAME = "default.proto"; + + private final Injector injector = Guice.createInjector(new ParserModule()); + + private final FileReaderFactory fileReaderFactory = injector.getInstance(FileReaderFactory.class); + + private final FileReader defaultReader = fileReaderFactory.create(Collections.emptyList()); + + private final FileDescriptorLoader loader = injector.getInstance(FileDescriptorLoader.class); + + public Proto parseFromContent(String content) { + // io.protostuff.compiler.parser.ClasspathFileReader will use ContextClassLoader load resource, but in some environment, + // ContextClassLoader is null, and we use class loader instead. + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + try { + if (classLoader == null) { + Thread.currentThread().setContextClassLoader(ProtoParser.class.getClassLoader()); + } + ProtoContext context = loader.load(new ContentFileReader(defaultReader, content), DEFAULT_PROTO_NAME); + return context.getProto(); + } finally { + Thread.currentThread().setContextClassLoader(classLoader); + } + } + + public Proto parse(String name) { + // io.protostuff.compiler.parser.ClasspathFileReader will use ContextClassLoader load resource, but in some environment, + // ContextClassLoader is null, and we use class loader instead. + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + try { + if (classLoader == null) { + Thread.currentThread().setContextClassLoader(ProtoParser.class.getClassLoader()); + } + ProtoContext context = loader.load(defaultReader, name); + return context.getProto(); + } finally { + Thread.currentThread().setContextClassLoader(classLoader); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/EnumMeta.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/EnumMeta.java new file mode 100644 index 00000000000..79a842a68c5 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/EnumMeta.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.foundation.protobuf.internal.schema; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.compiler.model.EnumConstant; +import io.protostuff.compiler.model.Field; + +public class EnumMeta { + private final Map enumNameToValueMap = new HashMap<>(); + + // key is idl defined enum value + // value is Enum or null + private final Map> enumValues = new HashMap<>(); + + public EnumMeta(Field protoField, JavaType javaType) { + io.protostuff.compiler.model.Enum enumType = (io.protostuff.compiler.model.Enum) protoField.getType(); + for (EnumConstant enumConstant : enumType.getConstants()) { + enumNameToValueMap.put(enumConstant.getName(), enumConstant.getValue()); + enumValues.put(enumConstant.getValue(), null); + } + + if (!javaType.isEnumType()) { + return; + } + + try { + Method method = javaType.getRawClass().getMethod("values"); + method.setAccessible(true); + Object[] values = (Object[]) method.invoke(null); + for (Object value : values) { + Enum enumValue = (Enum) value; + enumValues.put(enumNameToValueMap.get(enumValue.name()), enumValue); + } + } catch (Throwable e) { + throw new IllegalStateException( + "Failed to collect enum values, class=" + javaType.getRawClass().getName(), e); + } + } + + public boolean containsValue(Integer value) { + return enumValues.containsKey(value); + } + + public Enum getEnumByValue(int enumValue) { + return enumValues.get(enumValue); + } + + public Integer getValueByName(String name) { + return enumNameToValueMap.get(name); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/MessageAsFieldSchema.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/MessageAsFieldSchema.java new file mode 100644 index 00000000000..5f4fa999a37 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/MessageAsFieldSchema.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.InputEx; +import io.protostuff.OutputEx; +import io.protostuff.SchemaEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class MessageAsFieldSchema extends FieldSchema { + protected final SchemaEx schema; + + protected final Getter getter; + + protected final Setter setter; + + public MessageAsFieldSchema(Field protoField, PropertyDescriptor propertyDescriptor, SchemaEx schema) { + super(protoField, propertyDescriptor.getJavaType()); + this.schema = schema; + this.getter = propertyDescriptor.getGetter(); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value == null) { + return; + } + + output.writeObject(tag, tagSize, value, schema); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + output.writeObject(tag, tagSize, value, schema); + } + + @Override + public final int mergeFrom(InputEx input, T message) throws IOException { + Object value = getter.get(message); + if (value == null) { + value = schema.newMessage(); + setter.set(message, value); + } + input.mergeObject(value, schema); + return input.readFieldNumber(); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/PropertyWrapperAsFieldSchema.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/PropertyWrapperAsFieldSchema.java new file mode 100644 index 00000000000..fdb737ea194 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/PropertyWrapperAsFieldSchema.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.foundation.protobuf.internal.schema; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.MessageReadSchema; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.MessageWriteSchema; + +import io.protostuff.InputEx; +import io.protostuff.OutputEx; +import io.protostuff.SchemaEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +/** + * + */ +public class PropertyWrapperAsFieldSchema extends FieldSchema { + private final SchemaEx schema; + + private final FieldSchema fieldSchema; + + protected final Getter getter; + + protected final Setter setter; + + public PropertyWrapperAsFieldSchema(Field protoField, PropertyDescriptor propertyDescriptor, + SchemaEx schema) { + super(protoField, propertyDescriptor.getJavaType()); + this.schema = schema; + this.getter = propertyDescriptor.getGetter(); + this.setter = propertyDescriptor.getSetter(); + + if (schema instanceof MessageWriteSchema) { + fieldSchema = ((MessageWriteSchema) schema).getMainPojoFieldMaps().getFieldByNumber(1); + return; + } + + fieldSchema = ((MessageReadSchema) schema).getFieldMap().getFieldByNumber(1); + } + + @Override + public void getAndWriteTo(OutputEx output, T message) throws IOException { + output.writeObject(tag, tagSize, message, fieldSchema::writeTo); + } + + @Override + public void writeTo(OutputEx output, Object value) throws IOException { + output.writeObject(tag, tagSize, value, fieldSchema::writeTo); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + PropertyWrapper wrapper = new PropertyWrapper<>(); + input.mergeObject(wrapper, schema); + setter.set(message, wrapper.getValue()); + return input.readFieldNumber(); + } +} 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 new file mode 100644 index 00000000000..5d583954ca0 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema; + +import static org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils.isAnyField; +import static org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils.isWrapProperty; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang3.ClassUtils; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.utils.bean.MapGetter; +import org.apache.servicecomb.foundation.common.utils.bean.MapSetter; +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; +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.any.AnySchema; +import org.apache.servicecomb.foundation.protobuf.internal.schema.map.MapEntry; +import org.apache.servicecomb.foundation.protobuf.internal.schema.map.MapSchema; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.protostuff.SchemaEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.Message; +import io.protostuff.compiler.model.Proto; +import io.protostuff.runtime.FieldMapEx; +import io.protostuff.runtime.FieldSchema; + +public abstract class SchemaManager { + protected final ProtoMapper protoMapper; + + protected final Proto proto; + + // 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(); + } + + protected String generateCacheKey(Message message, JavaType javaType) { + return message.getCanonicalName() + ":" + javaType.toCanonical(); + } + + protected abstract SchemaEx newMessageSchema(Message message, JavaType javaType); + + protected abstract SchemaEx newMessageSchema(Message message, Map types); + + protected abstract FieldSchema createScalarField(Field protoField, PropertyDescriptor propertyDescriptor); + + @SuppressWarnings("unchecked") + protected SchemaEx getOrCreateMessageSchema(Message message, Map types) { + String cacheKey = generateCacheKey(message, ProtoConst.MAP_TYPE); + return (SchemaEx) canonicalSchemasCreated.computeIfAbsent(cacheKey, key -> createMessageSchema(message, types)); + } + + @SuppressWarnings("unchecked") + protected SchemaEx getOrCreateMessageSchema(Message message, JavaType javaType) { + String cacheKey = generateCacheKey(message, javaType); + return (SchemaEx) canonicalSchemasCreated.computeIfAbsent(cacheKey, + key -> createMessageSchema(message, javaType)); + } + + @SuppressWarnings("unchecked") + protected SchemaEx createMessageSchema(Message message, Map types) { + String cacheKey = generateCacheKey(message, ProtoConst.MAP_TYPE); + SchemaEx schema = (SchemaEx) canonicalSchemas.get(cacheKey); + if (schema != null) { + return schema; + } + + schema = newMessageSchema(message, types); + canonicalSchemas.put(cacheKey, schema); + + schema.init(); + return schema; + } + + @SuppressWarnings("unchecked") + protected SchemaEx createMessageSchema(Message message, JavaType javaType) { + String cacheKey = generateCacheKey(message, javaType); + SchemaEx schema = (SchemaEx) canonicalSchemas.get(cacheKey); + if (schema != null) { + return schema; + } + + schema = newMessageSchema(message, javaType); + canonicalSchemas.put(cacheKey, schema); + + schema.init(); + return schema; + } + + protected FieldSchema createMapFieldSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (javaType.isJavaLangObject()) { + javaType = ProtoConst.MAP_TYPE; + } + + JavaType entryType = TypeFactory.defaultInstance().constructParametricType(MapEntry.class, + javaType.getKeyType(), + javaType.getContentType()); + SchemaEx> entrySchema = createMessageSchema((Message) protoField.getType(), + entryType); + return new MapSchema<>(protoField, propertyDescriptor, entrySchema); + } + + // normal message write from or read to a map + public FieldMapEx> createMapFields(Message message) { + List>> fieldSchemas = new ArrayList<>(); + for (Field protoField : message.getFields()) { + PropertyDescriptor propertyDescriptor = new PropertyDescriptor(); + propertyDescriptor.setJavaType(ProtoConst.OBJECT_TYPE); + propertyDescriptor.setGetter(new MapGetter<>(protoField.getName())); + propertyDescriptor.setSetter(new MapSetter<>(protoField.getName())); + + FieldSchema> fieldSchema = createSchemaField(protoField, propertyDescriptor); + fieldSchemas.add(fieldSchema); + } + + return FieldMapEx.createFieldMap(fieldSchemas); + } + + public FieldMapEx> createMapFields(Message message, Map types) { + List>> fieldSchemas = new ArrayList<>(); + for (Field protoField : message.getFields()) { + PropertyDescriptor propertyDescriptor = new PropertyDescriptor(); + + JavaType javaType = getParameterType(types, protoField.getName()); + if (javaType.isPrimitive()) { + javaType = TypeFactory.defaultInstance().constructType(ClassUtils.primitiveToWrapper(javaType.getRawClass())); + } + propertyDescriptor.setJavaType(javaType); + propertyDescriptor.setGetter(new MapGetter<>(protoField.getName())); + propertyDescriptor.setSetter(new MapSetter<>(protoField.getName())); + + FieldSchema> fieldSchema = createSchemaField(protoField, propertyDescriptor); + fieldSchemas.add(fieldSchema); + } + + return FieldMapEx.createFieldMap(fieldSchemas); + } + + private JavaType getParameterType(Map types, String parameterName) { + + 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]", parameterName)); + } + + public FieldSchema createSchemaField(Field protoField, PropertyDescriptor propertyDescriptor) { + // map is a special repeated + if (protoField.isMap()) { + return createMapFieldSchema(protoField, propertyDescriptor); + } + + if (protoField.isRepeated()) { + return createRepeatedSchema(protoField, propertyDescriptor); + } + + if (isAnyField(protoField)) { + return new AnySchema<>(protoMapper, protoField, propertyDescriptor); + } + + if (protoField.getType().isScalar()) { + return createScalarField(protoField, propertyDescriptor); + } + + // message + if (protoField.getType().isMessage()) { + SchemaEx messageSchema = createMessageSchema((Message) protoField.getType(), + propertyDescriptor.getJavaType()); + if (isWrapProperty((Message) protoField.getType())) { + return new PropertyWrapperAsFieldSchema<>(protoField, propertyDescriptor, messageSchema); + } + + return new MessageAsFieldSchema<>(protoField, propertyDescriptor, messageSchema); + } + + if (protoField.isOneofPart()) { + throw new IllegalStateException("not IMPL oneof now."); + } + + ProtoUtils.throwNotSupportWrite(protoField, propertyDescriptor.getJavaType().getRawClass()); + return null; + } + + protected abstract FieldSchema createRepeatedSchema(Field protoField, PropertyDescriptor propertyDescriptor); +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntry.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntry.java new file mode 100644 index 00000000000..36178def784 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntry.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.foundation.protobuf.internal.schema.any; + +public class AnyEntry { + private String typeUrl; + + private byte[] value; + + public String getTypeUrl() { + return typeUrl; + } + + public void setTypeUrl(String typeUrl) { + this.typeUrl = typeUrl; + } + + public byte[] getValue() { + return value; + } + + public void setValue(byte[] value) { + this.value = value; + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntrySchema.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntrySchema.java new file mode 100644 index 00000000000..4fc8fd5e58a --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnyEntrySchema.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.foundation.protobuf.internal.schema.any; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +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.ProtoConst; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.protostuff.InputEx; +import io.protostuff.OutputEx; +import io.protostuff.SchemaEx; +import io.protostuff.SchemaWriter; +import io.protostuff.WireFormat; +import io.protostuff.compiler.model.Message; + +public class AnyEntrySchema implements SchemaEx { + private final ProtoMapper protoMapper; + + // key is message short name + private final Map> anyEntrySserializers = new ConcurrentHashMapEx<>(); + + // key is message canonical name + private final Map> rootDeserializers = new ConcurrentHashMapEx<>(); + + private final int keyTag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); + + private final int valueTag = WireFormat.makeTag(2, WireFormat.WIRETYPE_LENGTH_DELIMITED); + + private final Type anyTargetType; + + public AnyEntrySchema(ProtoMapper protoMapper, Type type) { + this.protoMapper = protoMapper; + this.anyTargetType = type; + } + + @Override + public void init() { + + } + + @Override + public Object newMessage() { + return new PropertyWrapper<>(); + } + + @Override + @SuppressWarnings({"rawtypes", "unchecked"}) + public void mergeFrom(InputEx input, Object message) throws IOException { + input.readFieldNumber(); + String typeUrl = input.readString(); + + input.readFieldNumber(); + byte[] bytes = input.readByteArray(); + + input.readFieldNumber(); + + if (message instanceof PropertyWrapper) { + if (typeUrl.startsWith(ProtoConst.PACK_SCHEMA)) { + ((PropertyWrapper) message).setValue(standardUnpack(typeUrl, bytes)); + } else { + ((PropertyWrapper) message).setValue(jsonExtendMergeFrom(typeUrl, bytes)); + } + } else if (message instanceof AnyEntry) { + ((AnyEntry) message).setTypeUrl(typeUrl); + ((AnyEntry) message).setValue(bytes); + } + } + + public Object deserialize(InputEx input) throws IOException { + AnyEntry anyEntry = new AnyEntry(); + input.mergeObject(anyEntry, this); + + if (anyEntry.getTypeUrl().startsWith(ProtoConst.PACK_SCHEMA)) { + return standardUnpack(anyEntry.getTypeUrl(), anyEntry.getValue()); + } + + return jsonExtendMergeFrom(anyEntry.getTypeUrl(), anyEntry.getValue()); + } + + @SuppressWarnings("unchecked") + protected Object standardUnpack(String typeUrl, byte[] bytes) throws IOException { + String msgCanonicalName = typeUrl.substring(ProtoConst.PACK_SCHEMA.length()); + RootDeserializer valueDeserializer = rootDeserializers + .computeIfAbsent(msgCanonicalName, this::createRootDeserializerFromCanonicaName); + Object value = valueDeserializer.deserialize(bytes); + if (value instanceof Map) { + ((Map) value).put(ProtoConst.JSON_ID_NAME, valueDeserializer.getSchema().messageName()); + } + return value; + } + + protected RootDeserializer createRootDeserializerFromCanonicaName(String msgCanonicalName) { + Message message = protoMapper.getMessageFromCanonicaName(msgCanonicalName); + if (message == null) { + throw new IllegalStateException( + "can not find proto message to create deserializer, name=" + msgCanonicalName); + } + + JavaType javaType = protoMapper.getAnyTypes() + .getOrDefault(msgCanonicalName, constructRuntimeType(ProtoConst.MAP_TYPE)); + return protoMapper.createRootDeserializer(message, javaType); + } + + protected Object jsonExtendMergeFrom(String typeUrl, byte[] bytes) throws IOException { + try { + return protoMapper.getJsonMapper() + .readValue(bytes, Class.forName(typeUrl.substring(ProtoConst.JSON_SCHEMA.length()))); + } catch (ClassNotFoundException e) { + return protoMapper.getJsonMapper() + .readValue(bytes, constructRuntimeType(ProtoConst.OBJECT_TYPE)); + } + } + + private JavaType constructRuntimeType(JavaType defaultType) { + if (this.anyTargetType == null) { + return defaultType; + } else { + return TypeFactory.defaultInstance().constructType(anyTargetType); + } + } + + protected String getInputActualTypeName(Object input) { + if (!(input instanceof Map)) { + return input.getClass().getSimpleName(); + } + + // @JsonTypeInfo(use = Id.NAME) + Object actualTypeName = ((Map) input).get(ProtoConst.JSON_ID_NAME); + if (actualTypeName instanceof String) { + return (String) actualTypeName; + } + + return null; + } + + /** + *
+   * if message is type of CustomGeneric<User>
+   * we can not get any information of "User" from message.getClass()
+   *
+   * when use with ServiceComb
+   * proto definition convert from swagger, the proto type will be "CustomGenericUser"
+   * is not match to "CustomGeneric"
+   * so message will be serialized with json schema
+   * 
+ * @param output + * @param value + * @throws IOException + */ + @Override + public void writeTo(OutputEx output, Object value) throws IOException { + String actualTypeName = getInputActualTypeName(value); + SchemaWriter entryWriter = actualTypeName == null ? this::jsonExtend : anyEntrySserializers + .computeIfAbsent(actualTypeName, n -> createEntryWriter(n, value)); + entryWriter.writeTo(output, value); + } + + private SchemaWriter createEntryWriter(String actualTypeName, Object _value) { + Message message = protoMapper.getProto().getMessage(actualTypeName); + if (message == null) { + // not standard, protobuf can not support or not define this type , just extend + return this::jsonExtend; + } + + // standard pack + RootSerializer valueSerializer = protoMapper.createRootSerializer(message, _value.getClass()); + String valueCanonicalName = message.getCanonicalName(); + return (output, value) -> standardPack(output, value, valueCanonicalName, valueSerializer); + } + + protected void standardPack(OutputEx output, Object message, String canonicalName, RootSerializer valueSerializer) + throws IOException { + output.writeString(keyTag, 1, ProtoConst.PACK_SCHEMA + canonicalName); + + byte[] bytes = valueSerializer.serialize(message); + output.writeByteArray(valueTag, 1, bytes); + } + + protected void jsonExtend(OutputEx output, Object input) throws IOException { + output.writeString(keyTag, 1, ProtoConst.JSON_SCHEMA + input.getClass().getName()); + + byte[] bytes = protoMapper.getJsonMapper().writeValueAsBytes(input); + output.writeByteArray(valueTag, 1, bytes); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnySchema.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnySchema.java new file mode 100644 index 00000000000..87cbba9633c --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/any/AnySchema.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.foundation.protobuf.internal.schema.any; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.InputEx; +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class AnySchema extends FieldSchema { + private final AnyEntrySchema anyEntrySchema; + + private final Getter getter; + + private final Setter setter; + + public AnySchema(ProtoMapper protoMapper, Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + + this.anyEntrySchema = new AnyEntrySchema(protoMapper, null); + this.getter = propertyDescriptor.getGetter(); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public final int mergeFrom(InputEx input, T message) throws IOException { + Object anyValue = anyEntrySchema.deserialize(input); + setter.set(message, anyValue); + + return input.readFieldNumber(); + } + + @Override + public void getAndWriteTo(OutputEx output, T message) throws IOException { + Object anyEntry = getter.get(message); + if (anyEntry == null) { + return; + } + + output.writeObject(tag, tagSize, anyEntry, anyEntrySchema); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + output.writeObject(tag, tagSize, value, anyEntrySchema); + } +} 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 new file mode 100644 index 00000000000..23b9d85a01d --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java @@ -0,0 +1,262 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.deserializer; + +import static org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils.isWrapProperty; + +import java.lang.reflect.Type; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.utils.TypesUtil; +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; +import org.apache.servicecomb.foundation.protobuf.internal.schema.SchemaManager; +import org.apache.servicecomb.foundation.protobuf.internal.schema.any.AnyEntrySchema; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.AnyRepeatedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.BytesRepeatedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.MessageRepeatedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.PropertyWrapperRepeatedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.StringRepeatedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools.impl.BoolNotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools.impl.BoolPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles.impl.DoubleNotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles.impl.DoublePackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.enums.EnumNotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.enums.EnumPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats.impl.FloatNotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats.impl.FloatPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.Fixed32NotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.Fixed32PackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.Int32NotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.Int32PackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.SFixed32NotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.SFixed32PackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.SInt32NotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.SInt32PackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.UInt32NotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl.UInt32PackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.Fixed64NotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.Fixed64PackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.Int64NotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.Int64PackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.SFixed64NotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.SFixed64PackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.SInt64NotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.SInt64PackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.UInt64NotPackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl.UInt64PackedReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.BoolReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.BytesReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.DoubleReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.EnumsReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.Fixed32ReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.Fixed64ReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.FloatReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.Int32ReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.Int64ReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.SFixed32ReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.SFixed64ReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.SInt32ReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.SInt64ReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.StringReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.UInt32ReadSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.UInt64ReadSchemas; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.protostuff.SchemaEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.Message; +import io.protostuff.compiler.model.ScalarFieldType; +import io.protostuff.runtime.FieldSchema; + +public class DeserializerSchemaManager extends SchemaManager { + public DeserializerSchemaManager(ProtoMapper protoMapper) { + super(protoMapper); + } + + public RootDeserializer createRootDeserializer(Message message, Map types) { + SchemaEx messageSchema = getOrCreateMessageSchema(message, types); + return new RootDeserializer<>(messageSchema); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public RootDeserializer createRootDeserializer(Message message, Type type) { + if (ProtoUtils.isAnyMessage(message)) { + SchemaEx messageSchema = new AnyEntrySchema(protoMapper, type); + return new RootDeserializer(messageSchema); + } + JavaType javaType = TypeFactory.defaultInstance().constructType(type); + SchemaEx messageSchema = getOrCreateMessageSchema(message, javaType); + return new RootDeserializer<>(messageSchema); + } + + @Override + protected SchemaEx newMessageSchema(Message message, Map types) { + return new MessageReadSchema<>(protoMapper, message, types); + } + + @Override + protected SchemaEx newMessageSchema(Message message, JavaType javaType) { + if (ProtoUtils.isWrapProperty(message) && javaType.getRawClass() != PropertyWrapper.class) { + Field protoField = message.getField(1); + if (javaType.isJavaLangObject()) { + javaType = + protoField.isRepeated() && !protoField.isMap() ? ProtoConst.LIST_TYPE + : ProtoConst.MAP_TYPE; + } + + if (javaType.isPrimitive()) { + javaType = TypeFactory.defaultInstance() + .constructParametricType(PropertyWrapper.class, TypesUtil.primitiveJavaTypeToWrapper(javaType)); + } else { + javaType = TypeFactory.defaultInstance().constructParametricType(PropertyWrapper.class, javaType); + } + } + + if (javaType.isJavaLangObject()) { + javaType = ProtoConst.MAP_TYPE; + } + + return new MessageReadSchema<>(protoMapper, message, javaType); + } + + protected FieldSchema createScalarField(Field protoField, PropertyDescriptor propertyDescriptor) { + if (protoField.getType().isEnum()) { + return EnumsReadSchemas.create(protoField, propertyDescriptor); + } + + switch ((ScalarFieldType) protoField.getType()) { + case INT32: + return Int32ReadSchemas.create(protoField, propertyDescriptor); + case UINT32: + return UInt32ReadSchemas.create(protoField, propertyDescriptor); + case SINT32: + return SInt32ReadSchemas.create(protoField, propertyDescriptor); + case FIXED32: + return Fixed32ReadSchemas.create(protoField, propertyDescriptor); + case SFIXED32: + return SFixed32ReadSchemas.create(protoField, propertyDescriptor); + case INT64: + return Int64ReadSchemas.create(protoField, propertyDescriptor); + case UINT64: + return UInt64ReadSchemas.create(protoField, propertyDescriptor); + case SINT64: + return SInt64ReadSchemas.create(protoField, propertyDescriptor); + case FIXED64: + return Fixed64ReadSchemas.create(protoField, propertyDescriptor); + case SFIXED64: + return SFixed64ReadSchemas.create(protoField, propertyDescriptor); + case FLOAT: + return FloatReadSchemas.create(protoField, propertyDescriptor); + case DOUBLE: + return DoubleReadSchemas.create(protoField, propertyDescriptor); + case BOOL: + return BoolReadSchemas.create(protoField, propertyDescriptor); + case STRING: + return StringReadSchemas.create(protoField, propertyDescriptor); + case BYTES: + return BytesReadSchemas.create(protoField, propertyDescriptor); + default: + throw new IllegalStateException("unknown proto field type: " + protoField.getType()); + } + } + + @Override + protected FieldSchema createRepeatedSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + boolean packed = ProtoUtils.isPacked(protoField); + if (protoField.getType().isEnum()) { + return packed ? EnumPackedReadSchemas.create(protoField, propertyDescriptor) : + EnumNotPackedReadSchemas.create(protoField, propertyDescriptor); + } + + if (protoField.getType().isScalar()) { + switch ((ScalarFieldType) protoField.getType()) { + case INT32: + return packed ? Int32PackedReadSchemas.create(protoField, propertyDescriptor) : + Int32NotPackedReadSchemas.create(protoField, propertyDescriptor); + case UINT32: + return packed ? UInt32PackedReadSchemas.create(protoField, propertyDescriptor) : + UInt32NotPackedReadSchemas.create(protoField, propertyDescriptor); + case SINT32: + return packed ? SInt32PackedReadSchemas.create(protoField, propertyDescriptor) : + SInt32NotPackedReadSchemas.create(protoField, propertyDescriptor); + case FIXED32: + return packed ? Fixed32PackedReadSchemas.create(protoField, propertyDescriptor) : + Fixed32NotPackedReadSchemas.create(protoField, propertyDescriptor); + case SFIXED32: + return packed ? SFixed32PackedReadSchemas.create(protoField, propertyDescriptor) : + SFixed32NotPackedReadSchemas.create(protoField, propertyDescriptor); + case INT64: + return packed ? Int64PackedReadSchemas.create(protoField, propertyDescriptor) : + Int64NotPackedReadSchemas.create(protoField, propertyDescriptor); + case UINT64: + return packed ? UInt64PackedReadSchemas.create(protoField, propertyDescriptor) : + UInt64NotPackedReadSchemas.create(protoField, propertyDescriptor); + case SINT64: + return packed ? SInt64PackedReadSchemas.create(protoField, propertyDescriptor) : + SInt64NotPackedReadSchemas.create(protoField, propertyDescriptor); + case FIXED64: + return packed ? Fixed64PackedReadSchemas.create(protoField, propertyDescriptor) : + Fixed64NotPackedReadSchemas.create(protoField, propertyDescriptor); + case SFIXED64: + return packed ? SFixed64PackedReadSchemas.create(protoField, propertyDescriptor) : + SFixed64NotPackedReadSchemas.create(protoField, propertyDescriptor); + case FLOAT: + return packed ? FloatPackedReadSchemas.create(protoField, propertyDescriptor) : + FloatNotPackedReadSchemas.create(protoField, propertyDescriptor); + case DOUBLE: + return packed ? DoublePackedReadSchemas.create(protoField, propertyDescriptor) : + DoubleNotPackedReadSchemas.create(protoField, propertyDescriptor); + case BOOL: + return packed ? BoolPackedReadSchemas.create(protoField, propertyDescriptor) : + BoolNotPackedReadSchemas.create(protoField, propertyDescriptor); + case STRING: + return StringRepeatedReadSchemas.create(protoField, propertyDescriptor); + case BYTES: + return BytesRepeatedReadSchemas.create(protoField, propertyDescriptor); + default: + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + } + } + + if (ProtoUtils.isAnyField(protoField)) { + AnyEntrySchema anyEntrySchema = new AnyEntrySchema(protoMapper, null); + return AnyRepeatedReadSchemas.create(protoField, propertyDescriptor, anyEntrySchema); + } + + if (protoField.getType().isMessage()) { + JavaType contentType = propertyDescriptor.getJavaType().getContentType(); + if (contentType == null) { + contentType = ProtoConst.OBJECT_TYPE; + } + SchemaEx contentSchema = createMessageSchema((Message) protoField.getType(), contentType); + if (isWrapProperty((Message) protoField.getType())) { + return PropertyWrapperRepeatedReadSchemas.create(protoField, propertyDescriptor, contentSchema); + } + + return MessageRepeatedReadSchemas.create(protoField, propertyDescriptor, contentSchema); + } + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/MessageReadSchema.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/MessageReadSchema.java new file mode 100644 index 00000000000..68990c29865 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/MessageReadSchema.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.foundation.protobuf.internal.schema.deserializer; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; +import org.apache.servicecomb.foundation.protobuf.internal.bean.BeanDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.OutputEx; +import io.protostuff.SchemaEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.Message; +import io.protostuff.runtime.FieldMapEx; +import io.protostuff.runtime.FieldSchema; +import io.protostuff.runtime.RuntimeEnv; +import io.protostuff.runtime.RuntimeEnv.Instantiator; + +/** + *
+ * map.put("user", new User())
+ * root write from map, but user should write from pojo
+ * so one schema should support dynamic and concrete logic at the same time
+ * 
+ */ +public class MessageReadSchema implements SchemaEx { + private static final Logger LOGGER = LoggerFactory.getLogger(MessageReadSchema.class); + + protected final ProtoMapper protoMapper; + + protected final Message message; + + private FieldMapEx fieldMap; + + private final Instantiator instantiator; + + private JavaType javaType; + + Map argumentsTypes; + + private boolean argumentsRoot = false; + + @SuppressWarnings("unchecked") + public MessageReadSchema(ProtoMapper protoMapper, Message message, Map argumentsTypes) { + this.argumentsRoot = true; + this.protoMapper = protoMapper; + this.message = message; + this.argumentsTypes = argumentsTypes; + this.instantiator = RuntimeEnv.newInstantiator((Class) ProtoConst.MAP_TYPE.getRawClass()); + } + + @SuppressWarnings("unchecked") + public MessageReadSchema(ProtoMapper protoMapper, Message message, JavaType javaType) { + this.protoMapper = protoMapper; + this.message = message; + this.javaType = javaType; + if (javaType.isJavaLangObject() || Map.class.isAssignableFrom(javaType.getRawClass())) { + javaType = ProtoConst.MAP_TYPE; + } + this.instantiator = RuntimeEnv.newInstantiator((Class) javaType.getRawClass()); + } + + public Message getMessage() { + return message; + } + + @Override + public T newMessage() { + return instantiator.newInstance(); + } + + @Override + public String messageName() { + return message.getName(); + } + + public FieldMapEx getFieldMap() { + return fieldMap; + } + + @SuppressWarnings("unchecked") + @Override + public void init() { + if (argumentsRoot) { + this.fieldMap = (FieldMapEx) protoMapper.getDeserializerSchemaManager() + .createMapFields(message, argumentsTypes); + return; + } + + if (Map.class.isAssignableFrom(javaType.getRawClass())) { + this.fieldMap = (FieldMapEx) protoMapper.getDeserializerSchemaManager() + .createMapFields(message); + return; + } + + this.createFieldMap(); + } + + private void createFieldMap() { + DeserializerSchemaManager deserializerSchemaManager = protoMapper.getDeserializerSchemaManager(); + BeanDescriptor beanDescriptor = protoMapper.getBeanDescriptorManager().getOrCreateBeanDescriptor(javaType); + + List> fieldSchemas = new ArrayList<>(); + for (PropertyDescriptor propertyDescriptor : beanDescriptor.getPropertyDescriptors().values()) { + Field protoField = message.getField(propertyDescriptor.getName()); + if (protoField == null) { + LOGGER.info("java field {}:{} not exist in proto message {}, ignore it.", + beanDescriptor.getJavaType().getRawClass().getName(), + propertyDescriptor.getName(), message.getCanonicalName()); + continue; + } + if (propertyDescriptor.getSetter() == null) { + LOGGER.info("no setter for java field {}:{} in proto message {}, ignore it.", + beanDescriptor.getJavaType().getRawClass().getName(), + propertyDescriptor.getName(), message.getCanonicalName()); + continue; + } + + FieldSchema fieldSchema = deserializerSchemaManager.createSchemaField(protoField, propertyDescriptor); + fieldSchemas.add(fieldSchema); + } + + this.fieldMap = FieldMapEx.createFieldMap(fieldSchemas); + } + + @Override + public void mergeFrom(InputEx input, T message) throws IOException { + FieldSchema fieldSchema = null; + try { + for (int n = input.readFieldNumber(); n != 0; ) { + fieldSchema = fieldMap.getFieldByNumber(n); + if (fieldSchema != null) { + n = fieldSchema.mergeFrom(input, message); + continue; + } + + input.handleUnknownField(n); + n = input.readFieldNumber(); + } + } catch (Throwable e) { + if (fieldSchema != null) { + Field protoField = fieldSchema.getProtoField(); + LOGGER.error("Failed to mergeFrom, field={}:{}, type={}, error {}", + protoField.getType().getCanonicalName(), + protoField.getName(), + protoField.getTypeName(), + e.getMessage()); + } + throw e; + } + } + + @Override + public void writeTo(OutputEx output, Object value) throws IOException { + throw new UnsupportedOperationException(); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/AbstractPrimitiveReaders.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/AbstractPrimitiveReaders.java new file mode 100644 index 00000000000..e43f6b61d11 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/AbstractPrimitiveReaders.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.foundation.protobuf.internal.schema.deserializer.repeated; + +import io.protostuff.compiler.model.Field; + +public abstract class AbstractPrimitiveReaders extends + AbstractReaders { + public RepeatedReader> primitiveArrayReader; + + @SuppressWarnings("unchecked") + public AbstractPrimitiveReaders(Field protoField) { + super(protoField); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/AbstractReaders.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/AbstractReaders.java new file mode 100644 index 00000000000..0143c12af70 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/AbstractReaders.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.foundation.protobuf.internal.schema.deserializer.repeated; + +import static org.apache.servicecomb.foundation.common.utils.ReflectUtils.getFieldArgument; + +import java.util.Collection; + +import io.protostuff.compiler.model.Field; + +public abstract class AbstractReaders { + protected final Field protoField; + + protected final int fieldNumber; + + public RepeatedReader> collectionReader; + + public final Class arrayClass; + + @SuppressWarnings("unchecked") + public AbstractReaders(Field protoField) { + this(protoField, null); + } + + public AbstractReaders(Field protoField, Class arrayClass) { + this.protoField = protoField; + this.fieldNumber = protoField.getTag(); + + if (arrayClass == null) { + arrayClass = getFieldArgument(this.getClass(), "arrayClass"); + } + this.arrayClass = arrayClass; + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/PrimitiveArrayBuilderWrapper.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/PrimitiveArrayBuilderWrapper.java new file mode 100644 index 00000000000..1478e152268 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/PrimitiveArrayBuilderWrapper.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.foundation.protobuf.internal.schema.deserializer.repeated; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +public class PrimitiveArrayBuilderWrapper { + private final PrimitiveArrayBuilder builder; + + private T array; + + public PrimitiveArrayBuilderWrapper(PrimitiveArrayBuilder builder) { + this.builder = builder; + } + + public T getArray() { + return array; + } + + public void setArray(T array) { + this.array = array; + } + + public PrimitiveArrayBuilder getBuilder() { + return builder; + } +} 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 new file mode 100644 index 00000000000..e03a7efd579 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/RepeatedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated; + +import java.io.IOException; +import java.lang.reflect.Array; +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; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class RepeatedReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractReaders readers) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (readers.arrayClass.isAssignableFrom(javaType.getRawClass())) { + return new ArrayRepeatedSchema<>(protoField, propertyDescriptor, readers); + } + + if (Collection.class.isAssignableFrom(javaType.getRawClass()) || javaType.isJavaLangObject()) { + return new CollectionRepeatedSchema<>(protoField, propertyDescriptor, readers); + } + + ProtoUtils.throwNotSupportMerge(protoField, javaType); + return null; + } + + static class AbstractRepeatedSchema extends FieldSchema { + protected final AbstractReaders readers; + + public AbstractRepeatedSchema(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractReaders readers) { + super(protoField, propertyDescriptor.getJavaType()); + this.readers = readers; + } + } + + static class CollectionRepeatedSchema extends AbstractRepeatedSchema { + private final Getter> getter; + + private final Setter> setter; + + public CollectionRepeatedSchema(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractReaders readers) { + super(protoField, propertyDescriptor, readers); + this.getter = propertyDescriptor.getGetter(); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public final int mergeFrom(InputEx input, T message) throws IOException { + Collection collection = getter.get(message); + if (collection == null) { + if (Set.class.equals(javaType.getRawClass())) { + collection = new HashSet<>(); + } else { + collection = new ArrayList<>(); + } + setter.set(message, collection); + } + + return readers.collectionReader.read(input, collection); + } + } + + static class ArrayRepeatedSchema extends AbstractRepeatedSchema { + private final Getter getter; + + private final Setter setter; + + public ArrayRepeatedSchema(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractReaders readers) { + super(protoField, propertyDescriptor, readers); + this.getter = propertyDescriptor.getGetter(); + this.setter = propertyDescriptor.getSetter(); + } + + @SuppressWarnings("unchecked") + @Override + public final int mergeFrom(InputEx input, T message) throws IOException { + ELE_TYPE[] array = getter.get(message); + Collection collection = array == null ? new ArrayList<>() : new ArrayList<>(Arrays.asList(array)); + int fieldNumber = readers.collectionReader.read(input, collection); + + ELE_TYPE[] newArray = (ELE_TYPE[]) Array.newInstance(readers.arrayClass.getComponentType(), collection.size()); + setter.set(message, collection.toArray(newArray)); + return fieldNumber; + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/RepeatedReader.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/RepeatedReader.java new file mode 100644 index 00000000000..fec171f4483 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/RepeatedReader.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated; + +import java.io.IOException; + +import io.protostuff.InputEx; + +public interface RepeatedReader { + int read(InputEx input, T value) throws IOException; +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/AnyRepeatedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/AnyRepeatedReadSchemas.java new file mode 100644 index 00000000000..cdfba2a422b --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/AnyRepeatedReadSchemas.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.any.AnyEntrySchema; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class AnyRepeatedReadSchemas { + private static class MessageReaders extends AbstractReaders { + public MessageReaders(Field protoField, AnyEntrySchema anyEntrySchema) { + super(protoField); + + collectionReader = (input, collection) -> { + while (true) { + Object value = anyEntrySchema.deserialize(input); + collection.add(value); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, + AnyEntrySchema anyEntrySchema) { + return RepeatedReadSchemas.create(protoField, propertyDescriptor, new MessageReaders(protoField, anyEntrySchema)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/BytesRepeatedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/BytesRepeatedReadSchemas.java new file mode 100644 index 00000000000..c1534c99851 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/BytesRepeatedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class BytesRepeatedReadSchemas { + private static class BytesReaders extends AbstractReaders { + public BytesReaders(Field protoField) { + super(protoField); + + collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readByteArray()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedReadSchemas.create(protoField, propertyDescriptor, new BytesReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/MessageRepeatedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/MessageRepeatedReadSchemas.java new file mode 100644 index 00000000000..7f6dfa7c874 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/MessageRepeatedReadSchemas.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; + +import io.protostuff.SchemaReader; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class MessageRepeatedReadSchemas { + private static class MessageReaders extends AbstractReaders { + public MessageReaders(Field protoField, SchemaReader elementSchema) { + super(protoField); + + collectionReader = (input, collection) -> { + while (true) { + Object value = input.mergeObject(null, elementSchema); + collection.add(value); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, + SchemaReader elementSchema) { + return RepeatedReadSchemas.create(protoField, propertyDescriptor, new MessageReaders(protoField, elementSchema)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/PropertyWrapperRepeatedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/PropertyWrapperRepeatedReadSchemas.java new file mode 100644 index 00000000000..0307fb92d63 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/PropertyWrapperRepeatedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; + +import io.protostuff.SchemaReader; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class PropertyWrapperRepeatedReadSchemas { + private static class PropertyWrapperReaders extends AbstractReaders { + public PropertyWrapperReaders(Field protoField, SchemaReader elementSchema) { + super(protoField); + + collectionReader = (input, collection) -> { + while (true) { + PropertyWrapper wrapper = new PropertyWrapper<>(); + input.mergeObject(wrapper, elementSchema); + collection.add(wrapper.getValue()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, + SchemaReader elementSchema) { + return RepeatedReadSchemas + .create(protoField, propertyDescriptor, new PropertyWrapperReaders(protoField, elementSchema)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/StringRepeatedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/StringRepeatedReadSchemas.java new file mode 100644 index 00000000000..2f64516f7e4 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/StringRepeatedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class StringRepeatedReadSchemas { + private static class StringReaders extends AbstractReaders { + public StringReaders(Field protoField) { + super(protoField); + + collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readString()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedReadSchemas.create(protoField, propertyDescriptor, new StringReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/bools/BoolRepeatedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/bools/BoolRepeatedReadSchemas.java new file mode 100644 index 00000000000..cca38bbb756 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/bools/BoolRepeatedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools; + +import java.io.IOException; +import java.util.Arrays; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.PrimitiveArrayBuilderWrapper; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class BoolRepeatedReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractPrimitiveReaders readers) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (boolean[].class.equals(javaType.getRawClass())) { + return new BoolPrimitiveArray<>(protoField, propertyDescriptor, readers); + } + + return RepeatedReadSchemas.create(protoField, propertyDescriptor, readers); + } + + static class BoolPrimitiveArray extends FieldSchema { + private final Getter getter; + + private final Setter setter; + + private final AbstractPrimitiveReaders readers; + + public BoolPrimitiveArray(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractPrimitiveReaders readers) { + super(protoField, propertyDescriptor.getJavaType()); + this.getter = propertyDescriptor.getGetter(); + this.setter = propertyDescriptor.getSetter(); + this.readers = readers; + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + PrimitiveArrayBuilderWrapper builderWrapper = new PrimitiveArrayBuilderWrapper<>( + input.getArrayBuilders().getBooleanBuilder()); + + int fieldNumber = readers.primitiveArrayReader.read(input, builderWrapper); + boolean[] newValue = builderWrapper.getArray(); + newValue = mergeArray(getter.get(message), newValue); + setter.set(message, newValue); + + return fieldNumber; + } + + private boolean[] mergeArray(boolean[] oldValue, boolean[] newValue) { + if (oldValue == null || oldValue.length == 0) { + return newValue; + } + + return concatArray(oldValue, newValue); + } + + private boolean[] concatArray(boolean[] oldValue, boolean[] newValue) { + int len1 = oldValue.length; + int len2 = newValue.length; + boolean[] result = Arrays.copyOf(oldValue, len1 + len2); + System.arraycopy(newValue, 0, result, len1, len2); + return result; + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/bools/impl/BoolNotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/bools/impl/BoolNotPackedReadSchemas.java new file mode 100644 index 00000000000..1aea9b54636 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/bools/impl/BoolNotPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools.BoolRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class BoolNotPackedReadSchemas { + private static class BoolNotPackedReaders extends AbstractPrimitiveReaders { + public BoolNotPackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + boolean[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readBool(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readBool()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return BoolRepeatedReadSchemas.create(protoField, propertyDescriptor, new BoolNotPackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/bools/impl/BoolPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/bools/impl/BoolPackedReadSchemas.java new file mode 100644 index 00000000000..5eb91e57604 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/bools/impl/BoolPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.bools.BoolRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class BoolPackedReadSchemas { + private static class BoolPackedReaders extends AbstractPrimitiveReaders { + public BoolPackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + boolean[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readPackedBool(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readPackedBool()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return BoolRepeatedReadSchemas.create(protoField, propertyDescriptor, new BoolPackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/doubles/DoubleRepeatedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/doubles/DoubleRepeatedReadSchemas.java new file mode 100644 index 00000000000..68d8f997168 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/doubles/DoubleRepeatedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles; + +import java.io.IOException; +import java.util.Arrays; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.PrimitiveArrayBuilderWrapper; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class DoubleRepeatedReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractPrimitiveReaders readers) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (double[].class.equals(javaType.getRawClass())) { + return new PrimitiveArraySchema<>(protoField, propertyDescriptor, readers); + } + + return RepeatedReadSchemas.create(protoField, propertyDescriptor, readers); + } + + static class PrimitiveArraySchema extends FieldSchema { + private final Getter getter; + + private final Setter setter; + + private final AbstractPrimitiveReaders readers; + + public PrimitiveArraySchema(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractPrimitiveReaders readers) { + super(protoField, propertyDescriptor.getJavaType()); + this.getter = propertyDescriptor.getGetter(); + this.setter = propertyDescriptor.getSetter(); + this.readers = readers; + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + PrimitiveArrayBuilderWrapper builderWrapper = new PrimitiveArrayBuilderWrapper<>( + input.getArrayBuilders().getDoubleBuilder()); + + int fieldNumber = readers.primitiveArrayReader.read(input, builderWrapper); + double[] newValue = builderWrapper.getArray(); + newValue = mergeArray(getter.get(message), newValue); + setter.set(message, newValue); + + return fieldNumber; + } + + public double[] mergeArray(double[] oldValue, double[] newValue) { + if (oldValue == null || oldValue.length == 0) { + return newValue; + } + + return concatArray(oldValue, newValue); + } + + private double[] concatArray(double[] oldValue, double[] newValue) { + int len1 = oldValue.length; + int len2 = newValue.length; + double[] result = Arrays.copyOf(oldValue, len1 + len2); + System.arraycopy(newValue, 0, result, len1, len2); + return result; + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/doubles/impl/DoubleNotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/doubles/impl/DoubleNotPackedReadSchemas.java new file mode 100644 index 00000000000..67d4c35ac00 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/doubles/impl/DoubleNotPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles.DoubleRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class DoubleNotPackedReadSchemas { + private static class NotPackedReaders extends AbstractPrimitiveReaders { + public NotPackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + double[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readDouble(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readDouble()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return DoubleRepeatedReadSchemas.create(protoField, propertyDescriptor, new NotPackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/doubles/impl/DoublePackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/doubles/impl/DoublePackedReadSchemas.java new file mode 100644 index 00000000000..e2608f43b9e --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/doubles/impl/DoublePackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.doubles.DoubleRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class DoublePackedReadSchemas { + private static class DoublePackedReaders extends AbstractPrimitiveReaders { + public DoublePackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + double[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readPackedDouble(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readPackedDouble()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return DoubleRepeatedReadSchemas.create(protoField, propertyDescriptor, new DoublePackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/enums/EnumNotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/enums/EnumNotPackedReadSchemas.java new file mode 100644 index 00000000000..a05f3ad671d --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/enums/EnumNotPackedReadSchemas.java @@ -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. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.enums; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.EnumMeta; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.Type; +import io.protostuff.runtime.FieldSchema; + +public class EnumNotPackedReadSchemas { + private static class EnumNotPackedReaders extends AbstractReaders> { + private final EnumMeta enumMeta; + + public EnumNotPackedReaders(Field protoField, JavaType javaType) { + super(protoField, EnumSchemaUtils.constructEnumArrayClass(javaType)); + this.enumMeta = new EnumMeta(protoField, javaType.getContentType()); + + collectionReader = (input, collection) -> { + while (true) { + int value = input.readEnum(); + Enum enumValue = enumMeta.getEnumByValue(value); + if (enumValue == null) { + throw new IllegalStateException( + String.format("invalid enum value %d for %s, proto field=%s:%s", + value, + javaType.getRawClass().getName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + collection.add(enumValue); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + private static class EnumIntNotPackedSchema extends FieldSchema { + private final Getter> getter; + + private final Setter> setter; + + public EnumIntNotPackedSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.getter = propertyDescriptor.getGetter(); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + Collection collection = getter.get(message); + if (collection == null) { + collection = new ArrayList<>(); + setter.set(message, collection); + } + + while (true) { + int value = input.readEnum(); + collection.add(value); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (javaType.isJavaLangObject()) { + return new EnumIntNotPackedSchema<>(protoField, propertyDescriptor); + } + + return RepeatedReadSchemas + .create(protoField, propertyDescriptor, new EnumNotPackedReaders(protoField, propertyDescriptor.getJavaType())); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/enums/EnumPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/enums/EnumPackedReadSchemas.java new file mode 100644 index 00000000000..8a98a18c292 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/enums/EnumPackedReadSchemas.java @@ -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. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.enums; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.EnumMeta; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.Type; +import io.protostuff.runtime.FieldSchema; + +public class EnumPackedReadSchemas { + private static class EnumPackedReaders extends AbstractReaders> { + private final EnumMeta enumMeta; + + public EnumPackedReaders(Field protoField, JavaType javaType) { + super(protoField, EnumSchemaUtils.constructEnumArrayClass(javaType)); + this.enumMeta = new EnumMeta(protoField, javaType.getContentType()); + + collectionReader = (input, collection) -> { + while (true) { + int value = input.readPackedEnum(); + Enum enumValue = enumMeta.getEnumByValue(value); + if (enumValue == null) { + throw new IllegalStateException( + String.format("invalid enum value %d for %s, proto field=%s:%s", + value, + javaType.getRawClass().getName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + collection.add(enumValue); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + private static class EnumIntPackedSchema extends FieldSchema { + private final Getter> getter; + + private final Setter> setter; + + public EnumIntPackedSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.getter = propertyDescriptor.getGetter(); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + Collection collection = getter.get(message); + if (collection == null) { + collection = new ArrayList<>(); + setter.set(message, collection); + } + + while (true) { + int value = input.readPackedEnum(); + collection.add(value); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (javaType.isJavaLangObject()) { + return new EnumIntPackedSchema<>(protoField, propertyDescriptor); + } + + return RepeatedReadSchemas + .create(protoField, propertyDescriptor, new EnumPackedReaders(protoField, propertyDescriptor.getJavaType())); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/enums/EnumSchemaUtils.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/enums/EnumSchemaUtils.java new file mode 100644 index 00000000000..399a4b2df6c --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/enums/EnumSchemaUtils.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.enums; + +import org.apache.servicecomb.foundation.common.utils.ReflectUtils; + +import com.fasterxml.jackson.databind.JavaType; + +public final class EnumSchemaUtils { + private EnumSchemaUtils() { + } + + public static T constructEnumArrayClass(JavaType javaType) { + if (javaType.isArrayType()) { + return ReflectUtils.constructArrayType(javaType.getContentType().getRawClass()); + } + + return ReflectUtils.constructArrayType(Object.class); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/floats/FloatRepeatedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/floats/FloatRepeatedReadSchemas.java new file mode 100644 index 00000000000..82b6d131772 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/floats/FloatRepeatedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats; + +import java.io.IOException; +import java.util.Arrays; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.PrimitiveArrayBuilderWrapper; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class FloatRepeatedReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractPrimitiveReaders readers) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (float[].class.equals(javaType.getRawClass())) { + return new FloatPrimitiveArraySchema<>(protoField, propertyDescriptor, readers); + } + + return RepeatedReadSchemas.create(protoField, propertyDescriptor, readers); + } + + static class FloatPrimitiveArraySchema extends FieldSchema { + private final Getter getter; + + private final Setter setter; + + private final AbstractPrimitiveReaders readers; + + public FloatPrimitiveArraySchema(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractPrimitiveReaders readers) { + super(protoField, propertyDescriptor.getJavaType()); + this.getter = propertyDescriptor.getGetter(); + this.setter = propertyDescriptor.getSetter(); + this.readers = readers; + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + PrimitiveArrayBuilderWrapper builderWrapper = new PrimitiveArrayBuilderWrapper<>( + input.getArrayBuilders().getFloatBuilder()); + + int fieldNumber = readers.primitiveArrayReader.read(input, builderWrapper); + float[] newValue = builderWrapper.getArray(); + newValue = mergeArray(getter.get(message), newValue); + setter.set(message, newValue); + + return fieldNumber; + } + + public float[] mergeArray(float[] oldValue, float[] newValue) { + if (oldValue == null || oldValue.length == 0) { + return newValue; + } + + return concatArray(oldValue, newValue); + } + + private float[] concatArray(float[] oldValue, float[] newValue) { + int len1 = oldValue.length; + int len2 = newValue.length; + float[] result = Arrays.copyOf(oldValue, len1 + len2); + System.arraycopy(newValue, 0, result, len1, len2); + return result; + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/floats/impl/FloatNotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/floats/impl/FloatNotPackedReadSchemas.java new file mode 100644 index 00000000000..8f8c2e12bf8 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/floats/impl/FloatNotPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats.FloatRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class FloatNotPackedReadSchemas { + private static class FloatNotPackedReaders extends AbstractPrimitiveReaders { + public FloatNotPackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + float[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readFloat(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readFloat()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return FloatRepeatedReadSchemas.create(protoField, propertyDescriptor, new FloatNotPackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/floats/impl/FloatPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/floats/impl/FloatPackedReadSchemas.java new file mode 100644 index 00000000000..2e5a15a47f3 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/floats/impl/FloatPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.floats.FloatRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class FloatPackedReadSchemas { + private static class FloatPackedReaders extends AbstractPrimitiveReaders { + public FloatPackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + float[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readPackedFloat(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readPackedFloat()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return FloatRepeatedReadSchemas.create(protoField, propertyDescriptor, new FloatPackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/IntRepeatedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/IntRepeatedReadSchemas.java new file mode 100644 index 00000000000..cd0000d46e1 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/IntRepeatedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints; + +import java.io.IOException; +import java.util.Arrays; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.PrimitiveArrayBuilderWrapper; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class IntRepeatedReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractPrimitiveReaders readers) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (int[].class.equals(javaType.getRawClass())) { + return new IntPrimitiveArraySchema<>(protoField, propertyDescriptor, readers); + } + + return RepeatedReadSchemas.create(protoField, propertyDescriptor, readers); + } + + static class IntPrimitiveArraySchema extends FieldSchema { + private final Getter getter; + + private final Setter setter; + + private final AbstractPrimitiveReaders readers; + + public IntPrimitiveArraySchema(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractPrimitiveReaders readers) { + super(protoField, propertyDescriptor.getJavaType()); + this.getter = propertyDescriptor.getGetter(); + this.setter = propertyDescriptor.getSetter(); + this.readers = readers; + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + PrimitiveArrayBuilderWrapper builderWrapper = new PrimitiveArrayBuilderWrapper<>( + input.getArrayBuilders().getIntBuilder()); + + int fieldNumber = readers.primitiveArrayReader.read(input, builderWrapper); + int[] newValue = builderWrapper.getArray(); + newValue = mergeArray(getter.get(message), newValue); + setter.set(message, newValue); + + return fieldNumber; + } + + public int[] mergeArray(int[] oldValue, int[] newValue) { + if (oldValue == null || oldValue.length == 0) { + return newValue; + } + + return concatArray(oldValue, newValue); + } + + private int[] concatArray(int[] oldValue, int[] newValue) { + int len1 = oldValue.length; + int len2 = newValue.length; + int[] result = Arrays.copyOf(oldValue, len1 + len2); + System.arraycopy(newValue, 0, result, len1, len2); + return result; + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Fixed32NotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Fixed32NotPackedReadSchemas.java new file mode 100644 index 00000000000..acbdc796b6a --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Fixed32NotPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Fixed32NotPackedReadSchemas { + private static class NotPackedReaders extends AbstractPrimitiveReaders { + public NotPackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + int[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readFixed32(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readFixed32()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new NotPackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Fixed32PackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Fixed32PackedReadSchemas.java new file mode 100644 index 00000000000..f8d25de95a7 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Fixed32PackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Fixed32PackedReadSchemas { + private static class Fixed32PackedReaders extends AbstractPrimitiveReaders { + public Fixed32PackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + int[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readPackedFixed32(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readPackedFixed32()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new Fixed32PackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Int32NotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Int32NotPackedReadSchemas.java new file mode 100644 index 00000000000..e5e86f96660 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Int32NotPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Int32NotPackedReadSchemas { + private static class Int32NotPackedReaders extends AbstractPrimitiveReaders { + public Int32NotPackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + int[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readInt32(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readInt32()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new Int32NotPackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Int32PackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Int32PackedReadSchemas.java new file mode 100644 index 00000000000..b578aea5aad --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/Int32PackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Int32PackedReadSchemas { + private static class Int32PackedReaders extends AbstractPrimitiveReaders { + public Int32PackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + int[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readPackedInt32(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readPackedInt32()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new Int32PackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SFixed32NotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SFixed32NotPackedReadSchemas.java new file mode 100644 index 00000000000..2ddf02fe23a --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SFixed32NotPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SFixed32NotPackedReadSchemas { + private static class NotPackedReaders extends AbstractPrimitiveReaders { + public NotPackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + int[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readSFixed32(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readSFixed32()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new NotPackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SFixed32PackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SFixed32PackedReadSchemas.java new file mode 100644 index 00000000000..a4be7f8f739 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SFixed32PackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SFixed32PackedReadSchemas { + private static class SFixed32PackedReaders extends AbstractPrimitiveReaders { + public SFixed32PackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + int[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readPackedSFixed32(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readPackedSFixed32()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new SFixed32PackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SInt32NotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SInt32NotPackedReadSchemas.java new file mode 100644 index 00000000000..3918c128216 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SInt32NotPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SInt32NotPackedReadSchemas { + private static class SInt32NotPackedReaders extends AbstractPrimitiveReaders { + public SInt32NotPackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + int[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readSInt32(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readSInt32()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new SInt32NotPackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SInt32PackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SInt32PackedReadSchemas.java new file mode 100644 index 00000000000..ff7984e266a --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/SInt32PackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SInt32PackedReadSchemas { + private static class SInt32PackedReaders extends AbstractPrimitiveReaders { + public SInt32PackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + int[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readPackedSInt32(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readPackedSInt32()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new SInt32PackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/UInt32NotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/UInt32NotPackedReadSchemas.java new file mode 100644 index 00000000000..dbd719c2773 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/UInt32NotPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class UInt32NotPackedReadSchemas { + private static class UInt32NotPackedReaders extends AbstractPrimitiveReaders { + public UInt32NotPackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + int[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readUInt32(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readUInt32()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new UInt32NotPackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/UInt32PackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/UInt32PackedReadSchemas.java new file mode 100644 index 00000000000..3afff304c77 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/ints/impl/UInt32PackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.ints.IntRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class UInt32PackedReadSchemas { + private static class UInt32PackedReaders extends AbstractPrimitiveReaders { + public UInt32PackedReaders(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + int[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readPackedUInt32(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readPackedUInt32()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return IntRepeatedReadSchemas.create(protoField, propertyDescriptor, new UInt32PackedReaders(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/LongRepeatedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/LongRepeatedReadSchemas.java new file mode 100644 index 00000000000..08308307c16 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/LongRepeatedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs; + +import java.io.IOException; +import java.util.Arrays; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.PrimitiveArrayBuilderWrapper; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.RepeatedReadSchemas; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class LongRepeatedReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractPrimitiveReaders readers) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (long[].class.equals(javaType.getRawClass())) { + return new LongPrimitiveArraySchema<>(protoField, propertyDescriptor, readers); + } + + return RepeatedReadSchemas.create(protoField, propertyDescriptor, readers); + } + + static class LongPrimitiveArraySchema extends FieldSchema { + private final Getter getter; + + private final Setter setter; + + private final AbstractPrimitiveReaders readers; + + public LongPrimitiveArraySchema(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractPrimitiveReaders readers) { + super(protoField, propertyDescriptor.getJavaType()); + this.getter = propertyDescriptor.getGetter(); + this.setter = propertyDescriptor.getSetter(); + this.readers = readers; + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + PrimitiveArrayBuilderWrapper builderWrapper = new PrimitiveArrayBuilderWrapper<>( + input.getArrayBuilders().getLongBuilder()); + + int fieldNumber = readers.primitiveArrayReader.read(input, builderWrapper); + long[] newValue = builderWrapper.getArray(); + newValue = mergeArray(getter.get(message), newValue); + setter.set(message, newValue); + + return fieldNumber; + } + + public long[] mergeArray(long[] oldValue, long[] newValue) { + if (oldValue == null || oldValue.length == 0) { + return newValue; + } + + return concatArray(oldValue, newValue); + } + + private long[] concatArray(long[] oldValue, long[] newValue) { + int len1 = oldValue.length; + int len2 = newValue.length; + long[] result = Arrays.copyOf(oldValue, len1 + len2); + System.arraycopy(newValue, 0, result, len1, len2); + return result; + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Fixed64NotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Fixed64NotPackedReadSchemas.java new file mode 100644 index 00000000000..b02112d82e7 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Fixed64NotPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Fixed64NotPackedReadSchemas { + private static class Readers extends AbstractPrimitiveReaders { + public Readers(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + long[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readFixed64(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readFixed64()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Fixed64PackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Fixed64PackedReadSchemas.java new file mode 100644 index 00000000000..9e768d84917 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Fixed64PackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Fixed64PackedReadSchemas { + private static class Readers extends AbstractPrimitiveReaders { + public Readers(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + long[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readPackedFixed64(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readPackedFixed64()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Int64NotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Int64NotPackedReadSchemas.java new file mode 100644 index 00000000000..c4c6a126fc1 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Int64NotPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Int64NotPackedReadSchemas { + private static class Readers extends AbstractPrimitiveReaders { + public Readers(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + long[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readInt64(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readInt64()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Int64PackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Int64PackedReadSchemas.java new file mode 100644 index 00000000000..051b8bc4620 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/Int64PackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Int64PackedReadSchemas { + private static class Readers extends AbstractPrimitiveReaders { + public Readers(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + long[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readPackedInt64(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readPackedInt64()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SFixed64NotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SFixed64NotPackedReadSchemas.java new file mode 100644 index 00000000000..606dd9712d8 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SFixed64NotPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SFixed64NotPackedReadSchemas { + private static class Readers extends AbstractPrimitiveReaders { + public Readers(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + long[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readSFixed64(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readSFixed64()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SFixed64PackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SFixed64PackedReadSchemas.java new file mode 100644 index 00000000000..85c13b7ef70 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SFixed64PackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SFixed64PackedReadSchemas { + private static class Readers extends AbstractPrimitiveReaders { + public Readers(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + long[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readPackedSFixed64(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readPackedSFixed64()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SInt64NotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SInt64NotPackedReadSchemas.java new file mode 100644 index 00000000000..3f23c9be383 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SInt64NotPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SInt64NotPackedReadSchemas { + private static class Readers extends AbstractPrimitiveReaders { + public Readers(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + long[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readSInt64(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readSInt64()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SInt64PackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SInt64PackedReadSchemas.java new file mode 100644 index 00000000000..a23da754371 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/SInt64PackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SInt64PackedReadSchemas { + private static class Readers extends AbstractPrimitiveReaders { + public Readers(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + long[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readPackedSInt64(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readPackedSInt64()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/UInt64NotPackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/UInt64NotPackedReadSchemas.java new file mode 100644 index 00000000000..482db0cc10d --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/UInt64NotPackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class UInt64NotPackedReadSchemas { + private static class Readers extends AbstractPrimitiveReaders { + public Readers(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + long[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readUInt64(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readUInt64()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/UInt64PackedReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/UInt64PackedReadSchemas.java new file mode 100644 index 00000000000..faceeea7cc1 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/repeated/impl/longs/impl/UInt64PackedReadSchemas.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.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.impl; + +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.AbstractPrimitiveReaders; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.repeated.impl.longs.LongRepeatedReadSchemas; + +import com.fasterxml.jackson.databind.util.PrimitiveArrayBuilder; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class UInt64PackedReadSchemas { + private static class Readers extends AbstractPrimitiveReaders { + public Readers(Field protoField) { + super(protoField); + + this.primitiveArrayReader = (input, builderWrapper) -> { + PrimitiveArrayBuilder builder = builderWrapper.getBuilder(); + long[] chunk = builder.resetAndStart(); + int ix = 0; + + while (true) { + if (ix >= chunk.length) { + chunk = builder.appendCompletedChunk(chunk, ix); + ix = 0; + } + chunk[ix++] = input.readPackedUInt64(); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + builderWrapper.setArray(builder.completeAndClearBuffer(chunk, ix)); + return fieldNumber; + } + } + }; + + this.collectionReader = (input, collection) -> { + while (true) { + collection.add(input.readPackedUInt64()); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return LongRepeatedReadSchemas.create(protoField, propertyDescriptor, new Readers(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/AbstractScalarReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/AbstractScalarReadSchemas.java new file mode 100644 index 00000000000..345fd477df2 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/AbstractScalarReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class AbstractScalarReadSchemas { + abstract static class AbstractIntSchema extends FieldSchema { + protected final Setter setter; + + public AbstractIntSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + } + + abstract static class AbstractLongSchema extends FieldSchema { + protected final Setter setter; + + public AbstractLongSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/BoolReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/BoolReadSchemas.java new file mode 100644 index 00000000000..a6b1e75f40a --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/BoolReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.BoolSetter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class BoolReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (boolean.class.equals(javaType.getRawClass())) { + return new BooleanPrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Boolean.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { + return new BooleanSchema<>(protoField, propertyDescriptor); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class BooleanSchema extends FieldSchema { + private final Setter setter; + + public BooleanSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + boolean value = input.readBool(); + setter.set(message, value); + return input.readFieldNumber(); + } + } + + private static class BooleanPrimitiveSchema extends FieldSchema { + private final BoolSetter setter; + + public BooleanPrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + boolean value = input.readBool(); + setter.set(message, value); + return input.readFieldNumber(); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/BytesReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/BytesReadSchemas.java new file mode 100644 index 00000000000..9c4ec6cdd3e --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/BytesReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class BytesReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (byte[].class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { + return new BytesSchema<>(protoField, propertyDescriptor); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class BytesSchema extends FieldSchema { + private final Setter setter; + + public BytesSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + byte[] value = input.readByteArray(); + setter.set(message, value); + return input.readFieldNumber(); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/DoubleReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/DoubleReadSchemas.java new file mode 100644 index 00000000000..dbac060b730 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/DoubleReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.DoubleSetter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class DoubleReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (double.class.equals(javaType.getRawClass())) { + return new DoublePrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Double.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { + return new DoubleSchema<>(protoField, propertyDescriptor); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class DoubleSchema extends FieldSchema { + private final Setter setter; + + public DoubleSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + double value = input.readDouble(); + setter.set(message, value); + return input.readFieldNumber(); + } + } + + private static class DoublePrimitiveSchema extends FieldSchema { + private final DoubleSetter setter; + + public DoublePrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + double value = input.readDouble(); + setter.set(message, value); + return input.readFieldNumber(); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/EnumsReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/EnumsReadSchemas.java new file mode 100644 index 00000000000..64022ac324f --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/EnumsReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.base.DynamicEnum; +import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +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.EnumMeta; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.Type; +import io.protostuff.runtime.FieldSchema; + +public class EnumsReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (javaType.isEnumType()) { + return new EnumSchema<>(protoField, propertyDescriptor); + } + + if (Integer.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { + return new IntEnumSchema<>(protoField, propertyDescriptor); + } + + if (int.class.equals(javaType.getRawClass())) { + return new IntPrimitiveEnumSchema<>(protoField, propertyDescriptor); + } + + if (javaType.isTypeOrSubTypeOf(DynamicEnum.class)) { + return new DynamicEnumSchema<>(protoField, propertyDescriptor); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class EnumSchema extends FieldSchema { + private final Setter> setter; + + private final EnumMeta enumMeta; + + public EnumSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + enumMeta = new EnumMeta(protoField, javaType); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + int value = input.readInt32(); + Enum enumValue = enumMeta.getEnumByValue(value); + if (enumValue != null) { + setter.set(message, enumValue); + return input.readFieldNumber(); + } + + throw new IllegalStateException( + String.format("invalid enum value %d for %s, proto field=%s:%s", + value, + javaType.getRawClass().getName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + } + + private static class DynamicEnumSchema extends FieldSchema { + public DynamicEnumSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + throw new IllegalStateException( + String.format("currently, protobuf not support dynamic enum, type=%s, proto field=%s:%s", + javaType.getRawClass().getName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + } + + private static class IntEnumSchema extends FieldSchema { + private final Setter setter; + + public IntEnumSchema(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.readInt32(); + setter.set(message, value); + return input.readFieldNumber(); + } + } + + private static class IntPrimitiveEnumSchema extends FieldSchema { + private final IntSetter setter; + + public IntPrimitiveEnumSchema(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.readInt32(); + setter.set(message, value); + return input.readFieldNumber(); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Fixed32ReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Fixed32ReadSchemas.java new file mode 100644 index 00000000000..37a9a4b4911 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Fixed32ReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; +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; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Fixed32ReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (int.class.equals(javaType.getRawClass())) { + return new Fixed32PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Integer.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { + return new Fixed32Schema<>(protoField, propertyDescriptor); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class Fixed32Schema extends AbstractIntSchema { + public Fixed32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + int value = input.readFixed32(); + setter.set(message, value); + return input.readFieldNumber(); + } + } + + private static class Fixed32PrimitiveSchema extends FieldSchema { + protected final IntSetter setter; + + public Fixed32PrimitiveSchema(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.readFixed32(); + setter.set(message, value); + return input.readFieldNumber(); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Fixed64ReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Fixed64ReadSchemas.java new file mode 100644 index 00000000000..ddd1cbf6911 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Fixed64ReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.LongSetter; +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.AbstractLongSchema; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Fixed64ReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (long.class.equals(javaType.getRawClass())) { + return new Fixed64PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Long.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { + return new Fixed64Schema<>(protoField, propertyDescriptor); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class Fixed64Schema extends AbstractLongSchema { + public Fixed64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + long value = input.readFixed64(); + setter.set(message, value); + return input.readFieldNumber(); + } + } + + private static class Fixed64PrimitiveSchema extends FieldSchema { + protected final LongSetter setter; + + public Fixed64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + long value = input.readFixed64(); + setter.set(message, value); + return input.readFieldNumber(); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/FloatReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/FloatReadSchemas.java new file mode 100644 index 00000000000..46879c17b7a --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/FloatReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.FloatSetter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class FloatReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (float.class.equals(javaType.getRawClass())) { + return new FloatPrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Float.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { + return new FloatSchema<>(protoField, propertyDescriptor); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class FloatSchema extends FieldSchema { + private final Setter setter; + + public FloatSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + float value = input.readFloat(); + setter.set(message, value); + return input.readFieldNumber(); + } + } + + private static class FloatPrimitiveSchema extends FieldSchema { + private final FloatSetter setter; + + public FloatPrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + float value = input.readFloat(); + setter.set(message, value); + return input.readFieldNumber(); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Int32ReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Int32ReadSchemas.java new file mode 100644 index 00000000000..644102f23a0 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Int32ReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +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; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Int32ReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (int.class.equals(javaType.getRawClass())) { + return new IntFieldIntPrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (short.class.equals(javaType.getRawClass())) { + return new ShortFieldIntPrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (byte.class.equals(javaType.getRawClass())) { + return new ByteFieldIntPrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Integer.class.equals(javaType.getRawClass()) || Byte.class.equals(javaType.getRawClass()) || Short.class + .equals(javaType.getRawClass()) || javaType + .isJavaLangObject()) { + return new Int32Schema<>(protoField, propertyDescriptor); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class Int32Schema extends AbstractIntSchema { + public Int32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + int value = input.readInt32(); + 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(); + } + } + + private static class IntFieldIntPrimitiveSchema extends FieldSchema { + protected final IntSetter setter; + + public IntFieldIntPrimitiveSchema(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.readInt32(); + setter.set(message, value); + return input.readFieldNumber(); + } + } + + private static class ShortFieldIntPrimitiveSchema extends FieldSchema { + protected final ShortSetter setter; + + public ShortFieldIntPrimitiveSchema(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.readInt32(); + setter.set(message, (short) value); + return input.readFieldNumber(); + } + } + + private static class ByteFieldIntPrimitiveSchema extends FieldSchema { + protected final ByteSetter setter; + + public ByteFieldIntPrimitiveSchema(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.readInt32(); + 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/Int64ReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Int64ReadSchemas.java new file mode 100644 index 00000000000..0befd0a7a26 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/Int64ReadSchemas.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar; + +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Date; + +import org.apache.servicecomb.foundation.common.utils.bean.LongSetter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.scalar.AbstractScalarReadSchemas.AbstractLongSchema; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Int64ReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (long.class.equals(javaType.getRawClass())) { + return new LongFiledLongPrimitiveSchema<>(protoField, propertyDescriptor); + } + + return new Int64Schema<>(protoField, propertyDescriptor); + } + + private static class Int64Schema extends AbstractLongSchema { + public Int64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + long value = input.readInt64(); + if (Date.class.equals(javaType.getRawClass())) { + setter.set(message, new Date(value)); + } else if (LocalDate.class.equals(javaType.getRawClass())) { + setter.set(message, LocalDate.ofEpochDay(value)); + } else if (LocalDateTime.class.equals(javaType.getRawClass())) { + setter.set(message, LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.UTC)); + } else { + setter.set(message, value); + } + return input.readFieldNumber(); + } + } + + private static class LongFiledLongPrimitiveSchema extends FieldSchema { + protected final LongSetter setter; + + public LongFiledLongPrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + long value = input.readInt64(); + setter.set(message, value); + return input.readFieldNumber(); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SFixed32ReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SFixed32ReadSchemas.java new file mode 100644 index 00000000000..dcd6530f6a4 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SFixed32ReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; +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; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SFixed32ReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (int.class.equals(javaType.getRawClass())) { + return new SFixed32PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Integer.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { + return new SFixed32Schema<>(protoField, propertyDescriptor); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class SFixed32Schema extends AbstractIntSchema { + public SFixed32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + int value = input.readSFixed32(); + setter.set(message, value); + return input.readFieldNumber(); + } + } + + private static class SFixed32PrimitiveSchema extends FieldSchema { + protected final IntSetter setter; + + public SFixed32PrimitiveSchema(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.readSFixed32(); + setter.set(message, value); + return input.readFieldNumber(); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SFixed64ReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SFixed64ReadSchemas.java new file mode 100644 index 00000000000..f1d4f8fc882 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SFixed64ReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.LongSetter; +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.AbstractLongSchema; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SFixed64ReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (long.class.equals(javaType.getRawClass())) { + return new SFixed64PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Long.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { + return new SFixed64Schema<>(protoField, propertyDescriptor); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class SFixed64Schema extends AbstractLongSchema { + public SFixed64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + long value = input.readSFixed64(); + setter.set(message, value); + return input.readFieldNumber(); + } + } + + private static class SFixed64PrimitiveSchema extends FieldSchema { + protected final LongSetter setter; + + public SFixed64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + long value = input.readSFixed64(); + setter.set(message, value); + return input.readFieldNumber(); + } + } +} 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 new file mode 100644 index 00000000000..8252e2a96ca --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SInt32ReadSchemas.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.deserializer.scalar; + +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; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SInt32ReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (int.class.equals(javaType.getRawClass())) { + return new SInt32PrimitiveSchema<>(protoField, propertyDescriptor); + } + + 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); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class SInt32Schema extends AbstractIntSchema { + public SInt32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + int value = input.readSInt32(); + 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(); + } + } + + private static class SInt32PrimitiveSchema extends FieldSchema { + protected final IntSetter setter; + + public SInt32PrimitiveSchema(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, value); + 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/SInt64ReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SInt64ReadSchemas.java new file mode 100644 index 00000000000..ff3c80c150c --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/SInt64ReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.LongSetter; +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.AbstractLongSchema; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SInt64ReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (long.class.equals(javaType.getRawClass())) { + return new SInt64PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Long.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { + return new SInt64Schema<>(protoField, propertyDescriptor); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class SInt64Schema extends AbstractLongSchema { + public SInt64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + long value = input.readSInt64(); + setter.set(message, value); + return input.readFieldNumber(); + } + } + + private static class SInt64PrimitiveSchema extends FieldSchema { + protected final LongSetter setter; + + public SInt64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + long value = input.readSInt64(); + setter.set(message, 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 new file mode 100644 index 00000000000..7b1f0a441b0 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/StringReadSchemas.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class StringReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + + if (char.class.equals(javaType.getRawClass())) { + return new CharFieldStringSchema<>(protoField, propertyDescriptor); + } + + if (String.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject() || Character.class + .equals(javaType.getRawClass())) { + 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; + } + + private static class StringSchema extends FieldSchema { + private final Setter setter; + + public StringSchema(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(); + if (char.class + .equals(javaType.getRawClass()) || Character.class.equals(javaType.getRawClass())) { + setter.set(message, value.toCharArray()[0]); + } else { + setter.set(message, value); + } + return input.readFieldNumber(); + } + } + + private static class CharFieldStringSchema extends FieldSchema { + private final CharSetter setter; + + public CharFieldStringSchema(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, value.toCharArray()[0]); + 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/deserializer/scalar/UInt32ReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/UInt32ReadSchemas.java new file mode 100644 index 00000000000..61ef71b64e4 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/UInt32ReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; +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; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class UInt32ReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (int.class.equals(javaType.getRawClass())) { + return new UInt32PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Integer.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { + return new UInt32Schema<>(protoField, propertyDescriptor); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class UInt32Schema extends AbstractIntSchema { + public UInt32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + int value = input.readUInt32(); + setter.set(message, value); + return input.readFieldNumber(); + } + } + + private static class UInt32PrimitiveSchema extends FieldSchema { + protected final IntSetter setter; + + public UInt32PrimitiveSchema(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.readUInt32(); + setter.set(message, value); + return input.readFieldNumber(); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/UInt64ReadSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/UInt64ReadSchemas.java new file mode 100644 index 00000000000..0a098a7152f --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/scalar/UInt64ReadSchemas.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.foundation.protobuf.internal.schema.deserializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.LongSetter; +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.AbstractLongSchema; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class UInt64ReadSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (long.class.equals(javaType.getRawClass())) { + return new UInt64PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Long.class.equals(javaType.getRawClass()) || javaType.isJavaLangObject()) { + return new UInt64Schema<>(protoField, propertyDescriptor); + } + + ProtoUtils.throwNotSupportMerge(protoField, propertyDescriptor.getJavaType()); + return null; + } + + private static class UInt64Schema extends AbstractLongSchema { + public UInt64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + long value = input.readUInt64(); + setter.set(message, value); + return input.readFieldNumber(); + } + } + + private static class UInt64PrimitiveSchema extends FieldSchema { + protected final LongSetter setter; + + public UInt64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public int mergeFrom(InputEx input, T message) throws IOException { + long value = input.readUInt64(); + setter.set(message, value); + return input.readFieldNumber(); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/map/MapEntry.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/map/MapEntry.java new file mode 100644 index 00000000000..b4b1ee75c9b --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/map/MapEntry.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.foundation.protobuf.internal.schema.map; + +import java.util.Map.Entry; + +import com.fasterxml.jackson.annotation.JsonSetter; + +public class MapEntry implements Entry { + private K key; + + private V value; + + public K getKey() { + return key; + } + + public void setKey(K key) { + this.key = key; + } + + public V getValue() { + return value; + } + + public V setValue(V value) { + this.value = value; + return value; + } + + @JsonSetter("value") + public void valueSetter(V value) { + this.value = value; + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/map/MapEntrySchema.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/map/MapEntrySchema.java new file mode 100644 index 00000000000..7f575ce11af --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/map/MapEntrySchema.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.map; + +import java.io.IOException; +import java.util.Map.Entry; + +import org.apache.servicecomb.foundation.protobuf.internal.schema.deserializer.MessageReadSchema; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.MessageWriteSchema; + +import io.protostuff.InputEx; +import io.protostuff.OutputEx; +import io.protostuff.SchemaEx; +import io.protostuff.runtime.FieldSchema; + +public class MapEntrySchema implements SchemaEx> { + private final FieldSchema> keySchema; + + private final FieldSchema> valueSchema; + + public MapEntrySchema(SchemaEx> entrySchema) { + if (entrySchema instanceof MessageWriteSchema) { + keySchema = ((MessageWriteSchema>) entrySchema).getMainPojoFieldMaps().getFieldByNumber(1); + valueSchema = ((MessageWriteSchema>) entrySchema).getMainPojoFieldMaps() + .getFieldByNumber(2); + return; + } + + keySchema = ((MessageReadSchema>) entrySchema).getFieldMap().getFieldByNumber(1); + valueSchema = ((MessageReadSchema>) entrySchema).getFieldMap().getFieldByNumber(2); + } + + @Override + public void init() { + + } + + @Override + public void mergeFrom(InputEx input, Entry message) throws IOException { + input.readFieldNumber(); + keySchema.mergeFrom(input, message); + valueSchema.mergeFrom(input, message); + } + + @Override + public void writeTo(OutputEx output, Entry value) throws IOException { + keySchema.writeTo(output, value.getKey()); + valueSchema.writeTo(output, value.getValue()); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/map/MapSchema.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/map/MapSchema.java new file mode 100644 index 00000000000..38d269c6d23 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/map/MapSchema.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.map; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.InputEx; +import io.protostuff.OutputEx; +import io.protostuff.SchemaEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class MapSchema extends FieldSchema { + private final Getter> getter; + + private final Setter> setter; + + private final SchemaEx> entrySchema; + + public MapSchema(Field protoField, PropertyDescriptor propertyDescriptor, + SchemaEx> entrySchema) { + super(protoField, propertyDescriptor.getJavaType()); + this.entrySchema = new MapEntrySchema(entrySchema); + this.getter = propertyDescriptor.getGetter(); + this.setter = propertyDescriptor.getSetter(); + } + + @Override + public final int mergeFrom(InputEx input, T message) throws IOException { + Map map = getter.get(message); + if (map == null) { + map = new LinkedHashMap<>(); + setter.set(message, map); + } + + Entry entry = new MapEntry<>(); + while (true) { + input.mergeObject(entry, entrySchema); + map.put(entry.getKey(), entry.getValue()); + entry.setValue(null); + + int fieldNumber = input.readFieldNumber(); + if (fieldNumber != this.fieldNumber) { + return fieldNumber; + } + } + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Map map = getter.get(message); + if (map == null) { + return; + } + + writeMap(output, map); + } + + @SuppressWarnings("unchecked") + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + writeMap(output, (Map) value); + } + + private void writeMap(OutputEx output, Map map) throws IOException { + for (Entry entry : map.entrySet()) { + if (entry.getKey() == null || entry.getValue() == null) { + continue; + } + + output.writeObject(tag, tagSize, entry, entrySchema); + } + } +} 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 new file mode 100644 index 00000000000..ccab4d81f82 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java @@ -0,0 +1,199 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.BeanDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.InputEx; +import io.protostuff.OutputEx; +import io.protostuff.SchemaEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.Message; +import io.protostuff.runtime.FieldMapEx; +import io.protostuff.runtime.FieldSchema; + +/** + *
+ * map.put("user", new User())
+ * root write from map, but user should write from pojo
+ * so one schema should support dynamic and concrete logic at the same time
+ * 
+ */ +public class MessageWriteSchema implements SchemaEx { + protected final ProtoMapper protoMapper; + + protected final Message message; + + private final JavaType javaType; + + // mostly, one message only relate to one pojo + private final Class mainPojoCls; + + private FieldMapEx mainPojoFieldMaps; + + private FieldMapEx> mapFieldMaps; + + // if not equals to mainPojoCls, then will find from pojoFieldMaps + private final Map, FieldMapEx> pojoFieldMaps = new ConcurrentHashMapEx<>(); + + @SuppressWarnings("unchecked") + public MessageWriteSchema(ProtoMapper protoMapper, Message message, JavaType javaType) { + this.protoMapper = protoMapper; + this.message = message; + this.javaType = javaType; + this.mainPojoCls = (Class) javaType.getRawClass(); + } + + public Message getMessage() { + return message; + } + + @Override + public T newMessage() { + throw new UnsupportedOperationException(); + } + + @Override + public String messageName() { + return message.getName(); + } + + public JavaType getJavaType() { + return javaType; + } + + public Class getMainPojoCls() { + return mainPojoCls; + } + + public FieldMapEx getMainPojoFieldMaps() { + return mainPojoFieldMaps; + } + + @Override + public void init() { + this.mapFieldMaps = protoMapper.getSerializerSchemaManager().createMapFields(message); + + if (ProtoUtils.isWrapProperty(message)) { + this.mainPojoFieldMaps = createPropertyWrapperFields(javaType); + return; + } + + this.mainPojoFieldMaps = createPojoFields(javaType); + } + + private FieldMapEx createPropertyWrapperFields(JavaType javaType) { + Field protoField = message.getField(1); + + PropertyDescriptor propertyDescriptor = new PropertyDescriptor(); + propertyDescriptor.setName(protoField.getName()); + propertyDescriptor.setJavaType(javaType); + + FieldSchema fieldSchema = protoMapper.getSerializerSchemaManager() + .createSchemaField(protoField, propertyDescriptor); + return FieldMapEx.createFieldMap(Arrays.asList(fieldSchema)); + } + + private FieldMapEx createPojoFields(Type type) { + SerializerSchemaManager serializerSchemaManager = protoMapper.getSerializerSchemaManager(); + BeanDescriptor beanDescriptor = protoMapper.getBeanDescriptorManager().getOrCreateBeanDescriptor(type); + + List> fieldSchemas = new ArrayList<>(); + for (Field protoField : message.getFields()) { + PropertyDescriptor propertyDescriptor = beanDescriptor.getPropertyDescriptors().get(protoField.getName()); + if (propertyDescriptor == null) { + continue; + } + + Object getter = propertyDescriptor.getGetter(); + if (getter == null) { + continue; + } + + FieldSchema fieldSchema = serializerSchemaManager.createSchemaField(protoField, propertyDescriptor); + fieldSchemas.add(fieldSchema); + } + + return FieldMapEx.createFieldMap(fieldSchemas); + } + + @SuppressWarnings("unchecked") + @Override + public void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Map) { + writeFromMap(output, (Map) value); + return; + } + + if (mainPojoCls == value.getClass()) { + writeFromMainPojo(output, (T) value); + return; + } + + writeDynamicPojo(output, value); + } + + private void writeFromMainPojo(OutputEx output, T value) throws IOException { + for (FieldSchema fieldSchema : mainPojoFieldMaps.getFields()) { + fieldSchema.getAndWriteTo(output, value); + } + } + + @SuppressWarnings("unchecked") + private void writeDynamicPojo(OutputEx output, Object dynamicValue) throws IOException { + FieldMapEx fieldMapEx = (FieldMapEx) this.pojoFieldMaps + .computeIfAbsent(dynamicValue.getClass(), this::createPojoFields); + + T value = (T) dynamicValue; + for (FieldSchema fieldSchema : fieldMapEx.getFields()) { + fieldSchema.getAndWriteTo(output, value); + } + } + + protected final void writeFromMap(OutputEx output, Map map) throws IOException { + for (Entry entry : map.entrySet()) { + if (entry.getValue() == null) { + continue; + } + + FieldSchema> fieldSchema = mapFieldMaps.getFieldByName(entry.getKey()); + if (fieldSchema != null) { + fieldSchema.writeTo(output, entry.getValue()); + } + } + } + + @Override + public void mergeFrom(InputEx input, T message) throws IOException { + throw new UnsupportedOperationException(); + } +} 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 new file mode 100644 index 00000000000..d63944e3423 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java @@ -0,0 +1,240 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.serializer; + +import static org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils.isWrapProperty; + +import java.lang.reflect.Type; +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.RootSerializer; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; +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.SchemaManager; +import org.apache.servicecomb.foundation.protobuf.internal.schema.any.AnyEntrySchema; +import org.apache.servicecomb.foundation.protobuf.internal.schema.any.AnySchema; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.AnyRepeatedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.BytesRepeatedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.MessagesRepeatedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.PropertyWrapperRepeatedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.StringsRepeatedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.bools.BoolNotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.bools.BoolPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.doubles.DoubleNotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.doubles.DoublePackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.enums.EnumNotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.enums.EnumPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.floats.FloatNotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.floats.FloatPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.Fixed32NotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.Fixed32PackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.Int32NotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.Int32PackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.SFixed32NotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.SFixed32PackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.SInt32NotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.SInt32PackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.UInt32NotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints.UInt32PackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.Fixed64NotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.Fixed64PackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.Int64NotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.Int64PackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.SFixed64NotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.SFixed64PackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.SInt64NotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.SInt64PackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.UInt64NotPackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs.UInt64PackedWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.BoolWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.BytesWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.DoubleWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.EnumWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.Fixed32WriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.Fixed64WriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.FloatWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.Int32WriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.Int64WriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.SFixed32WriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.SFixed64WriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.SInt32WriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.SInt64WriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.StringWriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.UInt32WriteSchemas; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar.UInt64WriteSchemas; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.protostuff.SchemaEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.Message; +import io.protostuff.compiler.model.ScalarFieldType; +import io.protostuff.runtime.FieldSchema; + +public class SerializerSchemaManager extends SchemaManager { + public SerializerSchemaManager(ProtoMapper protoMapper) { + super(protoMapper); + } + + public RootSerializer createRootSerializer(Message message, Type type) { + if (ProtoUtils.isAnyMessage(message)) { + SchemaEx messageSchema = new AnyEntrySchema(protoMapper, type); + return new RootSerializer(messageSchema); + } + JavaType javaType = TypeFactory.defaultInstance().constructType(type); + SchemaEx messageSchema = getOrCreateMessageSchema(message, javaType); + return new RootSerializer(messageSchema); + } + + public RootSerializer createRootSerializer(Message message, Map types) { + throw new IllegalStateException("not implemented"); + } + + @Override + protected SchemaEx newMessageSchema(Message message, JavaType javaType) { + return new MessageWriteSchema<>(protoMapper, message, javaType); + } + + @Override + protected SchemaEx newMessageSchema(Message message, Map types) { + throw new IllegalStateException("not implemented"); + } + + protected FieldSchema createScalarField(Field protoField, PropertyDescriptor propertyDescriptor) { + if (protoField.getType().isEnum()) { + return EnumWriteSchemas.create(protoField, propertyDescriptor); + } + + switch ((ScalarFieldType) protoField.getType()) { + case INT32: + return Int32WriteSchemas.create(protoField, propertyDescriptor); + case UINT32: + return UInt32WriteSchemas.create(protoField, propertyDescriptor); + case SINT32: + return SInt32WriteSchemas.create(protoField, propertyDescriptor); + case FIXED32: + return Fixed32WriteSchemas.create(protoField, propertyDescriptor); + case SFIXED32: + return SFixed32WriteSchemas.create(protoField, propertyDescriptor); + case INT64: + return Int64WriteSchemas.create(protoField, propertyDescriptor); + case UINT64: + return UInt64WriteSchemas.create(protoField, propertyDescriptor); + case SINT64: + return SInt64WriteSchemas.create(protoField, propertyDescriptor); + case FIXED64: + return Fixed64WriteSchemas.create(protoField, propertyDescriptor); + case SFIXED64: + return SFixed64WriteSchemas.create(protoField, propertyDescriptor); + case FLOAT: + return FloatWriteSchemas.create(protoField, propertyDescriptor); + case DOUBLE: + return DoubleWriteSchemas.create(protoField, propertyDescriptor); + case BOOL: + return BoolWriteSchemas.create(protoField, propertyDescriptor); + case STRING: + return StringWriteSchemas.create(protoField, propertyDescriptor); + case BYTES: + return BytesWriteSchemas.create(protoField, propertyDescriptor); + default: + throw new IllegalStateException("unknown proto field type: " + protoField.getType()); + } + } + + @Override + protected FieldSchema createRepeatedSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + boolean packed = ProtoUtils.isPacked(protoField); + if (protoField.getType().isEnum()) { + return packed ? EnumPackedWriteSchemas.create(protoField, propertyDescriptor) : + EnumNotPackedWriteSchemas.create(protoField, propertyDescriptor); + } + + if (protoField.getType().isScalar()) { + switch ((ScalarFieldType) protoField.getType()) { + case INT32: + return packed ? Int32PackedWriteSchemas.create(protoField, propertyDescriptor) : + Int32NotPackedWriteSchemas.create(protoField, propertyDescriptor); + case UINT32: + return packed ? UInt32PackedWriteSchemas.create(protoField, propertyDescriptor) : + UInt32NotPackedWriteSchemas.create(protoField, propertyDescriptor); + case SINT32: + return packed ? SInt32PackedWriteSchemas.create(protoField, propertyDescriptor) : + SInt32NotPackedWriteSchemas.create(protoField, propertyDescriptor); + case FIXED32: + return packed ? Fixed32PackedWriteSchemas.create(protoField, propertyDescriptor) : + Fixed32NotPackedWriteSchemas.create(protoField, propertyDescriptor); + case SFIXED32: + return packed ? SFixed32PackedWriteSchemas.create(protoField, propertyDescriptor) : + SFixed32NotPackedWriteSchemas.create(protoField, propertyDescriptor); + case INT64: + return packed ? Int64PackedWriteSchemas.create(protoField, propertyDescriptor) : + Int64NotPackedWriteSchemas.create(protoField, propertyDescriptor); + case UINT64: + return packed ? UInt64PackedWriteSchemas.create(protoField, propertyDescriptor) : + UInt64NotPackedWriteSchemas.create(protoField, propertyDescriptor); + case SINT64: + return packed ? SInt64PackedWriteSchemas.create(protoField, propertyDescriptor) : + SInt64NotPackedWriteSchemas.create(protoField, propertyDescriptor); + case FIXED64: + return packed ? Fixed64PackedWriteSchemas.create(protoField, propertyDescriptor) : + Fixed64NotPackedWriteSchemas.create(protoField, propertyDescriptor); + case SFIXED64: + return packed ? SFixed64PackedWriteSchemas.create(protoField, propertyDescriptor) : + SFixed64NotPackedWriteSchemas.create(protoField, propertyDescriptor); + case FLOAT: + return packed ? FloatPackedWriteSchemas.create(protoField, propertyDescriptor) : + FloatNotPackedWriteSchemas.create(protoField, propertyDescriptor); + case DOUBLE: + return packed ? DoublePackedWriteSchemas.create(protoField, propertyDescriptor) : + DoubleNotPackedWriteSchemas.create(protoField, propertyDescriptor); + case BOOL: + return packed ? BoolPackedWriteSchemas.create(protoField, propertyDescriptor) : + BoolNotPackedWriteSchemas.create(protoField, propertyDescriptor); + case STRING: + return StringsRepeatedWriteSchemas.create(protoField, propertyDescriptor); + case BYTES: + return BytesRepeatedWriteSchemas.create(protoField, propertyDescriptor); + default: + ProtoUtils.throwNotSupportWrite(protoField, propertyDescriptor.getJavaType().getRawClass()); + } + } + + if (ProtoUtils.isAnyField(protoField)) { + FieldSchema anySchema = new AnySchema<>(protoMapper, protoField, propertyDescriptor); + return AnyRepeatedWriteSchemas.create(protoField, propertyDescriptor, anySchema); + } + + if (protoField.getType().isMessage()) { + JavaType contentType = propertyDescriptor.getJavaType().getContentType(); + if (contentType == null) { + contentType = ProtoConst.OBJECT_TYPE; + } + SchemaEx contentSchema = createMessageSchema((Message) protoField.getType(), contentType); + if (isWrapProperty((Message) protoField.getType())) { + return PropertyWrapperRepeatedWriteSchemas.create(protoField, propertyDescriptor, contentSchema); + } + + return MessagesRepeatedWriteSchemas.create(protoField, propertyDescriptor, contentSchema); + } + + ProtoUtils.throwNotSupportWrite(protoField, propertyDescriptor.getJavaType().getRawClass()); + return null; + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/AbstractPrimitiveWriters.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/AbstractPrimitiveWriters.java new file mode 100644 index 00000000000..e244797ea9f --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/AbstractPrimitiveWriters.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.foundation.protobuf.internal.schema.serializer.repeated; + +import static org.apache.servicecomb.foundation.common.utils.ReflectUtils.getFieldArgument; + +import java.io.IOException; +import java.util.Collection; + +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; + +import io.protostuff.OutputEx; +import io.protostuff.SchemaWriter; +import io.protostuff.compiler.model.Field; + +public abstract class AbstractPrimitiveWriters extends + AbstractWriters { + public SchemaWriter primitiveArrayWriter; + + public final Class primitiveArrayClass; + + @SuppressWarnings("unchecked") + public AbstractPrimitiveWriters(Field protoField) { + super(protoField); + + this.primitiveArrayClass = getFieldArgument(this.getClass(), "primitiveArrayClass"); + } + + @SuppressWarnings("unchecked") + public final void dynamicWriteTo(OutputEx output, Object value) throws IOException { + // from normal model + if (primitiveArrayClass.isInstance(value)) { + primitiveArrayWriter.writeTo(output, (PRIMITIVE_ARRAY) value); + return; + } + + if (arrayClass.isInstance(value)) { + arrayWriter.writeTo(output, (PRIMITIVE_WRAPPER[]) value); + return; + } + + if (value instanceof Collection) { + collectionWriter.writeTo(output, (Collection) value); + return; + } + + // from http request + if (value instanceof String[]) { + stringArrayWriter.writeTo(output, (String[]) value); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/AbstractWriters.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/AbstractWriters.java new file mode 100644 index 00000000000..bb784ff7632 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/AbstractWriters.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.serializer.repeated; + +import static org.apache.servicecomb.foundation.common.utils.ReflectUtils.getFieldArgument; + +import java.io.IOException; +import java.util.Collection; + +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; + +import io.protostuff.OutputEx; +import io.protostuff.ProtobufOutputEx; +import io.protostuff.SchemaWriter; +import io.protostuff.WireFormat; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldTypeUtils; + +public abstract class AbstractWriters { + protected final Field protoField; + + protected final int tag; + + protected final int tagSize; + + public SchemaWriter arrayWriter; + + public SchemaWriter> collectionWriter; + + public SchemaWriter stringArrayWriter; + + public final Class arrayClass; + + @SuppressWarnings("unchecked") + public AbstractWriters(Field protoField) { + this(protoField, null); + } + + public AbstractWriters(Field protoField, Class arrayClass) { + this.protoField = protoField; + int wireType = ProtoUtils.isPacked(protoField) && protoField.isRepeated() ? WireFormat.WIRETYPE_LENGTH_DELIMITED + : FieldTypeUtils.convert(protoField.getType()).wireType; + this.tag = WireFormat.makeTag(protoField.getTag(), wireType); + this.tagSize = ProtobufOutputEx.computeRawVarint32Size(tag); + + if (arrayClass == null) { + arrayClass = getFieldArgument(this.getClass(), "arrayWriter"); + } + this.arrayClass = arrayClass; + } + + @SuppressWarnings("unchecked") + public void dynamicWriteTo(OutputEx output, Object value) throws IOException { + if (value instanceof Collection) { + collectionWriter.writeTo(output, (Collection) value); + return; + } + + // from normal model + if (arrayClass.isInstance(value)) { + arrayWriter.writeTo(output, (T[]) value); + return; + } + + // from http request + if (value instanceof String[]) { + stringArrayWriter.writeTo(output, (String[]) value); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/RepeatedPrimitiveWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/RepeatedPrimitiveWriteSchemas.java new file mode 100644 index 00000000000..8b01e83f8d8 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/RepeatedPrimitiveWriteSchemas.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.foundation.protobuf.internal.schema.serializer.repeated; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas.DynamicSchema; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class RepeatedPrimitiveWriteSchemas { + public static FieldSchema create(Field protoField, + PropertyDescriptor propertyDescriptor, + AbstractPrimitiveWriters writers) { + JavaType javaType = propertyDescriptor.getJavaType(); + if (writers.primitiveArrayClass == javaType.getRawClass()) { + return new PrimitiveArraySchema<>(protoField, propertyDescriptor, writers); + } + + return RepeatedWriteSchemas.create(protoField, propertyDescriptor, writers); + } + + private static class PrimitiveArraySchema extends + DynamicSchema { + private final Getter getter; + + private final AbstractPrimitiveWriters primitiveWriters; + + public PrimitiveArraySchema(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractPrimitiveWriters writers) { + super(protoField, propertyDescriptor, writers); + this.getter = propertyDescriptor.getGetter(); + this.primitiveWriters = writers; + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + PRIMITIVE_ARRAY value = getter.get(message); + if (value == null) { + return; + } + + primitiveWriters.primitiveArrayWriter.writeTo(output, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/RepeatedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/RepeatedWriteSchemas.java new file mode 100644 index 00000000000..5d0a332a09e --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/RepeatedWriteSchemas.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.foundation.protobuf.internal.schema.serializer.repeated; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class RepeatedWriteSchemas { + public static FieldSchema create(Field protoField, + PropertyDescriptor propertyDescriptor, + AbstractWriters writers) { + return new CollectionSchema<>(protoField, propertyDescriptor, writers); + } + + static class DynamicSchema extends FieldSchema { + protected final AbstractWriters writers; + + @SuppressWarnings("unchecked") + public DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractWriters writers) { + super(protoField, propertyDescriptor.getJavaType()); + + this.writers = writers; + } + + @SuppressWarnings("unchecked") + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + writers.dynamicWriteTo(output, value); + } + } + + private static class CollectionSchema extends DynamicSchema { + private final Getter getter; + + public CollectionSchema(Field protoField, PropertyDescriptor propertyDescriptor, + AbstractWriters writers) { + super(protoField, propertyDescriptor, writers); + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value == null) { + return; + } + this.writeTo(output, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/AnyRepeatedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/AnyRepeatedWriteSchemas.java new file mode 100644 index 00000000000..03324385c64 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/AnyRepeatedWriteSchemas.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl; + +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.serializer.repeated.AbstractWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class AnyRepeatedWriteSchemas { + private static class AnyWriters extends AbstractWriters { + public AnyWriters(Field protoField, FieldSchema anySchema) { + super(protoField); + + arrayWriter = (output, array) -> { + for (Object element : array) { + if (element != null) { + anySchema.writeTo(output, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (output, collection) -> { + for (Object element : collection) { + if (element != null) { + anySchema.writeTo(output, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + @SuppressWarnings("unchecked") + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, + FieldSchema anySchema) { + return RepeatedWriteSchemas.create(protoField, propertyDescriptor, new AnyWriters<>(protoField, anySchema)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/BytesRepeatedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/BytesRepeatedWriteSchemas.java new file mode 100644 index 00000000000..64c009cf2fe --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/BytesRepeatedWriteSchemas.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.foundation.protobuf.internal.schema.serializer.repeated.impl; + +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.serializer.repeated.AbstractWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class BytesRepeatedWriteSchemas { + private static class BytesWriters extends AbstractWriters { + public BytesWriters(Field protoField) { + super(protoField); + + arrayWriter = (output, array) -> { + for (byte[] element : array) { + if (element != null) { + output.writeByteArray(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (output, collection) -> { + for (byte[] element : collection) { + if (element != null) { + output.writeByteArray(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + @SuppressWarnings("unchecked") + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedWriteSchemas.create(protoField, propertyDescriptor, new BytesWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/MessagesRepeatedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/MessagesRepeatedWriteSchemas.java new file mode 100644 index 00000000000..702a849eef2 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/MessagesRepeatedWriteSchemas.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.foundation.protobuf.internal.schema.serializer.repeated.impl; + +import java.lang.reflect.Array; + +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.serializer.MessageWriteSchema; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; + +import io.protostuff.SchemaWriter; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class MessagesRepeatedWriteSchemas { + private static class MessageWriters extends AbstractWriters { + @SuppressWarnings("unchecked") + public MessageWriters(Field protoField, SchemaWriter elementSchema) { + super(protoField, + (Class) Array.newInstance(((MessageWriteSchema) elementSchema).getMainPojoCls(), 0) + .getClass()); + + arrayWriter = (output, array) -> { + for (Object element : array) { + if (element != null) { + output.writeObject(tag, tagSize, element, elementSchema); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (output, collection) -> { + for (Object element : collection) { + if (element != null) { + output.writeObject(tag, tagSize, element, elementSchema); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + @SuppressWarnings("unchecked") + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, + SchemaWriter elementSchema) { + return RepeatedWriteSchemas.create(protoField, propertyDescriptor, new MessageWriters(protoField, elementSchema)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/PropertyWrapperRepeatedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/PropertyWrapperRepeatedWriteSchemas.java new file mode 100644 index 00000000000..829fd94d155 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/PropertyWrapperRepeatedWriteSchemas.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.foundation.protobuf.internal.schema.serializer.repeated.impl; + +import java.lang.reflect.Array; + +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.serializer.MessageWriteSchema; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.SchemaWriter; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class PropertyWrapperRepeatedWriteSchemas { + private static class PropertyWrapperWriters extends AbstractWriters { + public PropertyWrapperWriters(Field protoField, SchemaWriter elementSchema, Class arrayClass) { + super(protoField, arrayClass); + + FieldSchema fieldSchema = + (elementSchema instanceof MessageWriteSchema) ? ((MessageWriteSchema) elementSchema) + .getMainPojoFieldMaps() + .getFieldByNumber(1) : null; + arrayWriter = (output, array) -> { + for (Object element : array) { + if (element != null) { + output.writeObject(tag, tagSize, element, fieldSchema::writeTo); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (output, collection) -> { + for (Object element : collection) { + if (element != null) { + output.writeObject(tag, tagSize, element, fieldSchema::writeTo); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + @SuppressWarnings("unchecked") + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor, + SchemaWriter elementSchema) { + JavaType contentType = propertyDescriptor.getJavaType().getContentType(); + Class contentClass = contentType == null ? Object.class : (Class) contentType.getRawClass(); + Class arrayClass = (Class) Array.newInstance(contentClass, 0).getClass(); + return RepeatedWriteSchemas + .create(protoField, propertyDescriptor, new PropertyWrapperWriters(protoField, elementSchema, arrayClass)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/StringsRepeatedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/StringsRepeatedWriteSchemas.java new file mode 100644 index 00000000000..16e71edcda0 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/StringsRepeatedWriteSchemas.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.foundation.protobuf.internal.schema.serializer.repeated.impl; + +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.serializer.repeated.AbstractWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class StringsRepeatedWriteSchemas { + private static class StringWriters extends AbstractWriters { + public StringWriters(Field protoField) { + super(protoField); + + arrayWriter = (output, array) -> { + for (String element : array) { + if (element != null) { + output.writeString(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (output, collection) -> { + for (String element : collection) { + if (element != null) { + output.writeString(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + @SuppressWarnings("unchecked") + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedWriteSchemas.create(protoField, propertyDescriptor, new StringWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/bools/BoolNotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/bools/BoolNotPackedWriteSchemas.java new file mode 100644 index 00000000000..64d024bc279 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/bools/BoolNotPackedWriteSchemas.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.protobuf.internal.schema.serializer.repeated.impl.bools; + +import java.util.Collection; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class BoolNotPackedWriteSchemas { + private static class BoolNotPackedWriters extends AbstractPrimitiveWriters { + public BoolNotPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (OutputEx output, boolean[] array) -> { + for (boolean element : array) { + output.writeBool(tag, tagSize, element); + } + }; + + arrayWriter = (OutputEx output, Boolean[] array) -> { + for (Boolean element : array) { + if (element != null) { + output.writeBool(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (OutputEx output, Collection collection) -> { + for (Boolean element : collection) { + if (element != null) { + output.writeBool(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + stringArrayWriter = (OutputEx output, String[] array) -> { + for (String element : array) { + if (element != null) { + boolean parsedValue = Boolean.parseBoolean(element); + output.writeBool(tag, tagSize, parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new BoolNotPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/bools/BoolPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/bools/BoolPackedWriteSchemas.java new file mode 100644 index 00000000000..699e82e4f06 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/bools/BoolPackedWriteSchemas.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.bools; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class BoolPackedWriteSchemas { + private static class BoolPackedWriters extends AbstractPrimitiveWriters { + public BoolPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (boolean element : array) { + output.writePackedBool(element); + } + }); + + arrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (Boolean element : array) { + if (element != null) { + output.writePackedBool(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + collectionWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, collection) -> { + for (Boolean element : collection) { + if (element != null) { + output.writePackedBool(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + stringArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element != null) { + boolean parsedValue = Boolean.parseBoolean(element); + output.writePackedBool(parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new BoolPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/doubles/DoubleNotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/doubles/DoubleNotPackedWriteSchemas.java new file mode 100644 index 00000000000..1bafb7c690f --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/doubles/DoubleNotPackedWriteSchemas.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.protobuf.internal.schema.serializer.repeated.impl.doubles; + +import java.util.Collection; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class DoubleNotPackedWriteSchemas { + private static class DoubleNotPackedWriters extends AbstractPrimitiveWriters { + public DoubleNotPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (OutputEx output, double[] array) -> { + for (double element : array) { + output.writeDouble(tag, tagSize, element); + } + }; + + arrayWriter = (OutputEx output, Double[] array) -> { + for (Double element : array) { + if (element != null) { + output.writeDouble(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (OutputEx output, Collection collection) -> { + for (Double element : collection) { + if (element != null) { + output.writeDouble(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + stringArrayWriter = (OutputEx output, String[] array) -> { + for (String element : array) { + if (element != null) { + double parsedValue = Double.parseDouble(element); + output.writeDouble(tag, tagSize, parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new DoubleNotPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/doubles/DoublePackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/doubles/DoublePackedWriteSchemas.java new file mode 100644 index 00000000000..7de41eb5709 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/doubles/DoublePackedWriteSchemas.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.doubles; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class DoublePackedWriteSchemas { + private static class DoublePackedWriters extends AbstractPrimitiveWriters { + public DoublePackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (double element : array) { + output.writePackedDouble(element); + } + }); + + arrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (Double element : array) { + if (element != null) { + output.writePackedDouble(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + collectionWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, collection) -> { + for (Double element : collection) { + if (element != null) { + output.writePackedDouble(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + stringArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element != null) { + double parsedValue = Double.parseDouble(element); + output.writePackedDouble(parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new DoublePackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/enums/EnumNotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/enums/EnumNotPackedWriteSchemas.java new file mode 100644 index 00000000000..220c9e03953 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/enums/EnumNotPackedWriteSchemas.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.enums; + +import java.io.IOException; +import java.util.Collection; + +import org.apache.servicecomb.foundation.common.utils.ReflectUtils; +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.EnumMeta; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.Type; +import io.protostuff.runtime.FieldSchema; + +public class EnumNotPackedWriteSchemas { + private static class EnumNotPackedWriters extends AbstractWriters> { + private final EnumMeta enumMeta; + + @SuppressWarnings("unchecked") + public EnumNotPackedWriters(Field protoField, JavaType javaType) { + super(protoField, ReflectUtils.constructArrayType(Enum.class)); + this.enumMeta = new EnumMeta(protoField, javaType); + + arrayWriter = (output, array) -> { + for (Enum element : array) { + if (element == null) { + ProtoUtils.throwNotSupportNullElement(protoField); + return; + } + + String name = element.name(); + Integer enumValue = enumMeta.getValueByName(name); + if (enumValue == null) { + throw new IllegalStateException( + String.format("invalid enum name %s for proto %s, field=%s:%s", + name, + protoField.getTypeName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + output.writeEnum(tag, tagSize, enumValue); + } + }; + + collectionWriter = (output, collection) -> { + if (collection.isEmpty()) { + return; + } + + Object first = collection.iterator().next(); + if (first.getClass().isEnum()) { + writeEnumCollection(output, collection); + return; + } + + if (first.getClass() == String.class) { + writeStringCollection(output, (Collection) (Object) collection); + return; + } + + writeIntCollection(output, (Collection) (Object) collection); + }; + + stringArrayWriter = (output, array) -> { + for (String element : array) { + if (element == null) { + ProtoUtils.throwNotSupportNullElement(protoField); + return; + } + + Integer enumValue = enumMeta.getValueByName(element); + if (enumValue == null) { + throw new IllegalStateException( + String.format("invalid enum name %s for proto %s, field=%s:%s", + element, + protoField.getTypeName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + output.writeEnum(tag, tagSize, enumValue); + } + }; + } + + private void writeStringCollection(OutputEx output, Collection collection) throws IOException { + for (String element : collection) { + if (element == null) { + ProtoUtils.throwNotSupportNullElement(protoField); + return; + } + + Integer enumValue = enumMeta.getValueByName(element); + if (enumValue == null) { + throw new IllegalStateException( + String.format("invalid enum name %s for proto %s, field=%s:%s", + element, + protoField.getTypeName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + output.writeEnum(tag, tagSize, enumValue); + } + } + + private void writeIntCollection(OutputEx output, Collection collection) throws IOException { + for (Number element : collection) { + if (element == null) { + ProtoUtils.throwNotSupportNullElement(protoField); + return; + } + + output.writeInt32(tag, tagSize, element.intValue()); + } + } + + private void writeEnumCollection(OutputEx output, Collection> collection) throws IOException { + for (Enum element : collection) { + if (element == null) { + ProtoUtils.throwNotSupportNullElement(protoField); + return; + } + + String name = element.name(); + Integer enumValue = enumMeta.getValueByName(name); + if (enumValue == null) { + throw new IllegalStateException( + String.format("invalid enum name %s for proto %s, field=%s:%s", + name, + protoField.getTypeName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + output.writeEnum(tag, tagSize, enumValue); + } + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedWriteSchemas.create(protoField, propertyDescriptor, + new EnumNotPackedWriters(protoField, propertyDescriptor.getJavaType())); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/enums/EnumPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/enums/EnumPackedWriteSchemas.java new file mode 100644 index 00000000000..36b270c25c8 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/enums/EnumPackedWriteSchemas.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.serializer.repeated.impl.enums; + +import java.io.IOException; +import java.util.Collection; + +import org.apache.servicecomb.foundation.common.utils.ReflectUtils; +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.EnumMeta; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.AbstractWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedWriteSchemas; + +import com.fasterxml.jackson.databind.JavaType; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.Type; +import io.protostuff.runtime.FieldSchema; + +public class EnumPackedWriteSchemas { + private static class EnumsPackedWriters extends AbstractWriters> { + private final EnumMeta enumMeta; + + @SuppressWarnings("unchecked") + public EnumsPackedWriters(Field protoField, JavaType javaType) { + super(protoField, ReflectUtils.constructArrayType(Enum.class)); + this.enumMeta = new EnumMeta(protoField, javaType); + + arrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { + for (Enum element : array) { + if (element == null) { + ProtoUtils.throwNotSupportNullElement(protoField); + return; + } + + String name = element.name(); + Integer enumValue = enumMeta.getValueByName(name); + if (enumValue == null) { + throw new IllegalStateException( + String.format("invalid enum name %s for proto %s, field=%s:%s", + name, + protoField.getTypeName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + output.writePackedEnum(enumValue); + } + }); + + collectionWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, collection) -> { + Object first = collection.iterator().next(); + if (first.getClass().isEnum()) { + writeEnumCollection(output, collection); + return; + } + + if (first.getClass() == String.class) { + writeStringCollection(output, (Collection) (Object) collection); + return; + } + + writeIntCollection(output, (Collection) (Object) collection); + }); + + stringArrayWriter = (o, value) -> o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element == null) { + ProtoUtils.throwNotSupportNullElement(protoField); + return; + } + + Integer enumValue = enumMeta.getValueByName(element); + if (enumValue == null) { + throw new IllegalStateException( + String.format("invalid enum name %s for proto %s, field=%s:%s", + element, + protoField.getTypeName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + output.writePackedEnum(enumValue); + } + }); + } + + private void writeStringCollection(OutputEx output, Collection collection) throws IOException { + for (String element : collection) { + if (element == null) { + ProtoUtils.throwNotSupportNullElement(protoField); + return; + } + + Integer enumValue = enumMeta.getValueByName(element); + if (enumValue == null) { + throw new IllegalStateException( + String.format("invalid enum name %s for proto %s, field=%s:%s", + element, + protoField.getTypeName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + output.writePackedEnum(enumValue); + } + } + + private void writeIntCollection(OutputEx output, Collection collection) throws IOException { + for (Number element : collection) { + if (element == null) { + ProtoUtils.throwNotSupportNullElement(protoField); + return; + } + + output.writePackedInt32(element.intValue()); + } + } + + private void writeEnumCollection(OutputEx output, Collection> collection) throws IOException { + for (Enum element : collection) { + if (element == null) { + ProtoUtils.throwNotSupportNullElement(protoField); + return; + } + + String name = element.name(); + Integer enumValue = enumMeta.getValueByName(name); + if (enumValue == null) { + throw new IllegalStateException( + String.format("invalid enum name %s for proto %s, field=%s:%s", + name, + protoField.getTypeName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + output.writePackedEnum(enumValue); + } + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedWriteSchemas + .create(protoField, propertyDescriptor, new EnumsPackedWriters(protoField, propertyDescriptor.getJavaType())); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/floats/FloatNotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/floats/FloatNotPackedWriteSchemas.java new file mode 100644 index 00000000000..82f2f377565 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/floats/FloatNotPackedWriteSchemas.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.protobuf.internal.schema.serializer.repeated.impl.floats; + +import java.util.Collection; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class FloatNotPackedWriteSchemas { + private static class FloatNotPackedWriters extends AbstractPrimitiveWriters { + public FloatNotPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (OutputEx output, float[] array) -> { + for (float element : array) { + output.writeFloat(tag, tagSize, element); + } + }; + + arrayWriter = (OutputEx output, Float[] array) -> { + for (Float element : array) { + if (element != null) { + output.writeFloat(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (OutputEx output, Collection collection) -> { + for (Float element : collection) { + if (element != null) { + output.writeFloat(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + stringArrayWriter = (OutputEx output, String[] array) -> { + for (String element : array) { + if (element != null) { + float parsedValue = Float.parseFloat(element); + output.writeFloat(tag, tagSize, parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new FloatNotPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/floats/FloatPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/floats/FloatPackedWriteSchemas.java new file mode 100644 index 00000000000..5d6d2d4c29e --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/floats/FloatPackedWriteSchemas.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.floats; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class FloatPackedWriteSchemas { + private static class FloatPackedWriters extends AbstractPrimitiveWriters { + public FloatPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (float element : array) { + output.writePackedFloat(element); + } + }); + + arrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (Float element : array) { + if (element != null) { + output.writePackedFloat(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + collectionWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, collection) -> { + for (Float element : collection) { + if (element != null) { + output.writePackedFloat(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + stringArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element != null) { + float parsedValue = Float.parseFloat(element); + output.writePackedFloat(parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new FloatPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Fixed32NotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Fixed32NotPackedWriteSchemas.java new file mode 100644 index 00000000000..4e05ac2fe64 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Fixed32NotPackedWriteSchemas.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints; + +import java.util.Collection; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Fixed32NotPackedWriteSchemas { + private static class Fixed32NotPackedWriters extends AbstractPrimitiveWriters { + public Fixed32NotPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (OutputEx output, int[] array) -> { + for (int element : array) { + output.writeFixed32(tag, tagSize, element); + } + }; + + arrayWriter = (OutputEx output, Integer[] array) -> { + for (Integer element : array) { + if (element != null) { + output.writeFixed32(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (OutputEx output, Collection collection) -> { + for (Integer element : collection) { + if (element != null) { + output.writeFixed32(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + stringArrayWriter = (OutputEx output, String[] array) -> { + for (String element : array) { + if (element != null) { + int parsedValue = Integer.parseInt(element, 10); + output.writeFixed32(tag, tagSize, parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas + .create(protoField, propertyDescriptor, new Fixed32NotPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Fixed32PackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Fixed32PackedWriteSchemas.java new file mode 100644 index 00000000000..9a5c99569a4 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Fixed32PackedWriteSchemas.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.serializer.repeated.impl.ints; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Fixed32PackedWriteSchemas { + private static class Fixed32PackedWriters extends AbstractPrimitiveWriters { + public Fixed32PackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (int element : array) { + output.writePackedFixed32(element); + } + }); + }; + + arrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (Integer element : array) { + if (element != null) { + output.writePackedFixed32(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + + collectionWriter = (o, value) -> { + if (value.isEmpty()) { + return; + } + o.writeObject(tag, tagSize, value, (output, collection) -> { + for (Integer element : collection) { + if (element != null) { + output.writePackedFixed32(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + + stringArrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element != null) { + int parsedValue = Integer.parseInt(element, 10); + output.writePackedFixed32(parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new Fixed32PackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Int32NotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Int32NotPackedWriteSchemas.java new file mode 100644 index 00000000000..55d61bf4bf9 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Int32NotPackedWriteSchemas.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.protobuf.internal.schema.serializer.repeated.impl.ints; + +import java.util.Collection; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Int32NotPackedWriteSchemas { + private static class Int32NotPackedWriters extends AbstractPrimitiveWriters { + public Int32NotPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (OutputEx output, int[] array) -> { + for (int element : array) { + output.writeInt32(tag, tagSize, element); + } + }; + + arrayWriter = (OutputEx output, Integer[] array) -> { + for (Integer element : array) { + if (element != null) { + output.writeInt32(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (OutputEx output, Collection collection) -> { + for (Integer element : collection) { + if (element != null) { + output.writeInt32(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + stringArrayWriter = (OutputEx output, String[] array) -> { + for (String element : array) { + if (element != null) { + int parsedValue = Integer.parseInt(element, 10); + output.writeInt32(tag, tagSize, parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new Int32NotPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Int32PackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Int32PackedWriteSchemas.java new file mode 100644 index 00000000000..5f8b8568b27 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/Int32PackedWriteSchemas.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.serializer.repeated.impl.ints; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Int32PackedWriteSchemas { + private static class Int32PackedWriters extends AbstractPrimitiveWriters { + public Int32PackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (int element : array) { + output.writePackedInt32(element); + } + }); + }; + + arrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (Integer element : array) { + if (element != null) { + output.writePackedInt32(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + + collectionWriter = (o, value) -> { + if (value.isEmpty()) { + return; + } + o.writeObject(tag, tagSize, value, (output, collection) -> { + for (Integer element : collection) { + if (element != null) { + output.writePackedInt32(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + + stringArrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element != null) { + int parsedValue = Integer.parseInt(element, 10); + output.writePackedInt32(parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new Int32PackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SFixed32NotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SFixed32NotPackedWriteSchemas.java new file mode 100644 index 00000000000..2bf91cf5221 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SFixed32NotPackedWriteSchemas.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.ints; + +import java.util.Collection; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SFixed32NotPackedWriteSchemas { + private static class SFixed32NotPackedWriters extends AbstractPrimitiveWriters { + public SFixed32NotPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (OutputEx output, int[] array) -> { + for (int element : array) { + output.writeSFixed32(tag, tagSize, element); + } + }; + + arrayWriter = (OutputEx output, Integer[] array) -> { + for (Integer element : array) { + if (element != null) { + output.writeSFixed32(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (OutputEx output, Collection collection) -> { + for (Integer element : collection) { + if (element != null) { + output.writeSFixed32(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + stringArrayWriter = (OutputEx output, String[] array) -> { + for (String element : array) { + if (element != null) { + int parsedValue = Integer.parseInt(element, 10); + output.writeSFixed32(tag, tagSize, parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas + .create(protoField, propertyDescriptor, new SFixed32NotPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SFixed32PackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SFixed32PackedWriteSchemas.java new file mode 100644 index 00000000000..7e0272e5ae6 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SFixed32PackedWriteSchemas.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.serializer.repeated.impl.ints; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SFixed32PackedWriteSchemas { + private static class SFixed32PackedWriters extends AbstractPrimitiveWriters { + public SFixed32PackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (int element : array) { + output.writePackedSFixed32(element); + } + }); + }; + + arrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (Integer element : array) { + if (element != null) { + output.writePackedSFixed32(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + + collectionWriter = (o, value) -> { + if (value.isEmpty()) { + return; + } + o.writeObject(tag, tagSize, value, (output, collection) -> { + for (Integer element : collection) { + if (element != null) { + output.writePackedSFixed32(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + + stringArrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element != null) { + int parsedValue = Integer.parseInt(element, 10); + output.writePackedSFixed32(parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new SFixed32PackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SInt32NotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SInt32NotPackedWriteSchemas.java new file mode 100644 index 00000000000..a1ce05dd544 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SInt32NotPackedWriteSchemas.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.protobuf.internal.schema.serializer.repeated.impl.ints; + +import java.util.Collection; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SInt32NotPackedWriteSchemas { + private static class SInt32NotPackedWriters extends AbstractPrimitiveWriters { + public SInt32NotPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (OutputEx output, int[] array) -> { + for (int element : array) { + output.writeSInt32(tag, tagSize, element); + } + }; + + arrayWriter = (OutputEx output, Integer[] array) -> { + for (Integer element : array) { + if (element != null) { + output.writeSInt32(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (OutputEx output, Collection collection) -> { + for (Integer element : collection) { + if (element != null) { + output.writeSInt32(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + stringArrayWriter = (OutputEx output, String[] array) -> { + for (String element : array) { + if (element != null) { + int parsedValue = Integer.parseInt(element, 10); + output.writeSInt32(tag, tagSize, parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new SInt32NotPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SInt32PackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SInt32PackedWriteSchemas.java new file mode 100644 index 00000000000..202ae2f0068 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/SInt32PackedWriteSchemas.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.serializer.repeated.impl.ints; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SInt32PackedWriteSchemas { + private static class SInt32PackedWriters extends AbstractPrimitiveWriters { + public SInt32PackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (int element : array) { + output.writePackedSInt32(element); + } + }); + }; + + arrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (Integer element : array) { + if (element != null) { + output.writePackedSInt32(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + + collectionWriter = (o, value) -> { + if (value.isEmpty()) { + return; + } + o.writeObject(tag, tagSize, value, (output, collection) -> { + for (Integer element : collection) { + if (element != null) { + output.writePackedSInt32(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + + stringArrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element != null) { + int parsedValue = Integer.parseInt(element, 10); + output.writePackedSInt32(parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new SInt32PackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/UInt32NotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/UInt32NotPackedWriteSchemas.java new file mode 100644 index 00000000000..daffa24eea2 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/UInt32NotPackedWriteSchemas.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.protobuf.internal.schema.serializer.repeated.impl.ints; + +import java.util.Collection; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class UInt32NotPackedWriteSchemas { + private static class UInt32NotPackedWriters extends AbstractPrimitiveWriters { + public UInt32NotPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (OutputEx output, int[] array) -> { + for (int element : array) { + output.writeUInt32(tag, tagSize, element); + } + }; + + arrayWriter = (OutputEx output, Integer[] array) -> { + for (Integer element : array) { + if (element != null) { + output.writeUInt32(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (OutputEx output, Collection collection) -> { + for (Integer element : collection) { + if (element != null) { + output.writeUInt32(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + stringArrayWriter = (OutputEx output, String[] array) -> { + for (String element : array) { + if (element != null) { + int parsedValue = Integer.parseInt(element, 10); + output.writeUInt32(tag, tagSize, parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new UInt32NotPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/UInt32PackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/UInt32PackedWriteSchemas.java new file mode 100644 index 00000000000..61be0eed41e --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/ints/UInt32PackedWriteSchemas.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.serializer.repeated.impl.ints; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class UInt32PackedWriteSchemas { + private static class UInt32PackedWriters extends AbstractPrimitiveWriters { + public UInt32PackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (int element : array) { + output.writePackedUInt32(element); + } + }); + }; + + arrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (Integer element : array) { + if (element != null) { + output.writePackedUInt32(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + + collectionWriter = (o, value) -> { + if (value.isEmpty()) { + return; + } + o.writeObject(tag, tagSize, value, (output, collection) -> { + for (Integer element : collection) { + if (element != null) { + output.writePackedUInt32(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + + stringArrayWriter = (o, value) -> { + if (value.length == 0) { + return; + } + o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element != null) { + int parsedValue = Integer.parseInt(element, 10); + output.writePackedUInt32(parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new UInt32PackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Fixed64NotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Fixed64NotPackedWriteSchemas.java new file mode 100644 index 00000000000..63bbaa053b8 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Fixed64NotPackedWriteSchemas.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.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Fixed64NotPackedWriteSchemas { + private static class Fixed64NotPackedWriters extends AbstractPrimitiveWriters { + public Fixed64NotPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (output, array) -> { + for (long element : array) { + output.writeFixed64(tag, tagSize, element); + } + }; + + arrayWriter = (output, array) -> { + for (Long element : array) { + if (element != null) { + output.writeFixed64(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (output, collection) -> { + for (Long element : collection) { + if (element != null) { + output.writeFixed64(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + stringArrayWriter = (output, array) -> { + for (String element : array) { + if (element != null) { + long parsedValue = Long.parseLong(element, 10); + output.writeFixed64(tag, tagSize, parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas + .create(protoField, propertyDescriptor, new Fixed64NotPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Fixed64PackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Fixed64PackedWriteSchemas.java new file mode 100644 index 00000000000..35271482dc2 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Fixed64PackedWriteSchemas.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Fixed64PackedWriteSchemas { + private static class Fixed64PackedWriters extends AbstractPrimitiveWriters { + public Fixed64PackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (long element : array) { + output.writePackedFixed64(element); + } + }); + + arrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (Long element : array) { + if (element != null) { + output.writePackedFixed64(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + collectionWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, collection) -> { + for (Long element : collection) { + if (element != null) { + output.writePackedFixed64(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + stringArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element != null) { + long parsedValue = Long.parseLong(element, 10); + output.writePackedFixed64(parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new Fixed64PackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Int64NotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Int64NotPackedWriteSchemas.java new file mode 100644 index 00000000000..2c7f6a708b5 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Int64NotPackedWriteSchemas.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.protobuf.internal.schema.serializer.repeated.impl.longs; + +import java.util.Collection; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Int64NotPackedWriteSchemas { + private static class Int64NotPackedWriters extends AbstractPrimitiveWriters { + public Int64NotPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (OutputEx output, long[] array) -> { + for (long element : array) { + output.writeInt64(tag, tagSize, element); + } + }; + + arrayWriter = (OutputEx output, Long[] array) -> { + for (Long element : array) { + if (element != null) { + output.writeInt64(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (OutputEx output, Collection collection) -> { + for (Long element : collection) { + if (element != null) { + output.writeInt64(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + stringArrayWriter = (OutputEx output, String[] array) -> { + for (String element : array) { + if (element != null) { + long parsedValue = Long.parseLong(element, 10); + output.writeInt64(tag, tagSize, parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new Int64NotPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Int64PackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Int64PackedWriteSchemas.java new file mode 100644 index 00000000000..1cc9f48f374 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/Int64PackedWriteSchemas.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Int64PackedWriteSchemas { + private static class Int64PackedWriters extends AbstractPrimitiveWriters { + public Int64PackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (long element : array) { + output.writePackedInt64(element); + } + }); + + arrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (Long element : array) { + if (element != null) { + output.writePackedInt64(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + collectionWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, collection) -> { + for (Long element : collection) { + if (element != null) { + output.writePackedInt64(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + stringArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element != null) { + long parsedValue = Long.parseLong(element, 10); + output.writePackedInt64(parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new Int64PackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SFixed64NotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SFixed64NotPackedWriteSchemas.java new file mode 100644 index 00000000000..1b0ba7cf44c --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SFixed64NotPackedWriteSchemas.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; + +import java.util.Collection; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SFixed64NotPackedWriteSchemas { + private static class SFixed64NotPackedWriters extends AbstractPrimitiveWriters { + public SFixed64NotPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (OutputEx output, long[] array) -> { + for (long element : array) { + output.writeSFixed64(tag, tagSize, element); + } + }; + + arrayWriter = (OutputEx output, Long[] array) -> { + for (Long element : array) { + if (element != null) { + output.writeSFixed64(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (OutputEx output, Collection collection) -> { + for (Long element : collection) { + if (element != null) { + output.writeSFixed64(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + stringArrayWriter = (OutputEx output, String[] array) -> { + for (String element : array) { + if (element != null) { + long parsedValue = Long.parseLong(element, 10); + output.writeSFixed64(tag, tagSize, parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas + .create(protoField, propertyDescriptor, new SFixed64NotPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SFixed64PackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SFixed64PackedWriteSchemas.java new file mode 100644 index 00000000000..b33c5efcd82 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SFixed64PackedWriteSchemas.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SFixed64PackedWriteSchemas { + private static class SFixed64PackedWriters extends AbstractPrimitiveWriters { + public SFixed64PackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (long element : array) { + output.writePackedSFixed64(element); + } + }); + + arrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (Long element : array) { + if (element != null) { + output.writePackedSFixed64(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + collectionWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, collection) -> { + for (Long element : collection) { + if (element != null) { + output.writePackedSFixed64(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + stringArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element != null) { + long parsedValue = Long.parseLong(element, 10); + output.writePackedSFixed64(parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new SFixed64PackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SInt64NotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SInt64NotPackedWriteSchemas.java new file mode 100644 index 00000000000..95e8db52016 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SInt64NotPackedWriteSchemas.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.protobuf.internal.schema.serializer.repeated.impl.longs; + +import java.util.Collection; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SInt64NotPackedWriteSchemas { + private static class SInt64NotPackedWriters extends AbstractPrimitiveWriters { + public SInt64NotPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (OutputEx output, long[] array) -> { + for (long element : array) { + output.writeSInt64(tag, tagSize, element); + } + }; + + arrayWriter = (OutputEx output, Long[] array) -> { + for (Long element : array) { + if (element != null) { + output.writeSInt64(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (OutputEx output, Collection collection) -> { + for (Long element : collection) { + if (element != null) { + output.writeSInt64(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + stringArrayWriter = (OutputEx output, String[] array) -> { + for (String element : array) { + if (element != null) { + long parsedValue = Long.parseLong(element, 10); + output.writeSInt64(tag, tagSize, parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new SInt64NotPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SInt64PackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SInt64PackedWriteSchemas.java new file mode 100644 index 00000000000..429e2314b33 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/SInt64PackedWriteSchemas.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SInt64PackedWriteSchemas { + private static class SInt64PackedWriters extends AbstractPrimitiveWriters { + public SInt64PackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (long element : array) { + output.writePackedSInt64(element); + } + }); + + arrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (Long element : array) { + if (element != null) { + output.writePackedSInt64(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + collectionWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, collection) -> { + for (Long element : collection) { + if (element != null) { + output.writePackedSInt64(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + stringArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element != null) { + long parsedValue = Long.parseLong(element, 10); + output.writePackedSInt64(parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new SInt64PackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/UInt64NotPackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/UInt64NotPackedWriteSchemas.java new file mode 100644 index 00000000000..ebc10b21cdf --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/UInt64NotPackedWriteSchemas.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.protobuf.internal.schema.serializer.repeated.impl.longs; + +import java.util.Collection; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class UInt64NotPackedWriteSchemas { + private static class UInt64NotPackedWriters extends AbstractPrimitiveWriters { + public UInt64NotPackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (OutputEx output, long[] array) -> { + for (long element : array) { + output.writeUInt64(tag, tagSize, element); + } + }; + + arrayWriter = (OutputEx output, Long[] array) -> { + for (Long element : array) { + if (element != null) { + output.writeUInt64(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + collectionWriter = (OutputEx output, Collection collection) -> { + for (Long element : collection) { + if (element != null) { + output.writeUInt64(tag, tagSize, element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + + stringArrayWriter = (OutputEx output, String[] array) -> { + for (String element : array) { + if (element != null) { + long parsedValue = Long.parseLong(element, 10); + output.writeUInt64(tag, tagSize, parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }; + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new UInt64NotPackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/UInt64PackedWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/UInt64PackedWriteSchemas.java new file mode 100644 index 00000000000..95511a44f0d --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/repeated/impl/longs/UInt64PackedWriteSchemas.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.impl.longs; + +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.serializer.repeated.AbstractPrimitiveWriters; +import org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.repeated.RepeatedPrimitiveWriteSchemas; + +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class UInt64PackedWriteSchemas { + private static class UInt64PackedWriters extends AbstractPrimitiveWriters { + public UInt64PackedWriters(Field protoField) { + super(protoField); + + primitiveArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (long element : array) { + output.writePackedUInt64(element); + } + }); + + arrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (Long element : array) { + if (element != null) { + output.writePackedUInt64(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + collectionWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, collection) -> { + for (Long element : collection) { + if (element != null) { + output.writePackedUInt64(element); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + + stringArrayWriter = (o, value) -> + o.writeObject(tag, tagSize, value, (output, array) -> { + for (String element : array) { + if (element != null) { + long parsedValue = Long.parseLong(element, 10); + output.writePackedUInt64(parsedValue); + continue; + } + + ProtoUtils.throwNotSupportNullElement(protoField); + } + }); + } + } + + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + return RepeatedPrimitiveWriteSchemas.create(protoField, propertyDescriptor, new UInt64PackedWriters(protoField)); + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/BoolWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/BoolWriteSchemas.java new file mode 100644 index 00000000000..6c57b6f2211 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/BoolWriteSchemas.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.BoolGetter; +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class BoolWriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (boolean.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new BooleanPrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Boolean.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new BooleanSchema<>(protoField, propertyDescriptor); + } + + return new BooleanSchema<>(protoField, propertyDescriptor); + } + + private static class BooleanDynamicSchema extends FieldSchema { + public BooleanDynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Boolean) { + output.writeScalarBool(tag, tagSize, (boolean) value); + return; + } + + if (value instanceof Number) { + output.writeScalarBool(tag, tagSize, ((Number) value).longValue() == 1); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + boolean parsedValue = Boolean.parseBoolean(((String[]) value)[0]); + output.writeScalarBool(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String) { + boolean parsedValue = Boolean.parseBoolean((String) value); + output.writeScalarBool(tag, tagSize, parsedValue); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class BooleanSchema extends BooleanDynamicSchema { + protected final Getter getter; + + public BooleanSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } + + private static class BooleanPrimitiveSchema extends BooleanDynamicSchema { + private final BoolGetter primitiveGetter; + + public BooleanPrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + boolean value = primitiveGetter.get(message); + output.writeScalarBool(tag, tagSize, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/BytesWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/BytesWriteSchemas.java new file mode 100644 index 00000000000..865e4a7c70f --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/BytesWriteSchemas.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.foundation.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class BytesWriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (byte[].class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new BytesSchema<>(protoField, propertyDescriptor); + } + + return new BytesSchema<>(protoField, propertyDescriptor); + } + + private static class BytesDynamicSchema extends FieldSchema { + public BytesDynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof byte[]) { + output.writeByteArray(tag, tagSize, (byte[]) value); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class BytesSchema extends BytesDynamicSchema { + protected final Getter getter; + + public BytesSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/DoubleWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/DoubleWriteSchemas.java new file mode 100644 index 00000000000..8e1c5baece5 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/DoubleWriteSchemas.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.foundation.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.DoubleGetter; +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class DoubleWriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (double.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new DoublePrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Double.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new DoubleSchema<>(protoField, propertyDescriptor); + } + + return new DoubleSchema<>(protoField, propertyDescriptor); + } + + private static class DoubleDynamicSchema extends FieldSchema { + public DoubleDynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Number) { + output.writeScalarDouble(tag, tagSize, ((Number) value).doubleValue()); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + double parsedValue = Double.parseDouble(((String[]) value)[0]); + output.writeScalarDouble(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String) { + double parsedValue = Double.parseDouble((String) value); + output.writeScalarDouble(tag, tagSize, parsedValue); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class DoubleSchema extends DoubleDynamicSchema { + protected final Getter getter; + + public DoubleSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } + + private static class DoublePrimitiveSchema extends DoubleDynamicSchema { + private final DoubleGetter primitiveGetter; + + public DoublePrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + double value = primitiveGetter.get(message); + output.writeScalarDouble(tag, tagSize, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/EnumWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/EnumWriteSchemas.java new file mode 100644 index 00000000000..8b4f82f858d --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/EnumWriteSchemas.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.base.DynamicEnum; +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +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.EnumMeta; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.Type; +import io.protostuff.runtime.FieldSchema; + +public class EnumWriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (propertyDescriptor.getJavaType().isEnumType()) { + return new EnumSchema<>(protoField, propertyDescriptor); + } + + return new EnumSchema<>(protoField, propertyDescriptor); + } + + private static class EnumDynamicSchema extends FieldSchema { + private final EnumMeta enumMeta; + + public EnumDynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + this.enumMeta = new EnumMeta(protoField, javaType); + } + + protected final void numberWrite(OutputEx output, Number value) throws IOException { + int enumValue = value.intValue(); + if (!enumMeta.containsValue(enumValue)) { + throw new IllegalStateException( + String.format("invalid enum value %d for proto %s, field=%s:%s", + enumValue, + protoField.getTypeName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + output.writeScalarInt32(tag, tagSize, enumValue); + } + + protected final void stringWrite(OutputEx output, String enumName) throws IOException { + Integer enumValue = enumMeta.getValueByName(enumName); + if (enumValue == null) { + throw new IllegalStateException( + String.format("invalid enum name %s for proto %s, field=%s:%s", + enumName, + protoField.getTypeName(), + ((Type) protoField.getParent()).getCanonicalName(), + protoField.getName())); + } + + output.writeScalarInt32(tag, tagSize, enumValue); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Enum) { + stringWrite(output, ((Enum) value).name()); + return; + } + + if (value instanceof DynamicEnum) { + // protobuf can not support unknown enum, because protobuf encode enum as tag value + writeTo(output, ((DynamicEnum) value).getValue()); + return; + } + + if (value instanceof Number) { + // need to check if it is a valid number + // because maybe come from http request + numberWrite(output, ((Number) value).intValue()); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + + stringWrite(output, ((String[]) value)[0]); + return; + } + + if (value instanceof String) { + stringWrite(output, (String) value); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class EnumSchema extends EnumDynamicSchema { + protected final Getter getter; + + public EnumSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Fixed32WriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Fixed32WriteSchemas.java new file mode 100644 index 00000000000..cee4c90cb23 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Fixed32WriteSchemas.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.foundation.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Fixed32WriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (int.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new Fixed32PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Integer.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new Fixed32Schema<>(protoField, propertyDescriptor); + } + + return new Fixed32Schema<>(protoField, propertyDescriptor); + } + + private static class Fixed32DynamicSchema extends FieldSchema { + public Fixed32DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Number) { + output.writeScalarFixed32(tag, tagSize, ((Number) value).intValue()); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + int parsedValue = Integer.parseInt(((String[]) value)[0], 10); + output.writeScalarFixed32(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String) { + int parsedValue = Integer.parseInt((String) value, 10); + output.writeScalarFixed32(tag, tagSize, parsedValue); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class Fixed32Schema extends Fixed32DynamicSchema { + protected final Getter getter; + + public Fixed32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } + + private static class Fixed32PrimitiveSchema extends Fixed32DynamicSchema { + private final IntGetter primitiveGetter; + + public Fixed32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + int value = primitiveGetter.get(message); + output.writeScalarFixed32(tag, tagSize, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Fixed64WriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Fixed64WriteSchemas.java new file mode 100644 index 00000000000..22d982b1ce1 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Fixed64WriteSchemas.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.foundation.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.LongGetter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Fixed64WriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new Fixed64PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new Fixed64Schema<>(protoField, propertyDescriptor); + } + + return new Fixed64Schema<>(protoField, propertyDescriptor); + } + + private static class Fixed64DynamicSchema extends FieldSchema { + public Fixed64DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Number) { + output.writeScalarFixed64(tag, tagSize, ((Number) value).longValue()); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + long parsedValue = Long.parseLong(((String[]) value)[0], 10); + output.writeScalarFixed64(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String) { + long parsedValue = Long.parseLong((String) value, 10); + output.writeScalarFixed64(tag, tagSize, parsedValue); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class Fixed64Schema extends Fixed64DynamicSchema { + protected final Getter getter; + + public Fixed64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } + + private static class Fixed64PrimitiveSchema extends Fixed64DynamicSchema { + private final LongGetter primitiveGetter; + + public Fixed64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + long value = primitiveGetter.get(message); + output.writeScalarFixed64(tag, tagSize, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/FloatWriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/FloatWriteSchemas.java new file mode 100644 index 00000000000..b9e41053250 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/FloatWriteSchemas.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.foundation.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.FloatGetter; +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class FloatWriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (float.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new FloatPrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Float.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new FloatSchema<>(protoField, propertyDescriptor); + } + + return new FloatSchema<>(protoField, propertyDescriptor); + } + + private static class FloatDynamicSchema extends FieldSchema { + public FloatDynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Number) { + output.writeScalarFloat(tag, tagSize, ((Number) value).floatValue()); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + float parsedValue = Float.parseFloat(((String[]) value)[0]); + output.writeScalarFloat(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String) { + float parsedValue = Float.parseFloat((String) value); + output.writeScalarFloat(tag, tagSize, parsedValue); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class FloatSchema extends FloatDynamicSchema { + protected final Getter getter; + + public FloatSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = javaType.isPrimitive() ? null : propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } + + private static class FloatPrimitiveSchema extends FloatDynamicSchema { + private final FloatGetter primitiveGetter; + + public FloatPrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + float value = primitiveGetter.get(message); + output.writeScalarFloat(tag, tagSize, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Int32WriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Int32WriteSchemas.java new file mode 100644 index 00000000000..9172823efe9 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Int32WriteSchemas.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.ByteGetter; +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; +import org.apache.servicecomb.foundation.common.utils.bean.ShortGetter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public final class Int32WriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (int.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new IntFieldInt32PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (short.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new ShortFieldInt32PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (byte.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new ByteFieldInt32PrimitiveSchema<>(protoField, propertyDescriptor); + } + + return new Int32Schema<>(protoField, propertyDescriptor); + } + + private static class Int32DynamicSchema extends FieldSchema { + public Int32DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Number) { + output.writeScalarInt32(tag, tagSize, ((Number) value).intValue()); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + int parsedValue = Integer.parseInt(((String[]) value)[0], 10); + output.writeScalarInt32(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String) { + if (((String) value).isEmpty()) { + return; + } + int parsedValue = Integer.parseInt((String) value, 10); + output.writeScalarInt32(tag, tagSize, parsedValue); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class Int32Schema extends Int32DynamicSchema { + protected final Getter getter; + + public Int32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } + + private static final class IntFieldInt32PrimitiveSchema extends Int32DynamicSchema { + private final IntGetter primitiveGetter; + + public IntFieldInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public void getAndWriteTo(OutputEx output, T message) throws IOException { + int value = primitiveGetter.get(message); + output.writeScalarInt32(tag, tagSize, value); + } + } + + private static final class ShortFieldInt32PrimitiveSchema extends Int32DynamicSchema { + private final ShortGetter primitiveGetter; + + public ShortFieldInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public void getAndWriteTo(OutputEx output, T message) throws IOException { + short value = primitiveGetter.get(message); + output.writeScalarInt32(tag, tagSize, value); + } + } + + private static final class ByteFieldInt32PrimitiveSchema extends Int32DynamicSchema { + private final ByteGetter primitiveGetter; + + public ByteFieldInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public void getAndWriteTo(OutputEx output, T message) throws IOException { + byte value = primitiveGetter.get(message); + output.writeScalarInt32(tag, tagSize, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Int64WriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Int64WriteSchemas.java new file mode 100644 index 00000000000..d85810524e8 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/Int64WriteSchemas.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.temporal.ChronoField; +import java.util.Date; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.LongGetter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class Int64WriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new Int64PrimitiveSchema<>(protoField, propertyDescriptor); + } + + return new Int64Schema<>(protoField, propertyDescriptor); + } + + private static class Int64DynamicSchema extends FieldSchema { + public Int64DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Number) { + output.writeScalarInt64(tag, tagSize, ((Number) value).longValue()); + return; + } + + if (value instanceof Date) { + long parsedValue = ((Date) value).getTime(); + output.writeScalarInt64(tag, tagSize, parsedValue); + return; + } + + if (value instanceof LocalDate) { + long parsedValue = ((LocalDate) value).getLong(ChronoField.EPOCH_DAY); + output.writeScalarInt64(tag, tagSize, parsedValue); + return; + } + + if (value instanceof LocalDateTime) { + long parsedValue = ((LocalDateTime) value).toInstant(ZoneOffset.UTC).toEpochMilli(); + output.writeScalarInt64(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String) { + long parsedValue; + if (((String) value).contains(":")) { + // from edge, ISO8601 date time, e.g. 2022-05-31T09:16:38.941Z + OffsetDateTime offsetDateTime = OffsetDateTime.parse((String) value); + parsedValue = offsetDateTime.toInstant().toEpochMilli(); + } else { + parsedValue = Long.parseLong((String) value, 10); + } + output.writeScalarInt64(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + long parsedValue = Long.parseLong(((String[]) value)[0], 10); + output.writeScalarInt64(tag, tagSize, parsedValue); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class Int64Schema extends Int64DynamicSchema { + protected final Getter getter; + + public Int64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + this.writeTo(output, value); + } + } + } + + private static final class Int64PrimitiveSchema extends Int64DynamicSchema { + private final LongGetter primitiveGetter; + + public Int64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public void getAndWriteTo(OutputEx output, T message) throws IOException { + long value = primitiveGetter.get(message); + output.writeScalarInt64(tag, tagSize, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SFixed32WriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SFixed32WriteSchemas.java new file mode 100644 index 00000000000..3cbbd8c7a40 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SFixed32WriteSchemas.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.foundation.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SFixed32WriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (int.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new SFixed32PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Integer.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new SFixed32Schema<>(protoField, propertyDescriptor); + } + + return new SFixed32Schema<>(protoField, propertyDescriptor); + } + + private static class SFixed32DynamicSchema extends FieldSchema { + public SFixed32DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Number) { + output.writeScalarSFixed32(tag, tagSize, ((Number) value).intValue()); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + int parsedValue = Integer.parseInt(((String[]) value)[0], 10); + output.writeScalarSFixed32(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String) { + int parsedValue = Integer.parseInt((String) value, 10); + output.writeScalarSFixed32(tag, tagSize, parsedValue); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class SFixed32Schema extends SFixed32DynamicSchema { + protected final Getter getter; + + public SFixed32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } + + private static class SFixed32PrimitiveSchema extends SFixed32DynamicSchema { + private final IntGetter primitiveGetter; + + public SFixed32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + int value = primitiveGetter.get(message); + output.writeScalarSFixed32(tag, tagSize, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SFixed64WriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SFixed64WriteSchemas.java new file mode 100644 index 00000000000..2f0a4bef028 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SFixed64WriteSchemas.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.foundation.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.LongGetter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SFixed64WriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new SFixed64PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new SFixed64Schema<>(protoField, propertyDescriptor); + } + + return new SFixed64Schema<>(protoField, propertyDescriptor); + } + + private static class SFixed64DynamicSchema extends FieldSchema { + public SFixed64DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Number) { + output.writeScalarSFixed64(tag, tagSize, ((Number) value).longValue()); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + long parsedValue = Long.parseLong(((String[]) value)[0], 10); + output.writeScalarSFixed64(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String) { + long parsedValue = Long.parseLong((String) value, 10); + output.writeScalarSFixed64(tag, tagSize, parsedValue); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class SFixed64Schema extends SFixed64DynamicSchema { + protected final Getter getter; + + public SFixed64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } + + private static class SFixed64PrimitiveSchema extends SFixed64DynamicSchema { + private final LongGetter primitiveGetter; + + public SFixed64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + long value = primitiveGetter.get(message); + output.writeScalarSFixed64(tag, tagSize, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SInt32WriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SInt32WriteSchemas.java new file mode 100644 index 00000000000..f3d2e1ad3fc --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SInt32WriteSchemas.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.foundation.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SInt32WriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (int.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new SInt32PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Integer.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new SInt32Schema<>(protoField, propertyDescriptor); + } + + return new SInt32Schema<>(protoField, propertyDescriptor); + } + + private static class SInt32DynamicSchema extends FieldSchema { + public SInt32DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Number) { + output.writeScalarSInt32(tag, tagSize, ((Number) value).intValue()); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + int parsedValue = Integer.parseInt(((String[]) value)[0], 10); + output.writeScalarSInt32(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String) { + int parsedValue = Integer.parseInt((String) value, 10); + output.writeScalarSInt32(tag, tagSize, parsedValue); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class SInt32Schema extends SInt32DynamicSchema { + protected final Getter getter; + + public SInt32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } + + private static class SInt32PrimitiveSchema extends SInt32DynamicSchema { + private final IntGetter primitiveGetter; + + public SInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + int value = primitiveGetter.get(message); + output.writeScalarSInt32(tag, tagSize, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SInt64WriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SInt64WriteSchemas.java new file mode 100644 index 00000000000..b935e261e54 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/SInt64WriteSchemas.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.foundation.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.LongGetter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class SInt64WriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new SInt64PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new SInt64Schema<>(protoField, propertyDescriptor); + } + + return new SInt64Schema<>(protoField, propertyDescriptor); + } + + private static class SInt64DynamicSchema extends FieldSchema { + public SInt64DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Number) { + output.writeScalarSInt64(tag, tagSize, ((Number) value).longValue()); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + long parsedValue = Long.parseLong(((String[]) value)[0], 10); + output.writeScalarSInt64(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String) { + long parsedValue = Long.parseLong((String) value, 10); + output.writeScalarSInt64(tag, tagSize, parsedValue); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class SInt64Schema extends SInt64DynamicSchema { + protected final Getter getter; + + public SInt64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } + + private static class SInt64PrimitiveSchema extends SInt64DynamicSchema { + private final LongGetter primitiveGetter; + + public SInt64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + long value = primitiveGetter.get(message); + output.writeScalarSInt64(tag, tagSize, value); + } + } +} 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 new file mode 100644 index 00000000000..7df329b849b --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/StringWriteSchemas.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class StringWriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (char.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new CharFieldStringSchema<>(protoField, propertyDescriptor); + } + + if (String.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new StringSchema<>(protoField, propertyDescriptor); + } + + return new StringSchema<>(protoField, propertyDescriptor); + } + + private static class StringDynamicSchema extends FieldSchema { + public StringDynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof String) { + output.writeScalarString(tag, tagSize, (String) value); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + output.writeScalarString(tag, tagSize, ((String[]) value)[0]); + return; + } + + if (value instanceof Character) { + output.writeScalarString(tag, tagSize, String.valueOf((char) value)); + return; + } + + if (value instanceof BigDecimal || value instanceof BigInteger) { + output.writeScalarString(tag, tagSize, value.toString()); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class StringSchema extends StringDynamicSchema { + protected final Getter getter; + + public StringSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } + + private static class CharFieldStringSchema extends StringDynamicSchema { + protected final CharGetter getter; + + public CharFieldStringSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + char value = getter.get(message); + writeTo(output, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/UInt32WriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/UInt32WriteSchemas.java new file mode 100644 index 00000000000..6a163bef021 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/UInt32WriteSchemas.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.foundation.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class UInt32WriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (int.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new UInt32PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Integer.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new UInt32Schema<>(protoField, propertyDescriptor); + } + + return new UInt32Schema<>(protoField, propertyDescriptor); + } + + private static class UInt32DynamicSchema extends FieldSchema { + public UInt32DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Number) { + output.writeScalarUInt32(tag, tagSize, ((Number) value).intValue()); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + int parsedValue = Integer.parseInt(((String[]) value)[0], 10); + output.writeScalarUInt32(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String) { + int parsedValue = Integer.parseInt((String) value, 10); + output.writeScalarUInt32(tag, tagSize, parsedValue); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class UInt32Schema extends UInt32DynamicSchema { + protected final Getter getter; + + public UInt32Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } + + private static class UInt32PrimitiveSchema extends UInt32DynamicSchema { + private final IntGetter primitiveGetter; + + public UInt32PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + int value = primitiveGetter.get(message); + output.writeScalarUInt32(tag, tagSize, value); + } + } +} diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/UInt64WriteSchemas.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/UInt64WriteSchemas.java new file mode 100644 index 00000000000..e8a449f4875 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/scalar/UInt64WriteSchemas.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.foundation.protobuf.internal.schema.serializer.scalar; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.LongGetter; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoUtils; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyDescriptor; + +import io.protostuff.OutputEx; +import io.protostuff.compiler.model.Field; +import io.protostuff.runtime.FieldSchema; + +public class UInt64WriteSchemas { + public static FieldSchema create(Field protoField, PropertyDescriptor propertyDescriptor) { + if (long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new UInt64PrimitiveSchema<>(protoField, propertyDescriptor); + } + + if (Long.class.equals(propertyDescriptor.getJavaType().getRawClass())) { + return new UInt64Schema<>(protoField, propertyDescriptor); + } + + return new UInt64Schema<>(protoField, propertyDescriptor); + } + + private static class UInt64DynamicSchema extends FieldSchema { + public UInt64DynamicSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor.getJavaType()); + } + + @Override + public final void writeTo(OutputEx output, Object value) throws IOException { + if (value instanceof Number) { + output.writeScalarUInt64(tag, tagSize, ((Number) value).longValue()); + return; + } + + if (value instanceof String[]) { + if (((String[]) value).length == 0) { + return; + } + long parsedValue = Long.parseLong(((String[]) value)[0], 10); + output.writeScalarUInt64(tag, tagSize, parsedValue); + return; + } + + if (value instanceof String) { + long parsedValue = Long.parseLong((String) value, 10); + output.writeScalarUInt64(tag, tagSize, parsedValue); + return; + } + + ProtoUtils.throwNotSupportWrite(protoField, value); + } + } + + private static class UInt64Schema extends UInt64DynamicSchema { + protected final Getter getter; + + public UInt64Schema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + this.getter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + Object value = getter.get(message); + if (value != null) { + writeTo(output, value); + } + } + } + + private static class UInt64PrimitiveSchema extends UInt64DynamicSchema { + private final LongGetter primitiveGetter; + + public UInt64PrimitiveSchema(Field protoField, PropertyDescriptor propertyDescriptor) { + super(protoField, propertyDescriptor); + + primitiveGetter = propertyDescriptor.getGetter(); + } + + @Override + public final void getAndWriteTo(OutputEx output, T message) throws IOException { + long value = primitiveGetter.get(message); + output.writeScalarUInt64(tag, tagSize, 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 new file mode 100644 index 00000000000..0b6783a90c6 --- /dev/null +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/notice.txt @@ -0,0 +1,18 @@ +1.List或List这种结构是PB不支持的 + 类似的场景,只要PB文件解析出错,说明本schema不支持PB相关的传输:highway/grpc等等 +2.反序列化,支持java类型为数组,不一定要求是Collection +3.原子类型的序列化/反序列化,(主要是原子数组?) + + +3.应该只有文件相关的场景,才是真正的不支持 + +------------- +优化: + 1.支持用户泛型 + 2. + +不兼容: + 1.数组编码规则从protoStuff变成标准的protobuf + +不确定: + 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/TestISODateTimeParsing.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/TestISODateTimeParsing.java new file mode 100644 index 00000000000..8fa797724b9 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/TestISODateTimeParsing.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.foundation.protobuf; + +import java.text.SimpleDateFormat; +import java.time.OffsetDateTime; +import java.util.Date; +import java.util.TimeZone; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestISODateTimeParsing { + @Test + public void testParseStringToDate() { + SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD'T'hh:mm:ssZ"); + sdf.setTimeZone(TimeZone.getTimeZone("EST")); + + OffsetDateTime offsetDateTime = OffsetDateTime.parse("2022-05-31T09:16:38.941Z"); + Date d = Date.from(offsetDateTime.toInstant()); + String date = sdf.format(d); + Assertions.assertEquals("2022-05-151T04:16:38-0500", date); + + offsetDateTime = OffsetDateTime.parse("2022-05-31T09:16:38.941+00:00"); + d = Date.from(offsetDateTime.toInstant()); + date = sdf.format(d); + Assertions.assertEquals("2022-05-151T04:16:38-0500", date); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/compatibility/TestCompatibilityOfImplementations.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/compatibility/TestCompatibilityOfImplementations.java new file mode 100644 index 00000000000..62fedeaf68e --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/compatibility/TestCompatibilityOfImplementations.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.protobuf.compatibility; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; +import org.apache.servicecomb.foundation.protobuf.internal.model.Root; +import org.apache.servicecomb.foundation.protobuf.performance.ProtubufCodecEngine; +import org.apache.servicecomb.foundation.protobuf.performance.engine.Protobuf; +import org.apache.servicecomb.foundation.protobuf.performance.engine.ScbWeak; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestCompatibilityOfImplementations { + static final ProtubufCodecEngine scbWeak = new ScbWeak(); + + static final ProtubufCodecEngine protobuf = new Protobuf(); + + @Test + @SuppressWarnings("unchecked") + public void testEmptyCollection() throws Exception { + ProtobufRoot.Root.Builder builder = ProtobufRoot.Root.newBuilder(); + byte[] values = protobuf.serialize(builder); + Assertions.assertEquals(values.length, 0); + ProtobufRoot.Root.Builder o = (ProtobufRoot.Root.Builder) protobuf.deserialize(values); + Assertions.assertTrue(o.getFixed32SNotPackedList().isEmpty()); + + builder = ProtobufRoot.Root.newBuilder().addFixed32SNotPacked(30); + values = protobuf.serialize(builder); + Assertions.assertArrayEquals(new byte[] {(byte) -123, (byte) 6, (byte) 30, (byte) 0, (byte) 0, (byte) 0}, values); + o = (ProtobufRoot.Root.Builder) protobuf.deserialize(values); + Assertions.assertEquals(30, (int) o.getFixed32SNotPackedList().get(0)); + + Root root = new Root(); + root.setFixed32sNotPacked(new ArrayList<>()); + values = scbWeak.serialize(root); + Assertions.assertEquals(values.length, 0); + Map newRootMap = (Map) scbWeak.deserialize(values); + Assertions.assertNull(newRootMap.get("fixed32sNotPacked")); // This is different , because depends on default model initializer + + List iValues = new ArrayList<>(); + iValues.add(30); + root.setFixed32sNotPacked(iValues); + values = scbWeak.serialize(root); + Assertions.assertArrayEquals(new byte[] {(byte) -123, (byte) 6, (byte) 30, (byte) 0, (byte) 0, (byte) 0}, values); + newRootMap = (Map) scbWeak.deserialize(values); + Assertions.assertEquals(30, (int) ((List) newRootMap.get("fixed32sNotPacked")).get(0)); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/TestModelWrap.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/TestModelWrap.java new file mode 100644 index 00000000000..fd19fd8f033 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/TestModelWrap.java @@ -0,0 +1,297 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal; + +import java.io.IOException; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.RootSerializer; + +import io.vertx.core.json.Json; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestModelWrap { + protected static ProtoMapperFactory factory = new ProtoMapperFactory(); + + protected static ProtoMapper modelProtoMapper = factory.createFromName("model.proto"); + + public static class User { + public String name; + + public User(String name) { + this.name = name; + } + } + + static User user = new User("uName"); + + public static class PojoModel { + public List> listListUser; + + public List> listMapUser; + + public Map> mapListUser; + + public Map> mapMapUser; + + public List>> listListListUser; + + public List>> listListMapUser; + + public List>> listMapListUser; + + public List>> listMapMapUser; + + public Map>> mapListListUser; + + public Map>> mapListMapUser; + + public Map>> mapMapListUser; + + public Map>> mapMapMapUser; + + public void init() { + List listUser = Arrays.asList(user, user); + + Map mapUser = new LinkedHashMap<>(); + mapUser.put("k1", user); + mapUser.put("k2", user); + + listListUser = Arrays.asList(listUser, listUser); + listMapUser = Arrays.asList(mapUser, mapUser); + + mapListUser = new LinkedHashMap<>(); + mapListUser.put("k1", listUser); + mapListUser.put("k2", listUser); + + mapMapUser = new LinkedHashMap<>(); + mapMapUser.put("k1", mapUser); + mapMapUser.put("k2", mapUser); + + listListListUser = Arrays.asList(listListUser, listListUser); + listListMapUser = Arrays.asList(listMapUser, listMapUser); + listMapListUser = Arrays.asList(mapListUser, mapListUser); + listMapMapUser = Arrays.asList(mapMapUser, mapMapUser); + + mapListListUser = new LinkedHashMap<>(); + mapListListUser.put("k1", listListUser); + mapListListUser.put("k2", listListUser); + + mapListMapUser = new LinkedHashMap<>(); + mapListMapUser.put("k1", listMapUser); + mapListMapUser.put("k2", listMapUser); + + mapMapListUser = new LinkedHashMap<>(); + mapMapListUser.put("k1", mapListUser); + mapMapListUser.put("k2", mapListUser); + + mapMapMapUser = new LinkedHashMap<>(); + mapMapMapUser.put("k1", mapMapUser); + mapMapMapUser.put("k2", mapMapUser); + } + } + + public static class ProtoModel { + public List listListUser; + + public List listMapUser; + + public Map mapListUser; + + public Map mapMapUser; + + public List listListListUser; + + public List listListMapUser; + + public List listMapListUser; + + public List listMapMapUser; + + public Map mapListListUser; + + public Map mapListMapUser; + + public Map mapMapListUser; + + public Map mapMapMapUser; + + public void init() { + ProtoListUser protoListUser = new ProtoListUser(); + protoListUser.init(); + + ProtoMapUser protoMapUser = new ProtoMapUser(); + protoMapUser.init(); + + ProtoListListUser protoListListUser = new ProtoListListUser(); + protoListListUser.init(); + + ProtoListMapUser protoListMapUser = new ProtoListMapUser(); + protoListMapUser.init(); + + ProtoMapListUser protoMapListUser = new ProtoMapListUser(); + protoMapListUser.init(); + + ProtoMapMapUser protoMapMapUser = new ProtoMapMapUser(); + protoMapMapUser.init(); + + listListUser = Arrays.asList(protoListUser, protoListUser); + listMapUser = Arrays.asList(protoMapUser, protoMapUser); + mapListUser = new LinkedHashMap<>(); + mapListUser.put("k1", protoListUser); + mapListUser.put("k2", protoListUser); + + mapMapUser = new LinkedHashMap<>(); + mapMapUser.put("k1", protoMapUser); + mapMapUser.put("k2", protoMapUser); + + listListListUser = Arrays.asList(protoListListUser, protoListListUser); + listListMapUser = Arrays.asList(protoListMapUser, protoListMapUser); + listMapListUser = Arrays.asList(protoMapListUser, protoMapListUser); + listMapMapUser = Arrays.asList(protoMapMapUser, protoMapMapUser); + + mapListListUser = new LinkedHashMap<>(); + mapListListUser.put("k1", protoListListUser); + mapListListUser.put("k2", protoListListUser); + + mapListMapUser = new LinkedHashMap<>(); + mapListMapUser.put("k1", protoListMapUser); + mapListMapUser.put("k2", protoListMapUser); + + mapMapListUser = new LinkedHashMap<>(); + mapMapListUser.put("k1", protoMapListUser); + mapMapListUser.put("k2", protoMapListUser); + + mapMapMapUser = new LinkedHashMap<>(); + mapMapMapUser.put("k1", protoMapMapUser); + mapMapMapUser.put("k2", protoMapMapUser); + } + } + + public static class ProtoListListUser { + public List value; + + public void init() { + ProtoListUser protoListUser = new ProtoListUser(); + protoListUser.init(); + value = Arrays.asList(protoListUser, protoListUser); + } + } + + public static class ProtoListMapUser { + public List value; + + public void init() { + ProtoMapUser protoMapUser = new ProtoMapUser(); + protoMapUser.init(); + value = Arrays.asList(protoMapUser, protoMapUser); + } + } + + public static class ProtoMapListUser { + public Map value; + + public void init() { + ProtoListUser protoListUser = new ProtoListUser(); + protoListUser.init(); + value = new LinkedHashMap<>(); + value.put("k1", protoListUser); + value.put("k2", protoListUser); + } + } + + public static class ProtoMapMapUser { + public Map value; + + public void init() { + ProtoMapUser protoMapUser = new ProtoMapUser(); + protoMapUser.init(); + value = new LinkedHashMap<>(); + value.put("k1", protoMapUser); + value.put("k2", protoMapUser); + } + } + + public static class ProtoListUser { + public List value; + + public void init() { + value = Arrays.asList(user, user); + } + } + + public static class ProtoMapUser { + public Map value; + + public void init() { + value = new LinkedHashMap<>(); + value.put("k1", user); + value.put("k2", user); + } + } + + @SuppressWarnings("unchecked") + @Test + public void pojoModel() throws IOException { + RootSerializer pojoSerializer = modelProtoMapper.createRootSerializer("PojoModel", PojoModel.class); + RootDeserializer> pojoMapDeserializer = modelProtoMapper + .createRootDeserializer("PojoModel", Map.class); + RootDeserializer pojoModelDeserializer = modelProtoMapper + .createRootDeserializer("PojoModel", PojoModel.class); + + RootSerializer protoSerializer = modelProtoMapper.createRootSerializer("ProtoModel", ProtoModel.class); + RootDeserializer> protoMapDeserializer = modelProtoMapper + .createRootDeserializer("ProtoModel", Map.class); + RootDeserializer protoModelDeserializer = modelProtoMapper + .createRootDeserializer("ProtoModel", ProtoModel.class); + + PojoModel pojoModel = new PojoModel(); + pojoModel.init(); + String jsonPojoModel = Json.encode(pojoModel); + Map mapFromPojoModel = (Map) Json.decodeValue(jsonPojoModel, Map.class); + + ProtoModel protoModel = new ProtoModel(); + protoModel.init(); + String jsonProtoModel = Json.encode(protoModel); + Map mapFromProtoModel = (Map) Json.decodeValue(jsonProtoModel, Map.class); + + // serialize + byte[] bytes = protoSerializer.serialize(protoModel); + Assertions.assertArrayEquals(bytes, protoSerializer.serialize(mapFromProtoModel)); + Assertions.assertArrayEquals(bytes, pojoSerializer.serialize(pojoModel)); + Assertions.assertArrayEquals(bytes, pojoSerializer.serialize(mapFromPojoModel)); + + // deserialize pojoModel + PojoModel newPojoModel = pojoModelDeserializer.deserialize(bytes); + Assertions.assertEquals(jsonPojoModel, Json.encode(newPojoModel)); + Map mapFromNewPojoModel = pojoMapDeserializer.deserialize(bytes); + Assertions.assertEquals(jsonPojoModel, Json.encode(mapFromNewPojoModel)); + + // deserialize protoModel + ProtoModel newProtoModel = protoModelDeserializer.deserialize(bytes); + Assertions.assertEquals(jsonProtoModel, Json.encode(newProtoModel)); + Map mapFromNewProtoModel = protoMapDeserializer.deserialize(bytes); + Assertions.assertEquals(jsonProtoModel, Json.encode(mapFromNewProtoModel)); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/TestSchemaBase.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/TestSchemaBase.java new file mode 100644 index 00000000000..6353815df6d --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/TestSchemaBase.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal; + +import java.io.IOException; +import java.util.Map; + +import org.apache.commons.codec.binary.Hex; +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.RootSerializer; +import org.apache.servicecomb.foundation.protobuf.internal.model.PrimitiveArrays; +import org.apache.servicecomb.foundation.protobuf.internal.model.PrimitiveWrapperArrays; +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; +import org.apache.servicecomb.foundation.protobuf.internal.model.Root; + +import io.protostuff.compiler.model.Field; +import org.junit.jupiter.api.Assertions; + +public class TestSchemaBase { + protected static ProtoMapperFactory factory = new ProtoMapperFactory(); + + protected static ProtoMapper protoMapper = factory.createFromName("protobufRoot.proto"); + + protected static RootSerializer rootSerializer = protoMapper.createRootSerializer("Root", Root.class); + + protected static RootDeserializer rootDeserializer = protoMapper.createRootDeserializer("Root", Root.class); + + protected static RootSerializer primitiveArraysSerializer = protoMapper + .createRootSerializer("Root", PrimitiveArrays.class); + + protected static RootDeserializer primitiveArraysDeserializer = protoMapper + .createRootDeserializer("Root", PrimitiveArrays.class); + + protected static RootSerializer primitiveWrapperArraysSerializer = protoMapper + .createRootSerializer("Root", PrimitiveWrapperArrays.class); + + protected static RootDeserializer primitiveWrapperArraysDeserializer = protoMapper + .createRootDeserializer("Root", PrimitiveWrapperArrays.class); + + protected static RootDeserializer> mapRootDeserializer = protoMapper + .createRootDeserializer("Root", Map.class); + + protected Object scbRoot; + + protected byte[] scbRootBytes; + + protected Map scbMap; + + protected byte[] scbMapBytes; + + protected ProtobufRoot.Root.Builder builder = ProtobufRoot.Root.newBuilder(); + + protected byte[] protobufBytes; + + protected String primitiveFieldName; + + protected Field primitiveProtoField; + + protected String fieldName; + + protected Field protoField; + + protected void initFields(String primitiveFieldName, String fieldName) { + this.primitiveFieldName = primitiveFieldName; + if (primitiveFieldName != null) { + primitiveProtoField = protoMapper.getProto().getMessage("Root").getField(primitiveFieldName); + } + + initField(fieldName); + } + + protected void initField(String fieldName) { + this.fieldName = fieldName; + if (fieldName != null) { + protoField = protoMapper.getProto().getMessage("Root").getField(fieldName); + } + } + + protected void checkRepeatedWithPrimitive() throws Exception { + check(); + + check(primitiveArraysDeserializer, mapRootDeserializer, primitiveArraysSerializer, false); + String primitiveFieldName = scbMap.keySet().iterator().next(); + java.lang.reflect.Field primitiveField = PrimitiveArrays.class.getDeclaredField(primitiveFieldName); + primitiveField.setAccessible(true); + Object primitiveArray = primitiveField.get(scbRoot); + + check(primitiveWrapperArraysDeserializer, mapRootDeserializer, primitiveWrapperArraysSerializer, false); + String wrapperFieldName = scbMap.keySet().iterator().next(); + java.lang.reflect.Field wrapperField = PrimitiveWrapperArrays.class.getDeclaredField(wrapperFieldName); + wrapperField.setAccessible(true); + Object[] array = (Object[]) wrapperField.get(scbRoot); + + // dynamic strings + String[] strings = new String[array.length]; + for (int idx = 0; idx < array.length; idx++) { + strings[idx] = array[idx].toString(); + } + scbMap.clear(); + scbMap.put(primitiveFieldName, strings); + scbMapBytes = primitiveArraysSerializer.serialize(scbMap); + Assertions.assertArrayEquals(protobufBytes, scbMapBytes); + + // dynamic primitive array + scbMap.clear(); + scbMap.put(primitiveFieldName, primitiveArray); + scbMapBytes = primitiveArraysSerializer.serialize(scbMap); + Assertions.assertArrayEquals(protobufBytes, scbMapBytes); + + // dynamic array + scbMap.clear(); + scbMap.put(primitiveFieldName, array); + scbMapBytes = primitiveArraysSerializer.serialize(scbMap); + Assertions.assertArrayEquals(protobufBytes, scbMapBytes); + } + + protected void check() throws IOException { + check(rootDeserializer, mapRootDeserializer, rootSerializer, false); + } + + protected void check(boolean print) throws IOException { + check(rootDeserializer, mapRootDeserializer, rootSerializer, print); + } + + protected void check(RootDeserializer deserializer, RootDeserializer> mapDeserializer, + RootSerializer serializer, + boolean print) throws IOException { + // 1.standard protobuf serialize to bytes + protobufBytes = builder.build().toByteArray(); + // 2.weak type deserialize + scbMap = mapDeserializer.deserialize(protobufBytes); + // 3.weak type serialize + scbMapBytes = serializer.serialize(scbMap); + // 4.strong type deserialize + scbRoot = deserializer.deserialize(scbMapBytes); + // 5.strong type serialize + scbRootBytes = serializer.serialize(scbRoot); + + if (print) { + System.out.println("scbRoot bytes:" + Hex.encodeHexString(scbRootBytes)); + System.out.println("scbRoot len :" + scbRootBytes.length); + System.out.println("scbMap bytes :" + Hex.encodeHexString(scbMapBytes)); + System.out.println("scbMap len :" + scbMapBytes.length); + System.out.println("protobuf :" + Hex.encodeHexString(protobufBytes)); + System.out.println("protobuf len :" + protobufBytes.length); + } + Assertions.assertArrayEquals(protobufBytes, scbMapBytes); + Assertions.assertArrayEquals(protobufBytes, scbRootBytes); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/bean/TestBeanDescriptorManager.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/bean/TestBeanDescriptorManager.java new file mode 100644 index 00000000000..60054c021d0 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/bean/TestBeanDescriptorManager.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.bean; + +import java.lang.reflect.Method; + +import org.apache.servicecomb.foundation.common.utils.ReflectUtils; +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.foundation.common.utils.bean.IntGetter; +import org.apache.servicecomb.foundation.common.utils.bean.IntSetter; +import org.apache.servicecomb.foundation.common.utils.bean.Setter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestBeanDescriptorManager { + public static class Model { + private int both; + + private int onlyGet; + + private int onlySet; + + public int direct; + + public int getBoth() { + return both; + } + + public void setBoth(int both) { + this.both = both; + } + + public int getOnlyGet() { + return onlyGet; + } + + public void onlyGet(int value) { + this.onlyGet = value; + } + + public void setOnlySet(int onlySet) { + this.onlySet = onlySet; + } + + public int onlySet() { + return onlySet; + } + } + + public static class CustomGeneric { + private T value; + + public T getValue() { + return value; + } + + public void setValue(T value) { + this.value = value; + } + } + + public static class GenericSchema { + public CustomGeneric genericMethod(CustomGeneric input) { + return null; + } + } + + static ObjectMapper mapper = new ObjectMapper(); + + static BeanDescriptorManager beanDescriptorManager = new BeanDescriptorManager(mapper.getSerializationConfig()); + + static BeanDescriptor beanDescriptor = beanDescriptorManager.getOrCreateBeanDescriptor(Model.class); + + Model model = new Model(); + + @Test + public void generic() { + Method method = ReflectUtils.findMethod(GenericSchema.class, "genericMethod"); + BeanDescriptor beanDescriptor = beanDescriptorManager + .getOrCreateBeanDescriptor(method.getGenericParameterTypes()[0]); + + Assertions.assertEquals(String.class, beanDescriptor.getPropertyDescriptors().get("value").getJavaType().getRawClass()); + } + + @Test + public void getOrCreate() { + Assertions.assertSame(beanDescriptor, beanDescriptorManager.getOrCreateBeanDescriptor(Model.class)); + Assertions.assertSame(Model.class, beanDescriptor.getJavaType().getRawClass()); + } + + @SuppressWarnings("unchecked") + @Test + public void both() { + PropertyDescriptor propertyDescriptor = beanDescriptor.getPropertyDescriptors().get("both"); + ((IntSetter) propertyDescriptor.getSetter()).set(model, 1); + Assertions.assertEquals(1, ((IntGetter) propertyDescriptor.getGetter()).get(model)); + Assertions.assertEquals(1, model.getBoth()); + } + + @SuppressWarnings("unchecked") + @Test + public void onlyGet() { + PropertyDescriptor propertyDescriptor = beanDescriptor.getPropertyDescriptors().get("onlyGet"); + Assertions.assertNull(propertyDescriptor.getSetter()); + + model.onlyGet(1); + Assertions.assertEquals(1, ((IntGetter) propertyDescriptor.getGetter()).get(model)); + Assertions.assertEquals(1, model.getOnlyGet()); + } + + @SuppressWarnings("unchecked") + @Test + public void onlySet() { + PropertyDescriptor propertyDescriptor = beanDescriptor.getPropertyDescriptors().get("onlySet"); + Assertions.assertNull(propertyDescriptor.getGetter()); + + ((IntSetter) propertyDescriptor.getSetter()).set(model, 1); + Assertions.assertEquals(1, model.onlySet()); + } + + @SuppressWarnings("unchecked") + @Test + public void direct() { + PropertyDescriptor propertyDescriptor = beanDescriptor.getPropertyDescriptors().get("direct"); + ((Setter) propertyDescriptor.getSetter()).set(model, 1); + Assertions.assertEquals(1, (int) ((Getter) propertyDescriptor.getGetter()).get(model)); + Assertions.assertEquals(1, model.direct); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/CustomGeneric.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/CustomGeneric.java new file mode 100644 index 00000000000..c57579ed18e --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/CustomGeneric.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.foundation.protobuf.internal.model; + +public class CustomGeneric { + public T user; +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/PrimitiveArrays.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/PrimitiveArrays.java new file mode 100644 index 00000000000..fdaa1f536e2 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/PrimitiveArrays.java @@ -0,0 +1,301 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License")Packed; you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.model; + +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; + +public class PrimitiveArrays { + private int[] int32sPacked; + + private long[] int64sPacked; + + private int[] uint32sPacked; + + private long[] uint64sPacked; + + private int[] sint32sPacked; + + private long[] sint64sPacked; + + private int[] fixed32sPacked; + + private long[] fixed64sPacked; + + private int[] sfixed32sPacked; + + private long[] sfixed64sPacked; + + private float[] floatsPacked; + + private double[] doublesPacked; + + private boolean[] boolsPacked; + + private Color[] colorsPacked; + + private int[] int32sNotPacked; + + private long[] int64sNotPacked; + + private int[] uint32sNotPacked; + + private long[] uint64sNotPacked; + + private int[] sint32sNotPacked; + + private long[] sint64sNotPacked; + + private int[] fixed32sNotPacked; + + private long[] fixed64sNotPacked; + + private int[] sfixed32sNotPacked; + + private long[] sfixed64sNotPacked; + + private float[] floatsNotPacked; + + private double[] doublesNotPacked; + + private boolean[] boolsNotPacked; + + private Color[] colorsNotPacked; + + public int[] getInt32sPacked() { + return int32sPacked; + } + + public void setInt32sPacked(int[] int32sPacked) { + this.int32sPacked = int32sPacked; + } + + public long[] getInt64sPacked() { + return int64sPacked; + } + + public void setInt64sPacked(long[] int64sPacked) { + this.int64sPacked = int64sPacked; + } + + public int[] getUint32sPacked() { + return uint32sPacked; + } + + public void setUint32sPacked(int[] uint32sPacked) { + this.uint32sPacked = uint32sPacked; + } + + public long[] getUint64sPacked() { + return uint64sPacked; + } + + public void setUint64sPacked(long[] uint64sPacked) { + this.uint64sPacked = uint64sPacked; + } + + public int[] getSint32sPacked() { + return sint32sPacked; + } + + public void setSint32sPacked(int[] sint32sPacked) { + this.sint32sPacked = sint32sPacked; + } + + public long[] getSint64sPacked() { + return sint64sPacked; + } + + public void setSint64sPacked(long[] sint64sPacked) { + this.sint64sPacked = sint64sPacked; + } + + public int[] getFixed32sPacked() { + return fixed32sPacked; + } + + public void setFixed32sPacked(int[] fixed32sPacked) { + this.fixed32sPacked = fixed32sPacked; + } + + public long[] getFixed64sPacked() { + return fixed64sPacked; + } + + public void setFixed64sPacked(long[] fixed64sPacked) { + this.fixed64sPacked = fixed64sPacked; + } + + public int[] getSfixed32sPacked() { + return sfixed32sPacked; + } + + public void setSfixed32sPacked(int[] sfixed32sPacked) { + this.sfixed32sPacked = sfixed32sPacked; + } + + public long[] getSfixed64sPacked() { + return sfixed64sPacked; + } + + public void setSfixed64sPacked(long[] sfixed64sPacked) { + this.sfixed64sPacked = sfixed64sPacked; + } + + public float[] getFloatsPacked() { + return floatsPacked; + } + + public void setFloatsPacked(float[] floatsPacked) { + this.floatsPacked = floatsPacked; + } + + public double[] getDoublesPacked() { + return doublesPacked; + } + + public void setDoublesPacked(double[] doublesPacked) { + this.doublesPacked = doublesPacked; + } + + public boolean[] getBoolsPacked() { + return boolsPacked; + } + + public void setBoolsPacked(boolean[] boolsPacked) { + this.boolsPacked = boolsPacked; + } + + public int[] getInt32sNotPacked() { + return int32sNotPacked; + } + + public void setInt32sNotPacked(int[] int32sNotPacked) { + this.int32sNotPacked = int32sNotPacked; + } + + public long[] getInt64sNotPacked() { + return int64sNotPacked; + } + + public void setInt64sNotPacked(long[] int64sNotPacked) { + this.int64sNotPacked = int64sNotPacked; + } + + public int[] getUint32sNotPacked() { + return uint32sNotPacked; + } + + public void setUint32sNotPacked(int[] uint32sNotPacked) { + this.uint32sNotPacked = uint32sNotPacked; + } + + public long[] getUint64sNotPacked() { + return uint64sNotPacked; + } + + public void setUint64sNotPacked(long[] uint64sNotPacked) { + this.uint64sNotPacked = uint64sNotPacked; + } + + public int[] getSint32sNotPacked() { + return sint32sNotPacked; + } + + public void setSint32sNotPacked(int[] sint32sNotPacked) { + this.sint32sNotPacked = sint32sNotPacked; + } + + public long[] getSint64sNotPacked() { + return sint64sNotPacked; + } + + public void setSint64sNotPacked(long[] sint64sNotPacked) { + this.sint64sNotPacked = sint64sNotPacked; + } + + public int[] getFixed32sNotPacked() { + return fixed32sNotPacked; + } + + public void setFixed32sNotPacked(int[] fixed32sNotPacked) { + this.fixed32sNotPacked = fixed32sNotPacked; + } + + public long[] getFixed64sNotPacked() { + return fixed64sNotPacked; + } + + public void setFixed64sNotPacked(long[] fixed64sNotPacked) { + this.fixed64sNotPacked = fixed64sNotPacked; + } + + public int[] getSfixed32sNotPacked() { + return sfixed32sNotPacked; + } + + public void setSfixed32sNotPacked(int[] sfixed32sNotPacked) { + this.sfixed32sNotPacked = sfixed32sNotPacked; + } + + public long[] getSfixed64sNotPacked() { + return sfixed64sNotPacked; + } + + public void setSfixed64sNotPacked(long[] sfixed64sNotPacked) { + this.sfixed64sNotPacked = sfixed64sNotPacked; + } + + public float[] getFloatsNotPacked() { + return floatsNotPacked; + } + + public void setFloatsNotPacked(float[] floatsNotPacked) { + this.floatsNotPacked = floatsNotPacked; + } + + public double[] getDoublesNotPacked() { + return doublesNotPacked; + } + + public void setDoublesNotPacked(double[] doublesNotPacked) { + this.doublesNotPacked = doublesNotPacked; + } + + public boolean[] getBoolsNotPacked() { + return boolsNotPacked; + } + + public void setBoolsNotPacked(boolean[] boolsNotPacked) { + this.boolsNotPacked = boolsNotPacked; + } + + public Color[] getColorsPacked() { + return colorsPacked; + } + + public void setColorsPacked(Color[] colorsPacked) { + this.colorsPacked = colorsPacked; + } + + public Color[] getColorsNotPacked() { + return colorsNotPacked; + } + + public void setColorsNotPacked(Color[] colorsNotPacked) { + this.colorsNotPacked = colorsNotPacked; + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/PrimitiveWrapperArrays.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/PrimitiveWrapperArrays.java new file mode 100644 index 00000000000..23392a9ef30 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/PrimitiveWrapperArrays.java @@ -0,0 +1,301 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License")Packed; you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.model; + +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; + +public class PrimitiveWrapperArrays { + private Integer[] int32sPacked; + + private Long[] int64sPacked; + + private Integer[] uint32sPacked; + + private Long[] uint64sPacked; + + private Integer[] sint32sPacked; + + private Long[] sint64sPacked; + + private Integer[] fixed32sPacked; + + private Long[] fixed64sPacked; + + private Integer[] sfixed32sPacked; + + private Long[] sfixed64sPacked; + + private Float[] floatsPacked; + + private Double[] doublesPacked; + + private Boolean[] boolsPacked; + + private Color[] colorsPacked; + + private Integer[] int32sNotPacked; + + private Long[] int64sNotPacked; + + private Integer[] uint32sNotPacked; + + private Long[] uint64sNotPacked; + + private Integer[] sint32sNotPacked; + + private Long[] sint64sNotPacked; + + private Integer[] fixed32sNotPacked; + + private Long[] fixed64sNotPacked; + + private Integer[] sfixed32sNotPacked; + + private Long[] sfixed64sNotPacked; + + private Float[] floatsNotPacked; + + private Double[] doublesNotPacked; + + private Boolean[] boolsNotPacked; + + private Color[] colorsNotPacked; + + public Integer[] getInt32sPacked() { + return int32sPacked; + } + + public void setInt32sPacked(Integer[] int32sPacked) { + this.int32sPacked = int32sPacked; + } + + public Long[] getInt64sPacked() { + return int64sPacked; + } + + public void setInt64sPacked(Long[] int64sPacked) { + this.int64sPacked = int64sPacked; + } + + public Integer[] getUint32sPacked() { + return uint32sPacked; + } + + public void setUint32sPacked(Integer[] uint32sPacked) { + this.uint32sPacked = uint32sPacked; + } + + public Long[] getUint64sPacked() { + return uint64sPacked; + } + + public void setUint64sPacked(Long[] uint64sPacked) { + this.uint64sPacked = uint64sPacked; + } + + public Integer[] getSint32sPacked() { + return sint32sPacked; + } + + public void setSint32sPacked(Integer[] sint32sPacked) { + this.sint32sPacked = sint32sPacked; + } + + public Long[] getSint64sPacked() { + return sint64sPacked; + } + + public void setSint64sPacked(Long[] sint64sPacked) { + this.sint64sPacked = sint64sPacked; + } + + public Integer[] getFixed32sPacked() { + return fixed32sPacked; + } + + public void setFixed32sPacked(Integer[] fixed32sPacked) { + this.fixed32sPacked = fixed32sPacked; + } + + public Long[] getFixed64sPacked() { + return fixed64sPacked; + } + + public void setFixed64sPacked(Long[] fixed64sPacked) { + this.fixed64sPacked = fixed64sPacked; + } + + public Integer[] getSfixed32sPacked() { + return sfixed32sPacked; + } + + public void setSfixed32sPacked(Integer[] sfixed32sPacked) { + this.sfixed32sPacked = sfixed32sPacked; + } + + public Long[] getSfixed64sPacked() { + return sfixed64sPacked; + } + + public void setSfixed64sPacked(Long[] sfixed64sPacked) { + this.sfixed64sPacked = sfixed64sPacked; + } + + public Float[] getFloatsPacked() { + return floatsPacked; + } + + public void setFloatsPacked(Float[] floatsPacked) { + this.floatsPacked = floatsPacked; + } + + public Double[] getDoublesPacked() { + return doublesPacked; + } + + public void setDoublesPacked(Double[] doublesPacked) { + this.doublesPacked = doublesPacked; + } + + public Boolean[] getBoolsPacked() { + return boolsPacked; + } + + public void setBoolsPacked(Boolean[] boolsPacked) { + this.boolsPacked = boolsPacked; + } + + public Integer[] getInt32sNotPacked() { + return int32sNotPacked; + } + + public void setInt32sNotPacked(Integer[] int32sNotPacked) { + this.int32sNotPacked = int32sNotPacked; + } + + public Long[] getInt64sNotPacked() { + return int64sNotPacked; + } + + public void setInt64sNotPacked(Long[] int64sNotPacked) { + this.int64sNotPacked = int64sNotPacked; + } + + public Integer[] getUint32sNotPacked() { + return uint32sNotPacked; + } + + public void setUint32sNotPacked(Integer[] uint32sNotPacked) { + this.uint32sNotPacked = uint32sNotPacked; + } + + public Long[] getUint64sNotPacked() { + return uint64sNotPacked; + } + + public void setUint64sNotPacked(Long[] uint64sNotPacked) { + this.uint64sNotPacked = uint64sNotPacked; + } + + public Integer[] getSint32sNotPacked() { + return sint32sNotPacked; + } + + public void setSint32sNotPacked(Integer[] sint32sNotPacked) { + this.sint32sNotPacked = sint32sNotPacked; + } + + public Long[] getSint64sNotPacked() { + return sint64sNotPacked; + } + + public void setSint64sNotPacked(Long[] sint64sNotPacked) { + this.sint64sNotPacked = sint64sNotPacked; + } + + public Integer[] getFixed32sNotPacked() { + return fixed32sNotPacked; + } + + public void setFixed32sNotPacked(Integer[] fixed32sNotPacked) { + this.fixed32sNotPacked = fixed32sNotPacked; + } + + public Long[] getFixed64sNotPacked() { + return fixed64sNotPacked; + } + + public void setFixed64sNotPacked(Long[] fixed64sNotPacked) { + this.fixed64sNotPacked = fixed64sNotPacked; + } + + public Integer[] getSfixed32sNotPacked() { + return sfixed32sNotPacked; + } + + public void setSfixed32sNotPacked(Integer[] sfixed32sNotPacked) { + this.sfixed32sNotPacked = sfixed32sNotPacked; + } + + public Long[] getSfixed64sNotPacked() { + return sfixed64sNotPacked; + } + + public void setSfixed64sNotPacked(Long[] sfixed64sNotPacked) { + this.sfixed64sNotPacked = sfixed64sNotPacked; + } + + public Float[] getFloatsNotPacked() { + return floatsNotPacked; + } + + public void setFloatsNotPacked(Float[] floatsNotPacked) { + this.floatsNotPacked = floatsNotPacked; + } + + public Double[] getDoublesNotPacked() { + return doublesNotPacked; + } + + public void setDoublesNotPacked(Double[] doublesNotPacked) { + this.doublesNotPacked = doublesNotPacked; + } + + public Boolean[] getBoolsNotPacked() { + return boolsNotPacked; + } + + public void setBoolsNotPacked(Boolean[] boolsNotPacked) { + this.boolsNotPacked = boolsNotPacked; + } + + public Color[] getColorsPacked() { + return colorsPacked; + } + + public void setColorsPacked(Color[] colorsPacked) { + this.colorsPacked = colorsPacked; + } + + public Color[] getColorsNotPacked() { + return colorsNotPacked; + } + + public void setColorsNotPacked(Color[] colorsNotPacked) { + this.colorsNotPacked = colorsNotPacked; + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/Root.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/Root.java new file mode 100644 index 00000000000..9bca6e5cdbb --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/Root.java @@ -0,0 +1,769 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.model; + +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; + +import io.protostuff.Tag; + +/** + * Tag annotation is only for protoStuff, not necessary for ServiceComb codec + */ +public class Root { + @Tag(1) + private int int32; + + @Tag(2) + private long int64; + + @Tag(3) + private int uint32; + + @Tag(4) + private long uint64; + + @Tag(5) + private int sint32; + + @Tag(6) + private long sint64; + + @Tag(7) + private int fixed32; + + @Tag(8) + private long fixed64; + + @Tag(9) + private int sfixed32; + + @Tag(10) + private long sfixed64; + + @Tag(11) + private float floatValue; + + @Tag(12) + private double doubleValue; + + @Tag(13) + private boolean bool; + + @Tag(20) + private Integer objInt32; + + @Tag(21) + private Long objInt64; + + @Tag(22) + private Integer objUint32; + + @Tag(23) + private Long objUint64; + + @Tag(24) + private Integer objSint32; + + @Tag(25) + private Long objSint64; + + @Tag(26) + private Integer objFixed32; + + @Tag(27) + private Long objFixed64; + + @Tag(28) + private Integer objSfixed32; + + @Tag(29) + private Long objSfixed64; + + @Tag(30) + private Float objFloatValue; + + @Tag(31) + private Double objDoubleValue; + + @Tag(32) + private Boolean objBool; + + @Tag(40) + private String string; + + @Tag(41) + private byte[] bytes; + + @Tag(42) + private Color color; + + @Tag(43) + private User user; + + @Tag(44) + private Root typeRecursive; + + @Tag(50) + private Object any; + + @Tag(51) + private List anys; + + @Tag(60) + private Map ssMap; + + @Tag(61) + private Map sint32Map; + + @Tag(62) + private Map spMap; + + // repeated packed + @Tag(70) + private List int32sPacked; + + @Tag(71) + private List int64sPacked; + + @Tag(72) + private List uint32sPacked; + + @Tag(73) + private List uint64sPacked; + + @Tag(74) + private List sint32sPacked; + + @Tag(75) + private List sint64sPacked; + + @Tag(76) + private List fixed32sPacked; + + @Tag(77) + private List fixed64sPacked; + + @Tag(78) + private List sfixed32sPacked; + + @Tag(79) + private List sfixed64sPacked; + + @Tag(80) + private List floatsPacked; + + @Tag(81) + private List doublesPacked; + + @Tag(82) + private List boolsPacked; + + @Tag(83) + private List colorsPacked; + + // repeated not packed + @Tag(90) + private List int32sNotPacked; + + @Tag(91) + private List int64sNotPacked; + + @Tag(92) + private List uint32sNotPacked; + + @Tag(93) + private List uint64sNotPacked; + + @Tag(94) + private List sint32sNotPacked; + + @Tag(95) + private List sint64sNotPacked; + + @Tag(96) + private List fixed32sNotPacked; + + @Tag(97) + private List fixed64sNotPacked; + + @Tag(98) + private List sfixed32sNotPacked; + + @Tag(99) + private List sfixed64sNotPacked; + + @Tag(100) + private List floatsNotPacked; + + @Tag(101) + private List doublesNotPacked; + + @Tag(102) + private List boolsNotPacked; + + @Tag(103) + private List colorsNotPacked; + + @Tag(110) + private List strings; + + @Tag(111) + private List bytess; + + @Tag(112) + private List users; + + public int getInt32() { + return int32; + } + + public void setInt32(int int32) { + this.int32 = int32; + } + + public long getInt64() { + return int64; + } + + public void setInt64(long int64) { + this.int64 = int64; + } + + public int getUint32() { + return uint32; + } + + public void setUint32(int uint32) { + this.uint32 = uint32; + } + + public long getUint64() { + return uint64; + } + + public void setUint64(long uint64) { + this.uint64 = uint64; + } + + public int getSint32() { + return sint32; + } + + public void setSint32(int sint32) { + this.sint32 = sint32; + } + + public long getSint64() { + return sint64; + } + + public void setSint64(long sint64) { + this.sint64 = sint64; + } + + public int getFixed32() { + return fixed32; + } + + public void setFixed32(int fixed32) { + this.fixed32 = fixed32; + } + + public long getFixed64() { + return fixed64; + } + + public void setFixed64(long fixed64) { + this.fixed64 = fixed64; + } + + public int getSfixed32() { + return sfixed32; + } + + public void setSfixed32(int sfixed32) { + this.sfixed32 = sfixed32; + } + + public long getSfixed64() { + return sfixed64; + } + + public void setSfixed64(long sfixed64) { + this.sfixed64 = sfixed64; + } + + public float getFloatValue() { + return floatValue; + } + + public void setFloatValue(float floatValue) { + this.floatValue = floatValue; + } + + public double getDoubleValue() { + return doubleValue; + } + + public void setDoubleValue(double doubleValue) { + this.doubleValue = doubleValue; + } + + public boolean isBool() { + return bool; + } + + public void setBool(boolean bool) { + this.bool = bool; + } + + public String getString() { + return string; + } + + public void setString(String string) { + this.string = string; + } + + public byte[] getBytes() { + return bytes; + } + + public void setBytes(byte[] bytes) { + this.bytes = bytes; + } + + public Color getColor() { + return color; + } + + public void setColor(Color color) { + this.color = color; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public Map getSsMap() { + return ssMap; + } + + public void setSsMap(Map ssMap) { + this.ssMap = ssMap; + } + + public Map getSpMap() { + return spMap; + } + + public void setSpMap(Map spMap) { + this.spMap = spMap; + } + + public Object getAny() { + return any; + } + + public void setAny(Object any) { + this.any = any; + } + + public List getAnys() { + return anys; + } + + public void setAnys(List anys) { + this.anys = anys; + } + + public Root getTypeRecursive() { + return typeRecursive; + } + + public void setTypeRecursive(Root typeRecursive) { + this.typeRecursive = typeRecursive; + } + + public List getInt32sPacked() { + return int32sPacked; + } + + public void setInt32sPacked(List int32sPacked) { + this.int32sPacked = int32sPacked; + } + + public List getInt64sPacked() { + return int64sPacked; + } + + public void setInt64sPacked(List int64sPacked) { + this.int64sPacked = int64sPacked; + } + + public List getUint32sPacked() { + return uint32sPacked; + } + + public void setUint32sPacked(List uint32sPacked) { + this.uint32sPacked = uint32sPacked; + } + + public List getUint64sPacked() { + return uint64sPacked; + } + + public void setUint64sPacked(List uint64sPacked) { + this.uint64sPacked = uint64sPacked; + } + + public List getSint32sPacked() { + return sint32sPacked; + } + + public void setSint32sPacked(List sint32sPacked) { + this.sint32sPacked = sint32sPacked; + } + + public List getSint64sPacked() { + return sint64sPacked; + } + + public void setSint64sPacked(List sint64sPacked) { + this.sint64sPacked = sint64sPacked; + } + + public List getFixed32sPacked() { + return fixed32sPacked; + } + + public void setFixed32sPacked(List fixed32sPacked) { + this.fixed32sPacked = fixed32sPacked; + } + + public List getFixed64sPacked() { + return fixed64sPacked; + } + + public void setFixed64sPacked(List fixed64sPacked) { + this.fixed64sPacked = fixed64sPacked; + } + + public List getSfixed32sPacked() { + return sfixed32sPacked; + } + + public void setSfixed32sPacked(List sfixed32sPacked) { + this.sfixed32sPacked = sfixed32sPacked; + } + + public List getSfixed64sPacked() { + return sfixed64sPacked; + } + + public void setSfixed64sPacked(List sfixed64sPacked) { + this.sfixed64sPacked = sfixed64sPacked; + } + + public List getFloatsPacked() { + return floatsPacked; + } + + public void setFloatsPacked(List floatsPacked) { + this.floatsPacked = floatsPacked; + } + + public List getDoublesPacked() { + return doublesPacked; + } + + public void setDoublesPacked(List doublesPacked) { + this.doublesPacked = doublesPacked; + } + + public List getBoolsPacked() { + return boolsPacked; + } + + public void setBoolsPacked(List boolsPacked) { + this.boolsPacked = boolsPacked; + } + + public List getColorsPacked() { + return colorsPacked; + } + + public void setColorsPacked(List colorsPacked) { + this.colorsPacked = colorsPacked; + } + + public List getInt32sNotPacked() { + return int32sNotPacked; + } + + public void setInt32sNotPacked(List int32sNotPacked) { + this.int32sNotPacked = int32sNotPacked; + } + + public List getInt64sNotPacked() { + return int64sNotPacked; + } + + public void setInt64sNotPacked(List int64sNotPacked) { + this.int64sNotPacked = int64sNotPacked; + } + + public List getUint32sNotPacked() { + return uint32sNotPacked; + } + + public void setUint32sNotPacked(List uint32sNotPacked) { + this.uint32sNotPacked = uint32sNotPacked; + } + + public List getUint64sNotPacked() { + return uint64sNotPacked; + } + + public void setUint64sNotPacked(List uint64sNotPacked) { + this.uint64sNotPacked = uint64sNotPacked; + } + + public List getSint32sNotPacked() { + return sint32sNotPacked; + } + + public void setSint32sNotPacked(List sint32sNotPacked) { + this.sint32sNotPacked = sint32sNotPacked; + } + + public List getSint64sNotPacked() { + return sint64sNotPacked; + } + + public void setSint64sNotPacked(List sint64sNotPacked) { + this.sint64sNotPacked = sint64sNotPacked; + } + + public List getFixed32sNotPacked() { + return fixed32sNotPacked; + } + + public void setFixed32sNotPacked(List fixed32sNotPacked) { + this.fixed32sNotPacked = fixed32sNotPacked; + } + + public List getFixed64sNotPacked() { + return fixed64sNotPacked; + } + + public void setFixed64sNotPacked(List fixed64sNotPacked) { + this.fixed64sNotPacked = fixed64sNotPacked; + } + + public List getSfixed32sNotPacked() { + return sfixed32sNotPacked; + } + + public void setSfixed32sNotPacked(List sfixed32sNotPacked) { + this.sfixed32sNotPacked = sfixed32sNotPacked; + } + + public List getSfixed64sNotPacked() { + return sfixed64sNotPacked; + } + + public void setSfixed64sNotPacked(List sfixed64sNotPacked) { + this.sfixed64sNotPacked = sfixed64sNotPacked; + } + + public List getFloatsNotPacked() { + return floatsNotPacked; + } + + public void setFloatsNotPacked(List floatsNotPacked) { + this.floatsNotPacked = floatsNotPacked; + } + + public List getDoublesNotPacked() { + return doublesNotPacked; + } + + public void setDoublesNotPacked(List doublesNotPacked) { + this.doublesNotPacked = doublesNotPacked; + } + + public List getBoolsNotPacked() { + return boolsNotPacked; + } + + public void setBoolsNotPacked(List boolsNotPacked) { + this.boolsNotPacked = boolsNotPacked; + } + + public List getColorsNotPacked() { + return colorsNotPacked; + } + + public void setColorsNotPacked( + List colorsNotPacked) { + this.colorsNotPacked = colorsNotPacked; + } + + public List getStrings() { + return strings; + } + + public void setStrings(List strings) { + this.strings = strings; + } + + public List getBytess() { + return bytess; + } + + public void setBytess(List bytess) { + this.bytess = bytess; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + public Integer getObjInt32() { + return objInt32; + } + + public void setObjInt32(Integer objInt32) { + this.objInt32 = objInt32; + } + + public Long getObjInt64() { + return objInt64; + } + + public void setObjInt64(Long objInt64) { + this.objInt64 = objInt64; + } + + public Integer getObjUint32() { + return objUint32; + } + + public void setObjUint32(Integer objUint32) { + this.objUint32 = objUint32; + } + + public Long getObjUint64() { + return objUint64; + } + + public void setObjUint64(Long objUint64) { + this.objUint64 = objUint64; + } + + public Integer getObjSint32() { + return objSint32; + } + + public void setObjSint32(Integer objSint32) { + this.objSint32 = objSint32; + } + + public Long getObjSint64() { + return objSint64; + } + + public void setObjSint64(Long objSint64) { + this.objSint64 = objSint64; + } + + public Integer getObjFixed32() { + return objFixed32; + } + + public void setObjFixed32(Integer objFixed32) { + this.objFixed32 = objFixed32; + } + + public Long getObjFixed64() { + return objFixed64; + } + + public void setObjFixed64(Long objFixed64) { + this.objFixed64 = objFixed64; + } + + public Integer getObjSfixed32() { + return objSfixed32; + } + + public void setObjSfixed32(Integer objSfixed32) { + this.objSfixed32 = objSfixed32; + } + + public Long getObjSfixed64() { + return objSfixed64; + } + + public void setObjSfixed64(Long objSfixed64) { + this.objSfixed64 = objSfixed64; + } + + public Float getObjFloatValue() { + return objFloatValue; + } + + public void setObjFloatValue(Float objFloatValue) { + this.objFloatValue = objFloatValue; + } + + public Double getObjDoubleValue() { + return objDoubleValue; + } + + public void setObjDoubleValue(Double objDoubleValue) { + this.objDoubleValue = objDoubleValue; + } + + public Boolean isObjBool() { + return objBool; + } + + public void setObjBool(Boolean objBool) { + this.objBool = objBool; + } + + public Map getSint32Map() { + return sint32Map; + } + + public void setSint32Map(Map sint32Map) { + this.sint32Map = sint32Map; + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/User.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/User.java new file mode 100644 index 00000000000..06c7d2e8e69 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/model/User.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.foundation.protobuf.internal.model; + +public class User { + private String name; + + private Root typeRecursive; + + public User() { + } + + public User(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public User name(String name) { + this.name = name; + return this; + } + + public Root getTypeRecursive() { + return typeRecursive; + } + + public void setTypeRecursive(Root typeRecursive) { + this.typeRecursive = typeRecursive; + } + + @Override + public String toString() { + return "User{" + + "name='" + name + '\'' + + '}'; + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/parser/TestProtoParser.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/parser/TestProtoParser.java new file mode 100644 index 00000000000..268281b9104 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/parser/TestProtoParser.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.foundation.protobuf.internal.parser; + +import com.google.common.base.MoreObjects; +import io.protostuff.compiler.model.Proto; +import org.apache.commons.io.IOUtils; +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; + +public class TestProtoParser { + @Test + public void parse() throws IOException { + URL url = Thread.currentThread().getContextClassLoader().getResource("protobufRoot.proto"); + String content = IOUtils.toString(url, StandardCharsets.UTF_8); + + ProtoParser parser = new ProtoParser(); + Proto protoFromContent = parser.parseFromContent(content); + Proto protoFromName = parser.parse("protobufRoot.proto"); + + Assertions.assertNotNull(protoFromContent.getMessage("Root")); + Assertions.assertNotNull(protoFromContent.getMessage("User")); + + Assertions.assertEquals(MoreObjects.toStringHelper(protoFromContent) + .omitNullValues() + .add("messages", protoFromContent.getMessages()) + .toString(), + MoreObjects.toStringHelper(protoFromName) + .omitNullValues() + .add("messages", protoFromName.getMessages()) + .toString()); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestAnySchema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestAnySchema.java new file mode 100644 index 00000000000..8bcbddd9d30 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestAnySchema.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.foundation.protobuf.internal.schema; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.internal.ProtoConst; +import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; +import org.apache.servicecomb.foundation.protobuf.internal.model.Root; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; + +import com.google.protobuf.Any; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestAnySchema extends TestSchemaBase { + @Test + public void empty() throws Throwable { + scbMap = new HashMap<>(); + scbMap.put("any", null); + Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); + } + + @Test + public void anys_pack() throws IOException { + builder + .addAnys(Any.pack(ProtobufRoot.User.newBuilder().setName("n1").build())) + .addAnys(Any.pack(ProtobufRoot.User.newBuilder().setName("n2").build())); + check(); + } + + @Test + public void anys_json() throws IOException { + Root root = new Root(); + root.setAnys(Arrays.asList("abc", "123")); + + scbRootBytes = rootSerializer.serialize(root); + root = rootDeserializer.deserialize(scbRootBytes); + MatcherAssert.assertThat(root.getAnys(), Matchers.contains("abc", "123")); + } + + @Test + public void pack() throws Throwable { + builder.setAny(Any.pack(ProtobufRoot.User.newBuilder().setName("n1").build())); + check(); + + Map map = new HashMap<>(); + map.put(ProtoConst.JSON_ID_NAME, "User"); + map.put("name", "n1"); + Root root = new Root(); + root.setAny(map); + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(root)); + } + + @SuppressWarnings("unchecked") + @Test + public void json_fromMapWithoutType() throws Throwable { + Map map = new HashMap<>(); + map.put("name", "n1"); + Root root = new Root(); + root.setAny(map); + + scbRootBytes = rootSerializer.serialize(root); + root = rootDeserializer.deserialize(scbRootBytes); + MatcherAssert.assertThat(root.getAny(), Matchers.instanceOf(Map.class)); + MatcherAssert.assertThat((Map) root.getAny(), Matchers.hasEntry("name", "n1")); + + RootDeserializer> deserializer = protoMapper.createRootDeserializer("Root", Map.class); + map = deserializer.deserialize(scbRootBytes); + MatcherAssert.assertThat((Map) map.get("any"), Matchers.hasEntry("name", "n1")); + } + + @Test + public void json() throws Throwable { + Root root = new Root(); + root.setAny("abc"); + + scbRootBytes = rootSerializer.serialize(root); + root = rootDeserializer.deserialize(scbRootBytes); + Assertions.assertEquals("abc", root.getAny()); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestMapSchema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestMapSchema.java new file mode 100644 index 00000000000..1013f3a6cf8 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestMapSchema.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.foundation.protobuf.internal.schema; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; +import org.junit.jupiter.api.Test; + +public class TestMapSchema extends TestSchemaBase { + @Test + public void ssMap() throws Throwable { + Map ssMap = new HashMap<>(); + ssMap.put("k1", "v1"); + ssMap.put("k2", "v2"); + builder.putAllSsMap(ssMap); + + check(); + } + + @Test + public void spMap() throws Throwable { + builder.putSpMap("k1", ProtobufRoot.User.newBuilder().setName("n1").build()); + builder.putSpMap("k2", ProtobufRoot.User.newBuilder().setName("n2").build()); + + check(); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestMessageSchema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestMessageSchema.java new file mode 100644 index 00000000000..4e504540874 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestMessageSchema.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.foundation.protobuf.internal.schema; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; +import org.apache.servicecomb.foundation.protobuf.internal.model.CustomGeneric; +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; +import org.apache.servicecomb.foundation.protobuf.internal.model.User; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestMessageSchema extends TestSchemaBase { + @Test + public void empty() throws Throwable { + check(); + + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(null)); + } + + @Test + public void generic() throws Throwable { + JavaType javaType = TypeFactory.defaultInstance().constructParametricType(CustomGeneric.class, User.class); + RootDeserializer> genericDeserializer = protoMapper.createRootDeserializer("Root", javaType); + + builder.setUser(ProtobufRoot.User.newBuilder().setName("name1").build()); + check(genericDeserializer, mapRootDeserializer, rootSerializer, false); + + @SuppressWarnings("unchecked") + CustomGeneric generic = (CustomGeneric) scbRoot; + Assertions.assertNotNull(generic.user); + } + + @Test + public void normal() throws Throwable { + builder.setString("abc"); + builder.setInt64(1L); + builder.setUser(ProtobufRoot.User.newBuilder().setName("name").build()); + + check(); + + // map + Map map = new LinkedHashMap<>(); + map.put("int64", 1); + map.put("string", "abc"); + + Map userMap = new LinkedHashMap<>(); + userMap.put("name", "name"); + map.put("user", userMap); + + map.put("notExist", null); + + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(map)); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestRepeatedSchema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestRepeatedSchema.java new file mode 100644 index 00000000000..a26156a3778 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/TestRepeatedSchema.java @@ -0,0 +1,242 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; + +import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.Color; +import org.apache.servicecomb.foundation.protobuf.internal.model.User; + +import com.google.protobuf.ByteString; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestRepeatedSchema extends TestSchemaBase { + public static class RootWithArray { + public byte[][] bytess; + + public String[] strings; + + public User[] users; + } + + @Test + public void fixed32s() throws Exception { + builder.addAllFixed32SPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void fixed32sNotPacked() throws Exception { + builder.addAllFixed32SNotPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void sfixed32s() throws Exception { + builder.addAllSfixed32SPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void sfixed32sNotPacked() throws Exception { + builder.addAllSfixed32SNotPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void int32s() throws Exception { + builder.addAllInt32SPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void int32sNotPacked() throws Exception { + builder.addAllInt32SNotPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void sint32s() throws Exception { + builder.addAllSint32SPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void sint32sNotPacked() throws Exception { + builder.addAllSint32SNotPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void uint32s() throws Exception { + builder.addAllUint32SPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void uint32sNotPacked() throws Exception { + builder.addAllUint32SNotPacked(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void fixed64s() throws Exception { + builder.addAllFixed64SPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void fixed64sNotPacked() throws Exception { + builder.addAllFixed64SNotPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void sfixed64s() throws Exception { + builder.addAllSfixed64SPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void sfixed64sNotPacked() throws Exception { + builder.addAllSfixed64SNotPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void int64s() throws Exception { + builder.addAllInt64SPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void int64sNotPacked() throws Exception { + builder.addAllInt64SNotPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void sint64s() throws Exception { + builder.addAllSint64SPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void sint64sNotPacked() throws Exception { + builder.addAllSint64SNotPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void uint64s() throws Exception { + builder.addAllUint64SPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void uint64sNotPacked() throws Exception { + builder.addAllUint64SNotPacked(Arrays.asList(Long.MIN_VALUE, 0L, Long.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void floats() throws Exception { + builder.addAllFloatsPacked(Arrays.asList(Float.MIN_VALUE, (float) 0.0, Float.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void floatsNotPacked() throws Exception { + builder.addAllFloatsNotPacked(Arrays.asList(Float.MIN_VALUE, (float) 0.0, Float.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void doubles() throws Exception { + builder.addAllDoublesPacked(Arrays.asList(Double.MIN_VALUE, 0.0, Double.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void doublesNotPacked() throws Exception { + builder.addAllDoublesNotPacked(Arrays.asList(Double.MIN_VALUE, 0.0, Double.MAX_VALUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void bools() throws Exception { + builder.addAllBoolsPacked(Arrays.asList(Boolean.FALSE, Boolean.TRUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void boolsNotPacked() throws Exception { + builder.addAllBoolsNotPacked(Arrays.asList(Boolean.FALSE, Boolean.TRUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void enums() throws Exception { + builder.addAllColorsPacked(Arrays.asList(Color.RED, Color.BLUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void enumsNotPacked() throws Exception { + builder.addAllColorsNotPacked(Arrays.asList(Color.RED, Color.BLUE)); + checkRepeatedWithPrimitive(); + } + + @Test + public void bytess() throws Throwable { + List sList = Arrays.asList("v1".getBytes(StandardCharsets.UTF_8), "v2".getBytes(StandardCharsets.UTF_8)); + builder.addAllBytess(Arrays.asList(ByteString.copyFromUtf8("v1"), ByteString.copyFromUtf8("v2"))); + check(); + + RootWithArray rootWithArray = new RootWithArray(); + rootWithArray.bytess = sList.toArray(new byte[0][]); + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(rootWithArray)); + } + + @Test + public void strings() throws Throwable { + List sList = Arrays.asList("v1", "v2"); + builder.addAllStrings(sList); + check(); + + RootWithArray rootWithArray = new RootWithArray(); + rootWithArray.strings = sList.toArray(new String[0]); + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(rootWithArray)); + } + + @Test + public void users() throws Throwable { + builder.addUsers(ProtobufRoot.User.newBuilder().setName("name1").build()); + builder.addUsers(ProtobufRoot.User.newBuilder().setName("name2").build()); + + check(); + + RootWithArray rootWithArray = new RootWithArray(); + rootWithArray.users = new User[] {new User("name1"), new User("name2")}; + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(rootWithArray)); + } +} 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 new file mode 100644 index 00000000000..5862c723bfc --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBoolSchema.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.scalar; + +import java.util.HashMap; + +import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; +import org.apache.servicecomb.foundation.protobuf.internal.model.User; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestBoolSchema extends TestSchemaBase { + public TestBoolSchema() { + initFields("bool", "objBool"); + } + + @Test + public void testTrue() throws Throwable { + // normal + builder.setBool(true); + check(); + + // equalsIgnoreCase + doTestFromString("true"); + doTestFromString("true"); + } + + @Test + public void testFalse() throws Throwable { + // normal + builder.setBool(false); + check(); + + // all not true is false + doTestFromString("false"); + doTestFromString("abcd"); + } + + protected void doTestFromString(String value) throws Throwable { + // string[] + scbMap = new HashMap<>(); + scbMap.put("bool", new String[] {value}); + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(scbMap)); + + // string + scbMap.put("bool", value); + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(scbMap)); + } + + @Test + public void nullOrEmpty() throws Throwable { + // null + scbMap = new HashMap<>(); + Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); + + // empty string[] + scbMap.put("bool", new String[] {}); + Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); + } + + @Test + public void type_invalid() throws Throwable { + IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { + scbMap = new HashMap<>(); + scbMap.put("bool", new User()); + rootSerializer.serialize(scbMap); + }); + Assertions.assertEquals("not support serialize from org.apache.servicecomb.foundation.protobuf.internal.model.User to proto bool, field=org.apache.servicecomb.foundation.protobuf.internal.model.Root:bool", + exception.getMessage()); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBytesSchema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBytesSchema.java new file mode 100644 index 00000000000..db350e87596 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBytesSchema.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; + +import java.util.HashMap; + +import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; +import org.apache.servicecomb.foundation.protobuf.internal.model.User; + +import com.google.protobuf.ByteString; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestBytesSchema extends TestSchemaBase { + public TestBytesSchema() { + initField("bytes"); + } + + @Test + public void normal() throws Throwable { + byte[] value = "abc".getBytes(); + builder.setBytes(ByteString.copyFrom(value)); + check(); + } + + @Test + public void type_invalid() { + IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { + scbMap = new HashMap<>(); + scbMap.put("bytes", new User()); + rootSerializer.serialize(scbMap); + }); + Assertions.assertEquals("not support serialize from org.apache.servicecomb.foundation.protobuf.internal.model.User to proto bytes, field=org.apache.servicecomb.foundation.protobuf.internal.model.Root:bytes", + exception.getMessage()); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestDoubleSchema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestDoubleSchema.java new file mode 100644 index 00000000000..2946db9d097 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestDoubleSchema.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; + +public class TestDoubleSchema extends TestNumberBaseSchema { + public TestDoubleSchema() { + minValue = Double.MIN_VALUE; + maxValue = Double.MAX_VALUE; + initFields("doubleValue", "objDoubleValue"); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestEnumSchema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestEnumSchema.java new file mode 100644 index 00000000000..4c70eb2e40e --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestEnumSchema.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.scalar; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; +import org.apache.servicecomb.foundation.protobuf.internal.model.User; +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestEnumSchema extends TestSchemaBase { + @Test + public void empty() throws Throwable { + // null + scbMap = new HashMap<>(); + Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); + + // empty string[] + scbMap.put("color", new String[] {}); + Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); + } + + public static class EnumRoot { + private int color; + + public int getColor() { + return color; + } + + public void setColor(int color) { + this.color = color; + } + } + + @Test + public void normal() throws Throwable { + builder.setColorValue(2); + check(); + + Map map = new HashMap<>(); + map.put("color", Color.BLUE); + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(map)); + + map.put("color", 2); + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(map)); + + map.put("color", new String[] {"BLUE"}); + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(map)); + + map.put("color", "BLUE"); + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(map)); + + EnumRoot enumRoot = protoMapper.createRootDeserializer("Root", EnumRoot.class).deserialize(protobufBytes); + Assertions.assertEquals(2, enumRoot.color); + } + + enum Sharp { + ROUND + } + + @Test + public void fromInvalidEnum() throws Throwable { + IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { + scbMap = new HashMap<>(); + scbMap.put("color", Sharp.ROUND); + rootSerializer.serialize(scbMap); + }); + Assertions.assertEquals("invalid enum name ROUND for proto Color, field=org.apache.servicecomb.foundation.protobuf.internal.model.Root:color", exception.getMessage()); + } + + @Test + public void fromInvalidNumber() { + IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { + scbMap = new HashMap<>(); + scbMap.put("color", 3); + rootSerializer.serialize(scbMap); + }); + Assertions.assertEquals("invalid enum value 3 for proto Color, field=org.apache.servicecomb.foundation.protobuf.internal.model.Root:color", exception.getMessage()); + } + + @Test + public void type_invalid() { + IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { + scbMap = new HashMap<>(); + scbMap.put("color", new User()); + rootSerializer.serialize(scbMap); + }); + Assertions.assertEquals("not support serialize from org.apache.servicecomb.foundation.protobuf.internal.model.User to proto Color, field=org.apache.servicecomb.foundation.protobuf.internal.model.Root:color", + exception.getMessage()); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestFixed32Schema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestFixed32Schema.java new file mode 100644 index 00000000000..81dedd363c1 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestFixed32Schema.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; + +public class TestFixed32Schema extends TestNumberBaseSchema { + public TestFixed32Schema() { + minValue = Integer.MIN_VALUE; + maxValue = Integer.MAX_VALUE; + initFields("fixed32", "objFixed32"); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestFixed64Schema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestFixed64Schema.java new file mode 100644 index 00000000000..03ea489b263 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestFixed64Schema.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; + +public class TestFixed64Schema extends TestNumberBaseSchema { + public TestFixed64Schema() { + minValue = Long.MIN_VALUE; + maxValue = Long.MAX_VALUE; + initFields("fixed64", "objFixed64"); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestFloatSchema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestFloatSchema.java new file mode 100644 index 00000000000..14bf8d7c9dd --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestFloatSchema.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; + +public class TestFloatSchema extends TestNumberBaseSchema { + public TestFloatSchema() { + minValue = Float.MIN_VALUE; + maxValue = Float.MAX_VALUE; + initFields("floatValue", "objFloatValue"); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestInt32Schema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestInt32Schema.java new file mode 100644 index 00000000000..100d51cdf8f --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestInt32Schema.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; + +public class TestInt32Schema extends TestNumberBaseSchema { + public TestInt32Schema() { + minValue = Integer.MIN_VALUE; + maxValue = Integer.MAX_VALUE; + initFields("int32", "objInt32"); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestInt64Schema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestInt64Schema.java new file mode 100644 index 00000000000..9e6f9095c57 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestInt64Schema.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; + +public class TestInt64Schema extends TestNumberBaseSchema { + public TestInt64Schema() { + minValue = Long.MIN_VALUE; + maxValue = Long.MAX_VALUE; + initFields("int64", "objInt64"); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestNumberBaseSchema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestNumberBaseSchema.java new file mode 100644 index 00000000000..fd9a236a089 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestNumberBaseSchema.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.scalar; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Locale; + +import org.apache.servicecomb.foundation.common.utils.ReflectUtils; +import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; +import org.apache.servicecomb.foundation.test.scaffolding.model.User; + +import io.protostuff.compiler.model.Field; +import io.protostuff.compiler.model.Type; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +@Disabled +public abstract class TestNumberBaseSchema extends TestSchemaBase { + protected Object minValue; + + protected Object maxValue; + + @Test + public void normal() throws Throwable { + byte[] primitiveBytes = doTestPojoNormal(primitiveFieldName); + byte[] bytes = doTestPojoNormal(fieldName); + + doTestMapNormal(primitiveFieldName, maxValue, primitiveBytes); + doTestMapNormal(fieldName, maxValue, bytes); + } + + private void doTestMapNormal(String field, Object value, byte[] expectBytes) throws IOException { + // null + scbMap = new HashMap<>(); + scbMap.put(field, null); + Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); + + // empty string[] + scbMap.put(field, new String[] {}); + Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); + + // string[] + scbMap.put(field, new String[] {String.valueOf(value)}); + Assertions.assertArrayEquals(expectBytes, rootSerializer.serialize(scbMap)); + + // string + scbMap.put(field, String.valueOf(value)); + Assertions.assertArrayEquals(expectBytes, rootSerializer.serialize(scbMap)); + } + + private byte[] doTestPojoNormal(String name) throws Throwable { + builder = ProtobufRoot.Root.newBuilder(); + String setName = "set" + name.substring(0, 1).toUpperCase(Locale.US) + name.substring(1); + Method builderSetter = ReflectUtils.findMethod(builder.getClass(), setName); + + builderSetter.invoke(builder, minValue); + check(); + + builderSetter.invoke(builder, maxValue); + check(); + + return protobufBytes; + } + + @Test + public void primitiveStrings_invalid() throws Throwable { + strings_invalid(primitiveProtoField); + } + + @Test + public void strings_invalid() throws Throwable { + strings_invalid(protoField); + } + + private void strings_invalid(Field field) { + NumberFormatException exception = Assertions.assertThrows(NumberFormatException.class, () -> { + scbMap = new HashMap<>(); + scbMap.put(field.getName(), new String[] {"a"}); + rootSerializer.serialize(scbMap); + }); + Assertions.assertEquals("For input string: \"a\"", exception.getMessage()); + } + + @Test + public void primitiveString_invalid() throws Throwable { + string_invalid(primitiveProtoField); + } + + @Test + public void string_invalid() throws Throwable { + string_invalid(protoField); + } + + private void string_invalid(Field field) { + NumberFormatException exception = Assertions.assertThrows(NumberFormatException.class, () -> { + scbMap = new HashMap<>(); + scbMap.put(field.getName(), "a"); + rootSerializer.serialize(scbMap); + }); + Assertions.assertEquals("For input string: \"a\"", exception.getMessage()); + } + + @Test + public void primitiveType_invalid() { + type_invalid(primitiveProtoField); + } + + @Test + public void type_invalid() throws Throwable { + type_invalid(protoField); + } + + private void type_invalid(Field field) { + IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { + scbMap = new HashMap<>(); + scbMap.put(field.getName(), new User()); + rootSerializer.serialize(scbMap); + }); + Assertions.assertEquals(String.format("not support serialize from %s to proto %s, field=%s:%s", + User.class.getName(), + field.getTypeName(), + ((Type) field.getParent()).getCanonicalName(), + field.getName()), exception.getMessage()); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSFixed32Schema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSFixed32Schema.java new file mode 100644 index 00000000000..b719dba66ba --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSFixed32Schema.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; + +public class TestSFixed32Schema extends TestNumberBaseSchema { + public TestSFixed32Schema() { + minValue = Integer.MIN_VALUE; + maxValue = Integer.MAX_VALUE; + initFields("sfixed32", "objSfixed32"); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSFixed64Schema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSFixed64Schema.java new file mode 100644 index 00000000000..09dc99b01e0 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSFixed64Schema.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; + +public class TestSFixed64Schema extends TestNumberBaseSchema { + public TestSFixed64Schema() { + minValue = Long.MIN_VALUE; + maxValue = Long.MAX_VALUE; + initFields("sfixed64", "objSfixed64"); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSInt32Schema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSInt32Schema.java new file mode 100644 index 00000000000..ab03f49a9fb --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSInt32Schema.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; + +public class TestSInt32Schema extends TestNumberBaseSchema { + public TestSInt32Schema() { + minValue = Integer.MIN_VALUE; + maxValue = Integer.MAX_VALUE; + initFields("sint32", "objSint32"); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSInt64Schema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSInt64Schema.java new file mode 100644 index 00000000000..f9b3de402cc --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestSInt64Schema.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; + +public class TestSInt64Schema extends TestNumberBaseSchema { + public TestSInt64Schema() { + minValue = Long.MIN_VALUE; + maxValue = Long.MAX_VALUE; + initFields("sint64", "objSint64"); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestStringSchema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestStringSchema.java new file mode 100644 index 00000000000..c1084568350 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestStringSchema.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.internal.schema.scalar; + +import java.util.HashMap; + +import org.apache.servicecomb.foundation.protobuf.internal.TestSchemaBase; +import org.apache.servicecomb.foundation.protobuf.internal.model.User; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestStringSchema extends TestSchemaBase { + @Test + public void normal() throws Throwable { + String value = "abc"; + builder.setString(value); + check(); + + // string[] + scbMap = new HashMap<>(); + scbMap.put("string", new String[] {value}); + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(scbMap)); + + // string + scbMap.put("string", value); + Assertions.assertArrayEquals(protobufBytes, rootSerializer.serialize(scbMap)); + } + + @Test + public void nullOrEmpty() throws Throwable { + // null + scbMap = new HashMap<>(); + Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); + + // empty string[] + scbMap.put("string", new String[] {}); + Assertions.assertEquals(0, rootSerializer.serialize(scbMap).length); + } + + @Test + public void type_invalid() throws Throwable { + IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { + scbMap = new HashMap<>(); + scbMap.put("string", new User()); + rootSerializer.serialize(scbMap); + }); + Assertions.assertEquals("not support serialize from org.apache.servicecomb.foundation.protobuf.internal.model.User to proto string, field=org.apache.servicecomb.foundation.protobuf.internal.model.Root:string", + exception.getMessage()); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestUInt32Schema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestUInt32Schema.java new file mode 100644 index 00000000000..5456ff0985c --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestUInt32Schema.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; + +public class TestUInt32Schema extends TestNumberBaseSchema { + public TestUInt32Schema() { + minValue = Integer.MIN_VALUE; + maxValue = Integer.MAX_VALUE; + initFields("uint32", "objUint32"); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestUInt64Schema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestUInt64Schema.java new file mode 100644 index 00000000000..6235a4b3e24 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestUInt64Schema.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.internal.schema.scalar; + +public class TestUInt64Schema extends TestNumberBaseSchema { + public TestUInt64Schema() { + minValue = Long.MIN_VALUE; + maxValue = Long.MAX_VALUE; + initFields("uint64", "objUint64"); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/ProtubufCodecEngine.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/ProtubufCodecEngine.java new file mode 100644 index 00000000000..783d4aad7d0 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/ProtubufCodecEngine.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.performance; + +import java.io.IOException; + +public interface ProtubufCodecEngine { + byte[] serialize(Object model) throws IOException; + + Object deserialize(byte[] bytes) throws IOException; +} 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 new file mode 100644 index 00000000000..c73caf9a327 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestBase.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.performance; + +import java.io.IOException; +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; +import org.apache.servicecomb.foundation.protobuf.internal.model.Root; +import org.apache.servicecomb.foundation.protobuf.performance.engine.Jackson; +import org.apache.servicecomb.foundation.protobuf.performance.engine.Protobuf; +import org.apache.servicecomb.foundation.protobuf.performance.engine.Protostuff; +import org.apache.servicecomb.foundation.protobuf.performance.engine.ScbStrong; +import org.apache.servicecomb.foundation.protobuf.performance.engine.ScbWeak; + +public abstract class TestBase { + public interface Case { + TestEngineResult run(); + } + + static ProtubufCodecEngine scbStrong = new ScbStrong(); + + static ProtubufCodecEngine scbWeak = new ScbWeak(); + + static ProtubufCodecEngine protoStuff = new Protostuff(); + + static ProtubufCodecEngine protobuf = new Protobuf(); + + static ProtubufCodecEngine jackson = new Jackson(); + + protected Root pojoRoot = new Root(); + + protected Map pojoRootMap; + + protected ProtobufRoot.Root.Builder builder = ProtobufRoot.Root.newBuilder(); + + @SuppressWarnings("unchecked") + public TestResult run(int count) throws IOException { + pojoRootMap = (Map) Jackson.jsonMapper.convertValue(pojoRoot, Map.class); + // warm + doRun(10_000); + + // real test + return doRun(count); + } + + private TestResult doRun(int count) throws IOException { + TestResult testResult = new TestResult(); + testResult.name = this.getClass().getSimpleName(); + + testResult.addTestEngineResult(runOneEngine(protoStuff, pojoRoot, count)); + testResult.addTestEngineResult(runOneEngine(scbStrong, pojoRoot, count)); + testResult.addTestEngineResult(runOneEngine(scbWeak, pojoRootMap, count)); + testResult.addTestEngineResult(runOneEngine(protobuf, builder, count)); + testResult.addTestEngineResult(runOneEngine(jackson, pojoRoot, count)); + return testResult; + } + + private TestEngineResult runOneEngine(ProtubufCodecEngine engine, Object model, int count) + throws IOException { + TestEngineResult engineResult = new TestEngineResult(); + engineResult.engineName = engine.getClass().getSimpleName(); + + // serialize + { + long msStart = System.currentTimeMillis(); + for (int idx = 0; idx < count; idx++) { + engineResult.serializeBytes = engine.serialize(model); + } + long msEnd = System.currentTimeMillis(); + + engineResult.msSerializeTime = msEnd - msStart; + } + + // deserialize + { + long msStart = System.currentTimeMillis(); + for (int idx = 0; idx < count; idx++) { + engineResult.deserializeResult = engine.deserialize(engineResult.serializeBytes); + } + long msEnd = System.currentTimeMillis(); + + engineResult.msDeserializeTime = msEnd - msStart; + } + + 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 new file mode 100644 index 00000000000..f02ee12092a --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestEngineResult.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.foundation.protobuf.performance; + +public class TestEngineResult { + public String engineName; + + // serialize + public long msSerializeTime; + + public byte[] serializeBytes; + + // deserialize + public long msDeserializeTime; + + public Object deserializeResult; + + 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 new file mode 100644 index 00000000000..98e2f5b7855 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestProtoPerformance.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.protobuf.performance; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.protobuf.performance.cases.Empty; +import org.apache.servicecomb.foundation.protobuf.performance.cases.Map; +import org.apache.servicecomb.foundation.protobuf.performance.cases.Mixed; +import org.apache.servicecomb.foundation.protobuf.performance.cases.Pojo; +import org.apache.servicecomb.foundation.protobuf.performance.cases.PojoList; +import org.apache.servicecomb.foundation.protobuf.performance.cases.Scalars; +import org.apache.servicecomb.foundation.protobuf.performance.cases.SimpleList; + +import com.google.common.base.Strings; + +public class TestProtoPerformance { + public static void main(String[] args) throws IOException { + System.out.println("1.protobuf\n" + + " in our real scenes\n" + + " business model never bind to transport, and can switch between different transports dynamically\n" + + " that means if we choose standard protobuf, must build protobuf models from business models each time\n" + + " so should be much slower than the test results"); + System.out.println("2.protoStuff\n" + + " some scenes, there is no field but have getter or setter, so we can not use unsafe to access field\n" + + " so we disable protoStuff unsafe feature\n\n" + + " for repeated fields, protoStuff have better performance, but not compatible to protobuf\n"); + System.out.println("3.jackson\n" + + " not support map/any/recursive, ignore related fields"); + System.out.println("4.serialize result size\n" + + " ScbStrong/ScbWeak/Protobuf have the same and smaller size, because skip all default/null value"); + + System.setProperty("protostuff.runtime.use_sun_misc_unsafe", "false"); + + int count = 50_0000; + + printResult(new Empty().run(count)); + printResult(new Scalars().run(count)); + printResult(new Pojo().run(count)); + printResult(new SimpleList().run(count)); + printResult(new PojoList().run(count)); + printResult(new Map().run(count)); + printResult(new Mixed().run(count)); + } + + private static void printResult(TestResult result) { + String strFmt = Strings.repeat("%-11s", result.engineResults.size()); + String numberFmt = Strings.repeat("%-11d", result.engineResults.size()); + + System.out.println(result.name + ": "); + System.out.printf(" " + strFmt + "\n", + result.engineResults.stream().map(r -> r.engineName).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("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("serialize+deserialize(ms) : " + numberFmt + "\n\n", + result.engineResults.stream().map(r -> r.msSerializeTime + r.msDeserializeTime).toArray()); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestResult.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestResult.java new file mode 100644 index 00000000000..1c24182154d --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestResult.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.foundation.protobuf.performance; + +import java.util.ArrayList; +import java.util.List; + +public class TestResult { + public String name; + + public List engineResults = new ArrayList<>(); + + public void addTestEngineResult(TestEngineResult engineResult) { + engineResults.add(engineResult); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Empty.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Empty.java new file mode 100644 index 00000000000..d7846b95d5d --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Empty.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.foundation.protobuf.performance.cases; + +import org.apache.servicecomb.foundation.protobuf.performance.TestBase; + +public class Empty extends TestBase { +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Map.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Map.java new file mode 100644 index 00000000000..6ee1c2f89f0 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Map.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.foundation.protobuf.performance.cases; + +import java.util.HashMap; + +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.User; +import org.apache.servicecomb.foundation.protobuf.performance.TestBase; + +public class Map extends TestBase { + public Map() { + pojoRoot.setSsMap(new HashMap<>()); + pojoRoot.getSsMap().put("k1", "v1"); + pojoRoot.getSsMap().put("k2", "v2"); + pojoRoot.setSpMap(new HashMap<>()); + pojoRoot.getSpMap().put("u1", new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name1")); + pojoRoot.getSpMap().put("u2", new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name2")); + + builder.putSsMap("k1", "v1") + .putSsMap("k2", "v2") + .putSpMap("u1", User.newBuilder().setName("name1").build()) + .putSpMap("u2", User.newBuilder().setName("name2").build()); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Mixed.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Mixed.java new file mode 100644 index 00000000000..9cc09534b80 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Mixed.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.protobuf.performance.cases; + +import java.util.Arrays; +import java.util.LinkedHashMap; + +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.User; +import org.apache.servicecomb.foundation.protobuf.performance.TestBase; +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; + +public class Mixed extends TestBase { + public Mixed() { + pojoRoot.setInt32(10000); + pojoRoot.setInt64(20000L); + pojoRoot.setUint32(30000); + pojoRoot.setUint64(40000L); + pojoRoot.setSint32(50000); + pojoRoot.setSint64(60000L); + pojoRoot.setFixed32(70000); + pojoRoot.setFixed64(80000L); + pojoRoot.setSfixed32(90000); + pojoRoot.setSfixed64(100000L); + pojoRoot.setFloatValue((float) 10000); + pojoRoot.setDoubleValue(20000.0); + pojoRoot.setBool(true); + + pojoRoot.setObjInt32(10000); + pojoRoot.setObjInt64(20000L); + pojoRoot.setObjUint32(30000); + pojoRoot.setObjUint64(40000L); + pojoRoot.setObjSint32(50000); + pojoRoot.setObjSint64(60000L); + pojoRoot.setObjFixed32(70000); + pojoRoot.setObjFixed64(80000L); + pojoRoot.setObjSfixed32(90000); + pojoRoot.setObjSfixed64(100000L); + pojoRoot.setObjFloatValue((float) 10000); + pojoRoot.setObjDoubleValue(20000.0); + pojoRoot.setObjBool(true); + + pojoRoot.setString("string value"); + pojoRoot.setColor(Color.BLUE); + pojoRoot.setUser(new org.apache.servicecomb.foundation.protobuf.internal.model.User("name1")); + + pojoRoot.setSsMap(new LinkedHashMap<>()); + pojoRoot.getSsMap().put("k1", "v1"); + pojoRoot.getSsMap().put("k2", "v2"); + pojoRoot.setSpMap(new LinkedHashMap<>()); + pojoRoot.getSpMap().put("u1", new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name1")); + pojoRoot.getSpMap().put("u2", new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name2")); + + pojoRoot.setInt32sPacked(Arrays.asList(10000, 20000, 30000)); + pojoRoot.setInt64sPacked(Arrays.asList(10000L, 20000L, 30000L)); + pojoRoot.setUint32sPacked(Arrays.asList(10000, 20000, 30000)); + pojoRoot.setUint64sPacked(Arrays.asList(10000L, 20000L, 30000L)); + pojoRoot.setSint32sPacked(Arrays.asList(10000, 20000, 30000)); + pojoRoot.setSint64sPacked(Arrays.asList(10000L, 20000L, 30000L)); + pojoRoot.setFixed32sPacked(Arrays.asList(10000, 20000, 30000)); + pojoRoot.setFixed64sPacked(Arrays.asList(10000L, 20000L, 30000L)); + pojoRoot.setSfixed32sPacked(Arrays.asList(10000, 20000, 30000)); + pojoRoot.setSfixed64sPacked(Arrays.asList(10000L, 20000L, 30000L)); + pojoRoot.setFloatsPacked(Arrays.asList((float) 10000, (float) 20000, (float) 30000)); + pojoRoot.setDoublesPacked(Arrays.asList(10000.0, 20000.0, 30000.0)); + pojoRoot.setBoolsPacked(Arrays.asList(true, false)); + pojoRoot.setColorsPacked(Arrays.asList(Color.RED, Color.BLUE)); + + pojoRoot.setStrings(Arrays.asList("string value1", "string value2")); + pojoRoot.setUsers(Arrays.asList( + new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name1"), + new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name2"), + new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name3"), + new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name4"))); + + builder.setInt32(10000) + .setInt64(20000L) + .setUint32(30000) + .setUint64(40000L) + .setSint32(50000) + .setSint64(60000L) + .setFixed32(70000) + .setFixed64(80000L) + .setSfixed32(90000) + .setSfixed64(100000L) + .setFloatValue((float) 10000) + .setDoubleValue(20000.0) + .setBool(true) + + .setObjInt32(10000) + .setObjInt64(20000L) + .setObjUint32(30000) + .setObjUint64(40000L) + .setObjSint32(50000) + .setObjSint64(60000L) + .setObjFixed32(70000) + .setObjFixed64(80000L) + .setObjSfixed32(90000) + .setObjSfixed64(100000L) + .setObjFloatValue((float) 10000) + .setObjDoubleValue(20000.0) + .setObjBool(true) + + .setString("string value") + .setColorValue(2) + .setUser(User.newBuilder().setName("name1").build()) + + .putSsMap("k1", "v1") + .putSsMap("k2", "v2") + .putSpMap("u1", User.newBuilder().setName("name1").build()) + .putSpMap("u2", User.newBuilder().setName("name2").build()) + + .addAllInt32SPacked(Arrays.asList(10000, 20000, 30000)) + .addAllInt64SPacked(Arrays.asList(10000L, 20000L, 30000L)) + .addAllUint32SPacked(Arrays.asList(10000, 20000, 30000)) + .addAllUint64SPacked(Arrays.asList(10000L, 20000L, 30000L)) + .addAllSint32SPacked(Arrays.asList(10000, 20000, 30000)) + .addAllSint64SPacked(Arrays.asList(10000L, 20000L, 30000L)) + .addAllFixed32SPacked(Arrays.asList(10000, 20000, 30000)) + .addAllFixed64SPacked(Arrays.asList(10000L, 20000L, 30000L)) + .addAllSfixed32SPacked(Arrays.asList(10000, 20000, 30000)) + .addAllSfixed64SPacked(Arrays.asList(10000L, 20000L, 30000L)) + .addAllFloatsPacked(Arrays.asList((float) 10000, (float) 20000, (float) 30000)) + .addAllDoublesPacked(Arrays.asList(10000.0, 20000.0, 30000.0)) + .addAllBoolsPacked(Arrays.asList(true, false)) + .addAllColorsPacked(Arrays.asList(ProtobufRoot.Color.RED, ProtobufRoot.Color.BLUE)) + + .addStrings("string value1") + .addStrings("string value2") + .addUsers(User.newBuilder().setName("name1").build()) + .addUsers(User.newBuilder().setName("name2").build()) + .addUsers(User.newBuilder().setName("name3").build()) + .addUsers(User.newBuilder().setName("name4").build()); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Pojo.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Pojo.java new file mode 100644 index 00000000000..d435ed392a7 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Pojo.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.foundation.protobuf.performance.cases; + +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.User; +import org.apache.servicecomb.foundation.protobuf.performance.TestBase; + +public class Pojo extends TestBase { + public Pojo() { + pojoRoot.setUser(new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name1")); + + builder.setUser(User.newBuilder().setName("name1").build()); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/PojoList.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/PojoList.java new file mode 100644 index 00000000000..929d62666e9 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/PojoList.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.foundation.protobuf.performance.cases; + +import java.util.Arrays; + +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.User; +import org.apache.servicecomb.foundation.protobuf.performance.TestBase; + +public class PojoList extends TestBase { + public PojoList() { + pojoRoot.setUsers(Arrays.asList( + new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name1"), + new org.apache.servicecomb.foundation.protobuf.internal.model.User().name("name2"))); + + builder.addUsers(User.newBuilder().setName("name1").build()) + .addUsers(User.newBuilder().setName("name2").build()); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Scalars.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Scalars.java new file mode 100644 index 00000000000..bf272164523 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/Scalars.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.foundation.protobuf.performance.cases; + +import org.apache.servicecomb.foundation.protobuf.performance.TestBase; +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; + +public class Scalars extends TestBase { + public Scalars() { + pojoRoot.setInt32(10000); + pojoRoot.setInt64(10000L); + pojoRoot.setString("string value"); + pojoRoot.setColor(Color.BLUE); + + builder.setInt32(10000) + .setInt64(10000L) + .setString("string value") + .setColorValue(2); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/SimpleList.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/SimpleList.java new file mode 100644 index 00000000000..2e30d28bc7e --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/cases/SimpleList.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.foundation.protobuf.performance.cases; + +import java.util.Arrays; + +import org.apache.servicecomb.foundation.protobuf.performance.TestBase; + +public class SimpleList extends TestBase { + public SimpleList() { + pojoRoot.setStrings(Arrays.asList("string value1", "string value2")); + + builder.addStrings("string value1") + .addStrings("string value2"); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/Jackson.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/Jackson.java new file mode 100644 index 00000000000..10d11d699bd --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/Jackson.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.foundation.protobuf.performance.engine; + +import java.io.IOException; +import java.net.URL; + +import org.apache.servicecomb.foundation.protobuf.internal.model.Root; +import org.apache.servicecomb.foundation.protobuf.internal.model.User; +import org.apache.servicecomb.foundation.protobuf.performance.ProtubufCodecEngine; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.dataformat.protobuf.ProtobufMapper; +import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; +import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchemaLoader; + +public class Jackson implements ProtubufCodecEngine { + @JsonIgnoreProperties({"ssMap", "spMap", "sint32Map", "any", "anys", "typeRecursive"}) + interface RootMixin { + } + + @JsonIgnoreProperties({"typeRecursive"}) + interface UserMixin { + } + + public static ObjectMapper jsonMapper = new ObjectMapper(); + + static ProtobufMapper protobufMapper = new ProtobufMapper(); + + static URL url = Jackson.class.getClassLoader().getResource("jacksonRoot.proto"); + + static ProtobufSchema protobufSchema; + + static { + protobufMapper.addMixIn(Root.class, RootMixin.class); + protobufMapper.addMixIn(User.class, UserMixin.class); + try { + protobufSchema = ProtobufSchemaLoader.std.load(url); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static ObjectWriter writer = protobufMapper.writer(protobufSchema); + + static ObjectReader reader = protobufMapper.reader(protobufSchema).forType(Root.class); + + @Override + public byte[] serialize(Object model) throws IOException { + return writer.writeValueAsBytes(model); + } + + @Override + public Object deserialize(byte[] bytes) throws IOException { + return reader.readValue(bytes); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/Protobuf.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/Protobuf.java new file mode 100644 index 00000000000..fe1be8cd640 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/Protobuf.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.foundation.protobuf.performance.engine; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot; +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.Root; +import org.apache.servicecomb.foundation.protobuf.internal.model.ProtobufRoot.Root.Builder; +import org.apache.servicecomb.foundation.protobuf.performance.ProtubufCodecEngine; + +public class Protobuf implements ProtubufCodecEngine { + @Override + public byte[] serialize(Object builder) { + return ((Builder) builder).build().toByteArray(); + } + + @Override + public Object deserialize(byte[] bytes) throws IOException { + Root.Builder builder = ProtobufRoot.Root.newBuilder().mergeFrom(bytes); + builder.build(); + return builder; + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/Protostuff.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/Protostuff.java new file mode 100644 index 00000000000..c58f85d2a85 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/Protostuff.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.foundation.protobuf.performance.engine; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.protobuf.internal.model.Root; +import org.apache.servicecomb.foundation.protobuf.performance.ProtubufCodecEngine; + +import io.protostuff.ByteArrayInput; +import io.protostuff.Input; +import io.protostuff.LinkedBuffer; +import io.protostuff.ProtobufOutput; +import io.protostuff.Schema; +import io.protostuff.runtime.RuntimeSchema; + +public class Protostuff implements ProtubufCodecEngine { + static Schema rootSchema = RuntimeSchema.createFrom(Root.class); + + @Override + public byte[] serialize(Object model) throws IOException { + LinkedBuffer linkedBuffer = LinkedBuffer.allocate(); + ProtobufOutput output = new ProtobufOutput(linkedBuffer); + if (model != null) { + rootSchema.writeTo(output, (Root) model); + } + return output.toByteArray(); + } + + @Override + public Object deserialize(byte[] bytes) throws IOException { + Input input = new ByteArrayInput(bytes, false); + Root result = new Root(); + rootSchema.mergeFrom(input, result); + return result; + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/SCB.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/SCB.java new file mode 100644 index 00000000000..dba5dd9bcde --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/SCB.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.foundation.protobuf.performance.engine; + +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.RootSerializer; +import org.apache.servicecomb.foundation.protobuf.internal.model.Root; + +public class SCB { + static ProtoMapperFactory factory = new ProtoMapperFactory(); + + static ProtoMapper protoMapper = factory.createFromName("protobufRoot.proto"); + + static RootSerializer serializer = protoMapper.createRootSerializer("Root", Root.class); + + static RootDeserializer deserializer = protoMapper.createRootDeserializer("Root", Root.class); + + static RootDeserializer> mapDeserializer = protoMapper.createRootDeserializer("Root", Map.class); +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/ScbStrong.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/ScbStrong.java new file mode 100644 index 00000000000..6f021b0595a --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/ScbStrong.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.foundation.protobuf.performance.engine; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.protobuf.performance.ProtubufCodecEngine; + +public class ScbStrong implements ProtubufCodecEngine { + @Override + public byte[] serialize(Object model) throws IOException { + return SCB.serializer.serialize(model); + } + + @Override + public Object deserialize(byte[] bytes) throws IOException { + return SCB.deserializer.deserialize(bytes); + } +} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/ScbWeak.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/ScbWeak.java new file mode 100644 index 00000000000..285bedc00a9 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/engine/ScbWeak.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.foundation.protobuf.performance.engine; + +import java.io.IOException; + +import org.apache.servicecomb.foundation.protobuf.performance.ProtubufCodecEngine; + +public class ScbWeak implements ProtubufCodecEngine { + @Override + public byte[] serialize(Object model) throws IOException { + return SCB.serializer.serialize(model); + } + + @Override + public Object deserialize(byte[] bytes) throws IOException { + return SCB.mapDeserializer.deserialize(bytes); + } +} diff --git a/foundations/foundation-protobuf/src/test/resources/jacksonRoot.proto b/foundations/foundation-protobuf/src/test/resources/jacksonRoot.proto new file mode 100644 index 00000000000..b1804922666 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/resources/jacksonRoot.proto @@ -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. + */ + +syntax = "proto2"; +package org.apache.servicecomb.foundation.protobuf.internal.model; + +message Root { + optional int32 int32 = 1; + optional int64 int64 = 2; + optional uint32 uint32 = 3; + optional uint64 uint64 = 4; + optional sint32 sint32 = 5; + optional sint64 sint64 = 6; + optional fixed32 fixed32 = 7; + optional fixed64 fixed64 = 8; + optional sfixed32 sfixed32 = 9; + optional sfixed64 sfixed64 = 10; + optional float floatValue = 11; + optional double doubleValue = 12; + optional bool bool = 13; + + optional int32 objInt32 = 20; + optional int64 objInt64 = 21; + optional uint32 objUint32 = 22; + optional uint64 objUint64 = 23; + optional sint32 objSint32 = 24; + optional sint64 objSint64 = 25; + optional fixed32 objFixed32 = 26; + optional fixed64 objFixed64 = 27; + optional sfixed32 objSfixed32 = 28; + optional sfixed64 objSfixed64 = 29; + optional float objFloatValue = 30; + optional double objDoubleValue = 31; + optional bool objBool = 32; + + optional string string = 40; + optional bytes bytes = 41; + optional Color color = 42; + optional User user = 43; + + repeated int32 int32sPacked = 70; + repeated int64 int64sPacked = 71; + repeated uint32 uint32sPacked = 72; + repeated uint64 uint64sPacked = 73; + repeated sint32 sint32sPacked = 74; + repeated sint64 sint64sPacked = 75; + repeated fixed32 fixed32sPacked = 76; + repeated fixed64 fixed64sPacked = 77; + repeated sfixed32 sfixed32sPacked = 78; + repeated sfixed64 sfixed64sPacked = 79; + repeated float floatsPacked = 80; + repeated double doublesPacked = 81; + repeated bool boolsPacked = 82; + repeated Color colorsPacked = 83; + + repeated int32 int32sNotPacked = 90 [packed = false]; + repeated int64 int64sNotPacked = 91 [packed = false]; + repeated uint32 uint32sNotPacked = 92 [packed = false]; + repeated uint64 uint64sNotPacked = 93 [packed = false]; + repeated sint32 sint32sNotPacked = 94 [packed = false]; + repeated sint64 sint64sNotPacked = 95 [packed = false]; + repeated fixed32 fixed32sNotPacked = 96 [packed = false]; + repeated fixed64 fixed64sNotPacked = 97 [packed = false]; + repeated sfixed32 sfixed32sNotPacked = 98 [packed = false]; + repeated sfixed64 sfixed64sNotPacked = 99 [packed = false]; + repeated float floatsNotPacked = 100 [packed = false]; + repeated double doublesNotPacked = 101 [packed = false]; + repeated bool boolsNotPacked = 102 [packed = false]; + repeated Color colorsNotPacked = 103 [packed = false]; + + repeated string strings = 110; + repeated bytes bytess = 111; + repeated User users = 112; +} + +enum Color { + RED = 0; + YELLOW = 1; + BLUE = 2; +} + +message User { + optional string name = 1; +} diff --git a/foundations/foundation-protobuf/src/test/resources/method.proto b/foundations/foundation-protobuf/src/test/resources/method.proto new file mode 100644 index 00000000000..414b7643880 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/resources/method.proto @@ -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. + */ + +syntax = "proto3"; + +//@WrapArguments +message RequestWrap { + bool boolValue = 1; + repeated int32 iArr = 2; + repeated User user = 3; +} + +message Request { + bool boolValue = 1; + repeated int32 iArr = 2; + repeated User user = 3; +} + +//@WrapProperty +message ResponseWrap { + repeated int32 value = 1; +} + +message Response { + repeated int32 value = 1; +} + +message User { + string name = 1; + repeated User friends = 2; +} diff --git a/foundations/foundation-protobuf/src/test/resources/model.proto b/foundations/foundation-protobuf/src/test/resources/model.proto new file mode 100644 index 00000000000..c72ec7dd861 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/resources/model.proto @@ -0,0 +1,108 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +syntax = "proto3"; + +message PojoModel { + repeated PojoListUser listListUser = 1; + repeated PojoMapUser listMapUser = 2; + map mapListUser = 3; + map mapMapUser = 4; + + repeated PojoListListUser listListListUser = 5; + repeated PojoListMapUser listListMapUser = 6; + repeated PojoMapListUser listMapListUser = 7; + repeated PojoMapMapUser listMapMapUser = 8; + map mapListListUser = 9; + map mapListMapUser = 10; + map mapMapListUser = 11; + map mapMapMapUser = 12; +} + +message ProtoModel { + repeated ProtoListUser listListUser = 1; + repeated ProtoMapUser listMapUser = 2; + map mapListUser = 3; + map mapMapUser = 4; + + repeated ProtoListListUser listListListUser = 5; + repeated ProtoListMapUser listListMapUser = 6; + repeated ProtoMapListUser listMapListUser = 7; + repeated ProtoMapMapUser listMapMapUser = 8; + map mapListListUser = 9; + map mapListMapUser = 10; + map mapMapListUser = 11; + map mapMapMapUser = 12; +} + +//@WrapProperty +message PojoListListUser { + repeated PojoListUser value = 1; +} + +//@WrapProperty +message PojoListMapUser { + repeated PojoMapUser value = 1; +} + +//@WrapProperty +message PojoMapListUser { + map value = 1; +} + +//@WrapProperty +message PojoMapMapUser { + map value = 1; +} + +//@WrapProperty +message PojoListUser { + repeated User value = 1; +} + +//@WrapProperty +message PojoMapUser { + map value = 1; +} + +message User { + string name = 1; +} + +message ProtoListListUser { + repeated ProtoListUser value = 1; +} + +message ProtoListMapUser { + repeated ProtoMapUser value = 1; +} + +message ProtoMapListUser { + map value = 1; +} + +message ProtoMapMapUser { + map value = 1; +} + +message ProtoListUser { + repeated User value = 1; +} + +message ProtoMapUser { + map value = 1; +} diff --git a/foundations/foundation-protobuf/src/test/resources/protobufRoot.proto b/foundations/foundation-protobuf/src/test/resources/protobufRoot.proto new file mode 100644 index 00000000000..1e9f7f41076 --- /dev/null +++ b/foundations/foundation-protobuf/src/test/resources/protobufRoot.proto @@ -0,0 +1,112 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +syntax = "proto3"; +import "google/protobuf/any.proto"; +package org.apache.servicecomb.foundation.protobuf.internal.model; + +option java_package = "org.apache.servicecomb.foundation.protobuf.internal.model"; +option java_outer_classname = "ProtobufRoot"; + +message Root { + int32 int32 = 1; + int64 int64 = 2; + uint32 uint32 = 3; + uint64 uint64 = 4; + sint32 sint32 = 5; + sint64 sint64 = 6; + fixed32 fixed32 = 7; + fixed64 fixed64 = 8; + sfixed32 sfixed32 = 9; + sfixed64 sfixed64 = 10; + float floatValue = 11; + double doubleValue = 12; + bool bool = 13; + + int32 objInt32 = 20; + int64 objInt64 = 21; + uint32 objUint32 = 22; + uint64 objUint64 = 23; + sint32 objSint32 = 24; + sint64 objSint64 = 25; + fixed32 objFixed32 = 26; + fixed64 objFixed64 = 27; + sfixed32 objSfixed32 = 28; + sfixed64 objSfixed64 = 29; + float objFloatValue = 30; + double objDoubleValue = 31; + bool objBool = 32; + + string string = 40; + bytes bytes = 41; + Color color = 42; + User user = 43; + Root typeRecursive = 44; + + google.protobuf.Any any = 50; + repeated google.protobuf.Any anys = 51; + + map ssMap = 60; + map sint32Map = 61; + map spMap = 62; + + repeated int32 int32sPacked = 70; + repeated int64 int64sPacked = 71; + repeated uint32 uint32sPacked = 72; + repeated uint64 uint64sPacked = 73; + repeated sint32 sint32sPacked = 74; + repeated sint64 sint64sPacked = 75; + repeated fixed32 fixed32sPacked = 76; + repeated fixed64 fixed64sPacked = 77; + repeated sfixed32 sfixed32sPacked = 78; + repeated sfixed64 sfixed64sPacked = 79; + repeated float floatsPacked = 80; + repeated double doublesPacked = 81; + repeated bool boolsPacked = 82; + repeated Color colorsPacked = 83; + + repeated int32 int32sNotPacked = 90 [packed = false]; + repeated int64 int64sNotPacked = 91 [packed = false]; + repeated uint32 uint32sNotPacked = 92 [packed = false]; + repeated uint64 uint64sNotPacked = 93 [packed = false]; + repeated sint32 sint32sNotPacked = 94 [packed = false]; + repeated sint64 sint64sNotPacked = 95 [packed = false]; + repeated fixed32 fixed32sNotPacked = 96 [packed = false]; + repeated fixed64 fixed64sNotPacked = 97 [packed = false]; + repeated sfixed32 sfixed32sNotPacked = 98 [packed = false]; + repeated sfixed64 sfixed64sNotPacked = 99 [packed = false]; + repeated float floatsNotPacked = 100 [packed = false]; + repeated double doublesNotPacked = 101 [packed = false]; + repeated bool boolsNotPacked = 102 [packed = false]; + repeated Color colorsNotPacked = 103 [packed = false]; + + repeated string strings = 110; + repeated bytes bytess = 111; + repeated User users = 112; +} + +enum Color { + RED = 0; + YELLOW = 1; + BLUE = 2; +} + +message User { + string name = 1; + + Root typeRecursive = 2; +} diff --git a/foundations/foundation-registry/pom.xml b/foundations/foundation-registry/pom.xml new file mode 100644 index 00000000000..d4cca8111c4 --- /dev/null +++ b/foundations/foundation-registry/pom.xml @@ -0,0 +1,56 @@ + + + + + + foundations + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + + + + foundation-registry + Java Chassis::Foundations::Registry + + + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-vertx + + + org.apache.servicecomb + swagger-invocation-core + + + org.apache.servicecomb + foundation-test-scaffolding + + + org.mockito + mockito-inline + test + + + 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 new file mode 100644 index 00000000000..edcd3b3caef --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/DiscoveryManager.java @@ -0,0 +1,337 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ArrayList; +import java.util.HashMap; +import java.util.List; +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.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.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; + +public class DiscoveryManager implements LifeCycle { + public interface InstanceChangeListener { + void onInstancesChanged(String registryName, String application, String serviceName, + List instances); + } + + private static final Logger LOGGER = LoggerFactory.getLogger(DiscoveryManager.class); + + private final ScheduledExecutorService task; + + private final List> discoveryList; + + private final InstancePing ping; + + private final HealthCheckTask healthCheckTask = new HealthCheckTask(); + + // application:serviceName:instanceId + private final Map>> + allInstances = new ConcurrentHashMapEx<>(); + + // application:serviceName + private final Map> + versionedCache = new ConcurrentHashMapEx<>(); + + private final Object cacheLock = new Object(); + + private final List instanceChangeListeners = new ArrayList<>(); + + 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 Discovery getPrimaryDiscovery() { + return this.discoveryList.get(0); + } + + private void onInstancesChanged(String application, String serviceName, + List instances) { + onInstancesChanged(null, application, serviceName, instances); + } + + 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); + } + } + } + + 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); + } + } + + 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; + } + + 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()); + } + + 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); + } + } + 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 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 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(LambdaUtils.ignoreException(LifeCycle::destroy)); + task.shutdownNow(); + } + + @Override + public void run() { + discoveryList.forEach(LifeCycle::run); + task.schedule(healthCheckTask, 3, TimeUnit.SECONDS); + } + + @Override + public void init() { + 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 new file mode 100644 index 00000000000..9c135d380c8 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationManager.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Optional; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +import org.apache.servicecomb.foundation.common.utils.LambdaUtils; +import org.apache.servicecomb.foundation.common.utils.SPIEnabled; +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.RegistrationInstance; +import org.springframework.util.CollectionUtils; + +import io.vertx.core.json.jackson.JacksonFactory; + +public class RegistrationManager { + private final List> registrationList; + + public RegistrationManager(List> registrationList) { + if (registrationList == null) { + this.registrationList = Collections.emptyList(); + return; + } + this.registrationList = registrationList.stream().filter(SPIEnabled::enabled).collect(Collectors.toList()); + } + + /** + * 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(); + } + + /** + * 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 Registration getPrimaryRegistration() { + return registrationList.get(0); + } + + public void updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { + registrationList + .forEach(registration -> registration.updateMicroserviceInstanceStatus(status)); + } + + public void addProperty(String key, String value) { + registrationList + .forEach(registration -> registration.addProperty(key, value)); + } + + public void addSchema(String schemaId, String content) { + registrationList + .forEach(registration -> registration.addSchema(schemaId, content)); + } + + public void addEndpoint(String endpoint) { + registrationList + .forEach(registration -> registration.addEndpoint(endpoint)); + } + + public void destroy() { + registrationList.forEach(LambdaUtils.ignoreException(LifeCycle::destroy)); + } + + public void run() { + registrationList.forEach(LifeCycle::run); + } + + public void init() { + registrationList.forEach(LifeCycle::init); + } + + public String info() { + StringBuilder result = new StringBuilder(); + AtomicBoolean first = new AtomicBoolean(true); + registrationList.forEach(registration -> { + if (first.getAndSet(false)) { + 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:").append(registration.name()).append("\n"); + result.append(" Instance ID: ").append(registration.getMicroserviceInstance().getInstanceId()).append("\n"); + }); + return result.toString(); + } + + private String getEndpoints(List endpoints) { + return JacksonFactory.CODEC.toString(endpoints); + } +} 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 new file mode 100644 index 00000000000..7a1d72460cc --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistryConfiguration.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; + +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 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/DataCenterInfo.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/DataCenterInfo.java new file mode 100644 index 00000000000..7c6496e4b91 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/DataCenterInfo.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.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; + } + + 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-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 new file mode 100644 index 00000000000..0c57ed6b605 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Discovery.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.api; + +import java.util.List; + +import org.apache.servicecomb.foundation.common.utils.SPIEnabled; +import org.apache.servicecomb.foundation.common.utils.SPIOrder; + +/** + * This is the core service discovery interface.
+ */ +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(); + + /** + * If this implementation enabled for this microservice. + */ + boolean enabled(String application, String serviceName); + + /** + * 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. + */ + List findServiceInstances(String application, String serviceName); + + /** + * Find all services of this application. + * + * @param application application + * @return all services match the criteria. + */ + List findServices(String application); + + /** + * Discovery can call InstanceChangedListener when instance get changed. + */ + 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/LifeCycle.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/LifeCycle.java new file mode 100644 index 00000000000..64647d9a29b --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/LifeCycle.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.registry.api; + +public interface LifeCycle { + void init(); + + void run(); + + void destroy(); +} 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/MicroserviceInstanceStatus.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstanceStatus.java new file mode 100644 index 00000000000..4bde42dee91 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstanceStatus.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.registry.api; + +public enum MicroserviceInstanceStatus { + STARTING, + TESTING, + UP, + DOWN, + UNKNOWN +} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceKey.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceKey.java new file mode 100644 index 00000000000..4f56d723354 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceKey.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * Created by on 2017/1/9. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class MicroserviceKey { + private String tenant; + + private String appId; + + private String serviceName; + + private String version; + + private String stage; + + public String getTenant() { + return tenant; + } + + public void setTenant(String tenant) { + this.tenant = tenant; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getStage() { + return stage; + } + + public void setStage(String stage) { + this.stage = stage; + } +} diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/PropertyExtended.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/PropertyExtended.java new file mode 100644 index 00000000000..e0d3cb8e50c --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/PropertyExtended.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.registry.api; + +import java.util.Map; + +public interface PropertyExtended { + Map getExtendedProperties(); +} 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 new file mode 100644 index 00000000000..014da5f147b --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Registration.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 org.apache.servicecomb.foundation.common.utils.SPIEnabled; +import org.apache.servicecomb.foundation.common.utils.SPIOrder; + + +/** + * This is the core service registration interface. + */ +public interface Registration extends SPIEnabled, SPIOrder, LifeCycle { + String name(); + + /** + * get MicroserviceInstance + *

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

+ * Life Cycle:This method is called anytime after run. + */ + boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status); + + /** + * 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 + *

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

+ * Life Cycle:This method is called after init and before run. + */ + 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/definition/DefinitionConst.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/definition/DefinitionConst.java new file mode 100644 index 00000000000..528caaafde9 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/definition/DefinitionConst.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.registry.definition; + +public interface DefinitionConst { + // Whether to allow cross-app calls to me + String CONFIG_ALLOW_CROSS_APP_KEY = "allowCrossApp"; + + String DEFAULT_STAGE = "prod"; + + String VERSION_RULE_LATEST = "latest"; + + String VERSION_RULE_ALL = "0.0.0.0+"; + + String APP_SERVICE_SEPARATOR = ":"; + + String URL_PREFIX = "urlPrefix"; + + String INSTANCE_PUBKEY_PRO = "publickey"; + + 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 new file mode 100644 index 00000000000..e99afdbf8be --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/definition/MicroserviceNameParser.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.registry.definition; + +/** + * Parse application and microservice from join name. + * + * 1. microserviceName + * 2. application:microserviceName + */ +public class MicroserviceNameParser { + private String appId; + + private String microserviceName; + + public MicroserviceNameParser(String defaultAppId, String microserviceName) { + parseMicroserviceName(defaultAppId, microserviceName); + } + + private void parseMicroserviceName(String defaultAppId, String microserviceName) { + int idxAt = microserviceName.indexOf(DefinitionConst.APP_SERVICE_SEPARATOR); + if (idxAt == -1) { + this.appId = defaultAppId; + this.microserviceName = microserviceName; + return; + } + + this.appId = microserviceName.substring(0, idxAt); + this.microserviceName = microserviceName.substring(idxAt + 1); + } + + public String getAppId() { + return appId; + } + + 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 new file mode 100644 index 00000000000..f274799c30e --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractDiscoveryFilter.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.registry.discovery; + +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()) { + synchronized (parent) { + if (!parent.childrenInited()) { + init(context, parent); + parent.childrenInited(true); + } + } + } + + String childName = findChildName(context, parent); + DiscoveryTreeNode node = parent.child(childName); + if (node == null) { + LOGGER.warn("discovery filter {}/{} return null.", this.getClass().getSimpleName(), childName); + return new DiscoveryTreeNode().subName(parent, "empty").data(new ArrayList<>()); + } + return node; + } + + protected abstract void init(DiscoveryContext context, DiscoveryTreeNode parent); + + protected abstract String findChildName(DiscoveryContext context, DiscoveryTreeNode parent); +} 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 new file mode 100644 index 00000000000..f4b464f0097 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractEndpointDiscoveryFilter.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.registry.discovery; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AbstractEndpointDiscoveryFilter implements DiscoveryFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractEndpointDiscoveryFilter.class); + + 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; + } + + @Override + public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { + String expectTransportName = findTransportName(context, parent); + return parent.children() + .computeIfAbsent(expectTransportName, etn -> createDiscoveryTreeNode(expectTransportName, context, parent)); + } + + protected DiscoveryTreeNode createDiscoveryTreeNode(String expectTransportName, DiscoveryContext context, + DiscoveryTreeNode parent) { + List endpoints = new ArrayList<>(); + List instances = parent.data(); + for (StatefulDiscoveryInstance instance : instances) { + for (String endpoint : instance.getEndpoints()) { + try { + 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); + if (objEndpoint == null) { + continue; + } + + endpoints.add(objEndpoint); + } catch (Exception e) { + LOGGER.warn("unrecognized address find, ignore {}.", endpoint); + } + } + } + + return new DiscoveryTreeNode() + .subName(parent, expectTransportName) + .data(endpoints); + } + + protected boolean isTransportNameMatch(String transportName, String 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, + 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/DiscoveryContext.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryContext.java new file mode 100644 index 00000000000..7340c7dbf65 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryContext.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.discovery; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +public class DiscoveryContext { + private Object inputParameters; + + private final Map contextParameters = new HashMap<>(); + + // some filter support rerun logic, eg:ZoneAware + // instances grouping to self zone, other zone, and so on + // first try self zone, after other filter(Isolation Filter), no instances are available + // then try other zone + private final Stack rerunStack = new Stack<>(); + + private DiscoveryTreeNode currentNode; + + @SuppressWarnings("unchecked") + public T getInputParameters() { + return (T) inputParameters; + } + + public void setInputParameters(Object inputParameters) { + this.inputParameters = inputParameters; + } + + @SuppressWarnings("unchecked") + public T getContextParameter(String name) { + return (T) contextParameters.get(name); + } + + public void putContextParameter(String name, Object value) { + contextParameters.put(name, value); + } + + public void setCurrentNode(DiscoveryTreeNode node) { + this.currentNode = node; + } + + public void pushRerunFilter() { + rerunStack.push(currentNode); + } + + public DiscoveryTreeNode popRerunFilter() { + if (rerunStack.isEmpty()) { + return null; + } + + return rerunStack.pop(); + } +} 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 new file mode 100644 index 00000000000..14b0cc44681 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryFilter.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.registry.discovery; + +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; + } + + /** + * grouping filter, means grouping instances to some groups + * eg: operation/ZoneAware/transport + */ + default boolean isGroupingFilter() { + return false; + } + + DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent); +} 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 new file mode 100644 index 00000000000..c7b70f606b9 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTree.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.registry.discovery; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +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.registry.DiscoveryManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + *
+ * DiscoveryTree is used to:
+ * 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:
+ * 1.normal filter: just filter input set
+ * 2.grouping filter: will split input set to groups
+ * 3.convert filter: eg: convert from instance to endpoint
+ * different types can composite in one filter
+ *
+ * features:
+ * 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
+ * 2.every filter must try to cache result, avoid calculate every time.
+ *
+ * usage:
+ * 1.declare a field: DiscoveryTree discoveryTree = new DiscoveryTree();
+ * 2.initialize:
+ *     discoveryTree.loadFromSPI(DiscoveryFilter.class);
+ *     // add filters by your requirement
+ *     discoveryTree.addFilter(new EndpointDiscoveryFilter());
+ *     discoveryTree.sort();
+ * 3.filter for a invocation:
+ *     DiscoveryContext context = new DiscoveryContext();
+ *     context.setInputParameters(invocation);
+ *     VersionedCache endpointsVersionedCache = discoveryTree.discovery(context,
+ *         invocation.getAppId(),
+ *         invocation.getMicroserviceName(),
+ *         invocation.getMicroserviceVersionRule());
+ *     if (endpointsVersionedCache.isEmpty()) {
+ *       // 404 not found logic
+ *       ......
+ *       return;
+ *     }
+ *
+ *     // result is endpoints or something else, which is depends on your filter set
+ *     List<Endpoint> endpoints = endpointsVersionedCache.data();
+ *
+ */ +public class DiscoveryTree { + private static final Logger LOGGER = LoggerFactory.getLogger(DiscoveryTree.class); + + private final Map> root = new ConcurrentHashMapEx<>(); + + private final Object lock = new Object(); + + private List filters = Collections.emptyList(); + + private final DiscoveryManager discoveryManager; + + public DiscoveryTree(DiscoveryManager discoveryManager) { + this.discoveryManager = discoveryManager; + } + + @Autowired + public void setDiscoveryFilters(List filters) { + this.filters = filters; + log(); + } + + 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()); + } + + boolean isMatch(VersionedCache existing, VersionedCache inputCache) { + return existing != null && existing.isSameVersion(inputCache); + } + + boolean isExpired(VersionedCache existing, VersionedCache inputCache) { + return existing == null || existing.isExpired(inputCache); + } + + public DiscoveryTreeNode discovery(DiscoveryContext context, String appId, String microserviceName) { + VersionedCache instanceVersionedCache = this.discoveryManager.getOrCreateVersionedCache(appId, microserviceName); + + return discovery(appId, microserviceName, context, instanceVersionedCache); + } + + 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(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(tmpRoot, inputCache)) { + // not initialized or inputCache newer than root, create new root + tmpRoot = new DiscoveryTreeNode().cacheVersion(inputCache.cacheVersion()); + root.get(appId).put(microserviceName, tmpRoot); + return tmpRoot; + } + + if (tmpRoot.isSameVersion(inputCache)) { + // reuse root directly + return tmpRoot; + } + } + + // root newer than inputCache, it's a minimal probability event: + // 1) thread 1, use v1 inputCache, run into getOrCreateRoot, but did not run any code yet, suspend and switch to thread 2 + // 2) thread 2, use v2 inputCache, v2 > v1, create new root + // 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 + 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); + if (child == null) { + // impossible, throw exception to help fix bug + throw new ServiceCombException(filter.getClass().getName() + " discovery return null."); + } + + child.level(idx + 1); + if (!filter.isGroupingFilter()) { + child.name(parent.name()); + } + + if (child.isEmpty()) { + // maybe need to rerun some filter + DiscoveryTreeNode rerunNode = context.popRerunFilter(); + if (rerunNode != null) { + parent = rerunNode; + idx = parent.level(); + continue; + } + + // no rerun support, go on even result is empty + // because maybe some filter use other mechanism to create an instance(eg:domain name) + } + + parent = child; + idx++; + } + + return parent; + } +} 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 new file mode 100644 index 00000000000..e0f7c828c10 --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTreeNode.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.registry.discovery; + +import java.util.Map; + +import org.apache.servicecomb.foundation.common.cache.VersionedCache; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; + +public class DiscoveryTreeNode extends VersionedCache { + private volatile boolean childrenInited; + + private volatile int level; + + protected Map attributes = new ConcurrentHashMapEx<>(); + + protected Map children = new ConcurrentHashMapEx<>(); + + public boolean childrenInited() { + return childrenInited; + } + + public DiscoveryTreeNode childrenInited(boolean childrenInited) { + this.childrenInited = childrenInited; + return this; + } + + public int level() { + return level; + } + + public DiscoveryTreeNode level(int level) { + this.level = level; + return this; + } + + @SuppressWarnings("unchecked") + public T attribute(String key) { + return (T) attributes.get(key); + } + + public DiscoveryTreeNode attribute(String key, Object value) { + attributes.put(key, value); + return this; + } + + public Map children() { + return children; + } + + public DiscoveryTreeNode children(Map children) { + this.children = children; + return this; + } + + public DiscoveryTreeNode child(String childName) { + return children.get(childName); + } + + public DiscoveryTreeNode child(String childName, DiscoveryTreeNode child) { + children.put(childName, child); + return this; + } + + public DiscoveryTreeNode fromCache(VersionedCache other) { + this.cacheVersion = other.cacheVersion(); + this.name = other.name(); + this.data(other.data()); + return this; + } +} 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 new file mode 100644 index 00000000000..e7a4029bdeb --- /dev/null +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstanceStatusDiscoveryFilter.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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; + +public class InstanceStatusDiscoveryFilter extends AbstractGroupDiscoveryFilter { + public static final String PARAMETER = "status_level"; + + 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() { + return -10000; + } + + @Override + public boolean enabled() { + + 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 + protected String contextParameter() { + return PARAMETER; + } + + @Override + protected String groupPrefix() { + return GROUP_PREFIX; + } + + @Override + public void init(DiscoveryContext context, DiscoveryTreeNode parent) { + 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 (!level0.isEmpty()) { + parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() + .subName(parent, GROUP_PREFIX + groups).data(level0)); + groups++; + } + 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/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 new file mode 100644 index 00000000000..56169c4507c --- /dev/null +++ b/foundations/foundation-registry/src/main/resources/META-INF/services/org.apache.servicecomb.registry.consumer.MicroserviceInstancePing @@ -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.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/discovery/TestAbstractDiscoveryFilter.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestAbstractDiscoveryFilter.java new file mode 100644 index 00000000000..987ec0a0be1 --- /dev/null +++ b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestAbstractDiscoveryFilter.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.registry.discovery; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestAbstractDiscoveryFilter { + int initCallCount; + + int childrenInitedCallCount; + + boolean[] inited = new boolean[] {true, true}; + + DiscoveryContext context = new DiscoveryContext(); + + DiscoveryTreeNode child = new DiscoveryTreeNode().name("c1"); + + DiscoveryTreeNode parent = new DiscoveryTreeNode() { + public boolean childrenInited() { + childrenInitedCallCount++; + return inited[childrenInitedCallCount - 1]; + } + }; + + class AbstractDiscoveryFilterForTest extends AbstractDiscoveryFilter { + @Override + public int getOrder() { + return 0; + } + + @Override + protected void init(DiscoveryContext context, DiscoveryTreeNode parent) { + initCallCount++; + } + + @Override + protected String findChildName(DiscoveryContext context, DiscoveryTreeNode parent) { + return child.name(); + } + } + + AbstractDiscoveryFilterForTest filter = new AbstractDiscoveryFilterForTest(); + + DiscoveryTreeNode result; + + private void doDiscovery() { + parent.child(child.name(), child); + result = filter.discovery(context, parent); + } + + @Test + public void discoveryInited() { + doDiscovery(); + + Assertions.assertEquals(1, childrenInitedCallCount); + Assertions.assertEquals(0, initCallCount); + Assertions.assertSame(child, result); + } + + @Test + public void discoveryNotInitedOnce() { + inited[0] = false; + doDiscovery(); + + Assertions.assertEquals(2, childrenInitedCallCount); + Assertions.assertEquals(0, initCallCount); + Assertions.assertSame(child, result); + } + + @Test + public void discoveryNotInitedTwice() { + inited[0] = false; + inited[1] = false; + doDiscovery(); + + Assertions.assertEquals(2, childrenInitedCallCount); + Assertions.assertEquals(1, initCallCount); + Assertions.assertSame(child, result); + } +} 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 new file mode 100644 index 00000000000..4a0c04140e4 --- /dev/null +++ b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestAbstractTransportDiscoveryFilter.java @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Collections; +import java.util.List; +import java.util.UUID; + +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 { + @Override + public int getOrder() { + return 0; + } + + @Override + protected String findTransportName(DiscoveryContext context, DiscoveryTreeNode parent) { + return transportName; + } + + @Override + protected Object createEndpoint(DiscoveryContext context, String transportName, String endpoint, + StatefulDiscoveryInstance instance) { + if (disableCreate) { + return null; + } + + return endpoint; + } + } + + boolean disableCreate; + + String transportName; + + AbstractEndpointDiscoveryFilterForTest filter = new AbstractEndpointDiscoveryFilterForTest(); + + DiscoveryContext context = new DiscoveryContext(); + + DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent"); + + DiscoveryTreeNode result; + + @Test + public void isGroupingFilter() { + Assertions.assertTrue(filter.isGroupingFilter()); + } + + @Test + public void isTransportNameMatch_expectAll() { + Assertions.assertTrue(filter.isTransportNameMatch("any", "")); + } + + @Test + public void isTransportNameMatch_equals() { + Assertions.assertTrue(filter.isTransportNameMatch("rest", "rest")); + } + + @Test + public void isTransportNameMatch_notEquals() { + Assertions.assertFalse(filter.isTransportNameMatch("rest", "highway")); + } + + @Test + public void discoveryNotExist() { + transportName = "notExist"; + parent.data(Collections.emptyList()); + result = filter.discovery(context, parent); + + Assertions.assertTrue(result.isEmpty()); + } + + @Test + public void discoveryExist() { + transportName = "rest"; + DiscoveryTreeNode child = new DiscoveryTreeNode(); + parent.child(transportName, child); + result = filter.discovery(context, parent); + + Assertions.assertSame(child, result); + } + + private StatefulDiscoveryInstance createInstance(String... schemas) { + String id = UUID.randomUUID().toString(); + 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]; + 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 List createMicroserviceInstances(StatefulDiscoveryInstance... instances) { + List result = new ArrayList<>(); + for (StatefulDiscoveryInstance instance : instances) { + result.add(instance); + } + return result; + } + + @Test + public void createDiscoveryTree_oneTransport() { + 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))); + } + + @Test + public void createDiscoveryTree_allTransport() { + StatefulDiscoveryInstance instance1 = createInstance("a", "b"); + StatefulDiscoveryInstance instance2 = createInstance("b"); + List instances = createMicroserviceInstances(instance1, instance2); + parent.data(instances); + + result = filter.createDiscoveryTreeNode("", context, parent); + + Assertions.assertEquals("parent/", result.name()); + + List expect = new ArrayList<>(); + expect.addAll(instance1.getEndpoints()); + expect.addAll(instance2.getEndpoints()); + MatcherAssert.assertThat(result.collectionData(), Matchers.contains(expect.toArray())); + } + + @Test + public void createDiscoveryTree_ignoreInvalid() { + 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())); + } + + @Test + public void createEndpointNull() { + disableCreate = true; + StatefulDiscoveryInstance instance1 = createInstance("a", "b"); + List instances = createMicroserviceInstances(instance1); + parent.data(instances); + + result = filter.createDiscoveryTreeNode("", context, parent); + + Assertions.assertEquals("parent/", result.name()); + MatcherAssert.assertThat(result.collectionData(), Matchers.empty()); + } +} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryContext.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryContext.java new file mode 100644 index 00000000000..53c9d026c6f --- /dev/null +++ b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryContext.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.registry.discovery; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestDiscoveryContext { + DiscoveryContext context = new DiscoveryContext(); + + @Test + public void inputParameters() { + Object inputParameters = new Object(); + context.setInputParameters(inputParameters); + + Assertions.assertSame(inputParameters, context.getInputParameters()); + } + + @Test + public void contextParameters() { + String name = "name"; + Object value = new Object(); + context.putContextParameter(name, value); + + Assertions.assertSame(value, context.getContextParameter(name)); + Assertions.assertNull(context.getContextParameter("notExist")); + } + + @Test + public void rerun() { + Assertions.assertNull(context.popRerunFilter()); + + DiscoveryTreeNode node = new DiscoveryTreeNode(); + context.setCurrentNode(node); + context.pushRerunFilter(); + + Assertions.assertSame(node, context.popRerunFilter()); + Assertions.assertNull(context.popRerunFilter()); + } +} 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 new file mode 100644 index 00000000000..d726d1b59c0 --- /dev/null +++ b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTree.java @@ -0,0 +1,279 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.DynamicProperties; +import org.apache.servicecomb.foundation.common.cache.VersionedCache; +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.apache.servicecomb.registry.DiscoveryManager; +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 TestDiscoveryTree { + DiscoveryContext context = new DiscoveryContext(); + + DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent"); + + DiscoveryTreeNode result; + + @BeforeEach + public void before() { + + } + + @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)); + } + + static class DiscoveryFilterForTest implements DiscoveryFilter { + protected String groupName; + + public DiscoveryFilterForTest(String groupName) { + this.groupName = groupName; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public boolean isGroupingFilter() { + return groupName != null; + } + + @Override + public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { + DiscoveryTreeNode child = new DiscoveryTreeNode(); + if (groupName != null) { + child.subName(parent, groupName); + } + return child; + } + } + + @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.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() { + DiscoveryManager discoveryManager = Mockito.mock(DiscoveryManager.class); + DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); + Mockito.when(discoveryManager.getOrCreateVersionedCache("app", "svc")).thenReturn(parent); + + result = discoveryTree.discovery(context, "app", "svc"); + Assertions.assertEquals(parent.name(), result.name()); + Assertions.assertEquals(parent.cacheVersion(), result.cacheVersion()); + } + + @Test + public void discovery_filterReturnNull() { + 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() { + return 0; + } + + @Override + public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { + return null; + } + }; + discoveryTree.setDiscoveryFilters(Arrays.asList(filter)); + + ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, + () -> result = discoveryTree.discovery(context, "app", "service")); + Assertions.assertEquals(filter.getClass().getName() + " discovery return null.", exception.getMessage()); + } + + @Test + public void filterRerun() { + parent.name("1.0.0-2.0.0"); + + DiscoveryFilterForTest f1 = new DiscoveryFilterForTest("g1") { + @Override + public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { + if (context.getContextParameter("step") == null) { + context.pushRerunFilter(); + context.putContextParameter("step", 1); + return new DiscoveryTreeNode().name(groupName).data("first"); + } + + return new DiscoveryTreeNode().name(groupName).data("second"); + } + }; + DiscoveryFilterForTest f2 = new DiscoveryFilterForTest(null) { + @Override + public DiscoveryTreeNode discovery(DiscoveryContext context, DiscoveryTreeNode parent) { + if ("first".equals(parent.data())) { + return new DiscoveryTreeNode(); + } + + return new DiscoveryTreeNode().data(parent.data()); + } + }; + 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 + @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 + @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(); + } + + countDownLatch.await(3000, TimeUnit.MILLISECONDS); + Assertions.assertEquals(1000, success.get()); + } +} diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTreeNode.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTreeNode.java new file mode 100644 index 00000000000..0dcd4ff99ac --- /dev/null +++ b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTreeNode.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.registry.discovery; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.cache.VersionedCache; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestDiscoveryTreeNode { + DiscoveryTreeNode node = new DiscoveryTreeNode(); + + @Test + public void childrenInited() { + Assertions.assertFalse(node.childrenInited()); + + node.childrenInited(true); + Assertions.assertTrue(node.childrenInited()); + } + + @Test + public void level() { + node.level(1); + + Assertions.assertEquals(1, node.level()); + } + + @Test + public void attribute() { + node.attribute("k1", "v1"); + + Assertions.assertEquals("v1", node.attribute("k1")); + } + + @Test + public void children() { + Map children = new HashMap<>(); + node.children(children); + + Assertions.assertSame(children, node.children()); + } + + @Test + public void child() { + DiscoveryTreeNode child = new DiscoveryTreeNode().name("child"); + node.child(child.name(), child); + + Assertions.assertSame(child, node.child(child.name())); + } + + @Test + public void fromCache() { + Object data = new Object(); + VersionedCache other = new VersionedCache().cacheVersion(1).name("cache").data(data); + node.fromCache(other); + + Assertions.assertEquals(1, node.cacheVersion()); + Assertions.assertEquals("cache", node.name()); + Assertions.assertSame(data, node.data()); + } +} 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/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 new file mode 100644 index 00000000000..aa38214b3ee --- /dev/null +++ b/foundations/foundation-spi/pom.xml @@ -0,0 +1,44 @@ + + + + 4.0.0 + + org.apache.servicecomb + foundations + 3.4.0-SNAPSHOT + + foundation-spi + Java Chassis::Foundations::SSL + + + + org.slf4j + slf4j-api + + + org.springframework + spring-core + + + org.jmockit + jmockit + 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 new file mode 100644 index 00000000000..668e77faa38 --- /dev/null +++ b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderLoader.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.foundation.auth; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; + +public class AuthHeaderLoader { + private final List authHeaderProviders = + SPIServiceUtils.getSortedService(AuthHeaderProvider.class); + + private static final AuthHeaderLoader INSTANCE = new AuthHeaderLoader(); + + private AuthHeaderLoader() { + + } + + public static AuthHeaderLoader getInstance() { + return INSTANCE; + } + + public Map loadAuthHeaders(SignRequest signRequest) { + Map result = new HashMap<>(); + authHeaderProviders.forEach(provider -> result.putAll(provider.getSignAuthHeaders(signRequest))); + return result; + } +} 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 new file mode 100644 index 00000000000..0e036e21a58 --- /dev/null +++ b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderProvider.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.foundation.auth; + +import java.util.HashMap; +import java.util.Map; + +public interface AuthHeaderProvider { + /** + * 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) { + 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/auth/SignRequest.java b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/SignRequest.java new file mode 100644 index 00000000000..a41aaf571b5 --- /dev/null +++ b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/SignRequest.java @@ -0,0 +1,198 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.auth; + +import java.io.InputStream; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +public class SignRequest { + + /** + * the resource path being requested + */ + private String resourcePath; + + /** + * queryParams parameters being sent as part of this request. + */ + private Map queryParams; + + /** + * Map of the headers included in this request + */ + private final Map headers = new HashMap<>(); + + /** + * The service endpoint to which this request should be sent + */ + private URI endpoint; + + /** + * The HTTP method to use when sending this request. + */ + private String httpMethod = "GET"; + + /** + * An optional stream from which to read the request payload. + */ + private InputStream content; + + /** + * The datetime in milliseconds for which the signature needs to be + * computed. + */ + private final long signingDateTimeMilli = System.currentTimeMillis(); + + /** + * The scope of the signature. + */ + private String scope; + + /** + * The region to be used for computing the signature. + */ + private String regionName; + + /** + * The name of the service. + */ + private String serviceName; + + /** + * UTC formatted version of the signing time stamp. + */ + private String formattedSigningDateTime; + + /** + * UTC Formatted Signing date with time stamp stripped + */ + private String formattedSigningDate; + + public SignRequest() { + + } + + public String getResourcePath() { + return resourcePath; + } + + public void setResourcePath(String resourcePath) { + this.resourcePath = resourcePath; + } + + + public long getSigningDateTimeMilli() { + return signingDateTimeMilli; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public String getRegionName() { + if (regionName == null) { + return ""; + } + return regionName; + } + + public void setRegionName(String regionName) { + this.regionName = regionName; + } + + public String getServiceName() { + if (serviceName == null) { + return ""; + } + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getFormattedSigningDateTime() { + return formattedSigningDateTime; + } + + public void setFormattedSigningDateTime(String formattedSigningDateTime) { + this.formattedSigningDateTime = formattedSigningDateTime; + } + + public String getFormattedSigningDate() { + return formattedSigningDate; + } + + public void setFormattedSigningDate(String formattedSigningDate) { + this.formattedSigningDate = formattedSigningDate; + } + + public Map getHeaders() { + return headers; + } + + public String getHttpMethod() { + return httpMethod; + } + + + public void setHttpMethod(String httpMethod) { + this.httpMethod = httpMethod; + } + + + public void setEndpoint(URI endpoint) { + this.endpoint = endpoint; + } + + + public URI getEndpoint() { + return endpoint; + } + + + public InputStream getContent() { + return content; + } + + + public void setContent(InputStream content) { + this.content = content; + } + + + public void setHeaders(Map headers) { + this.headers.clear(); + this.headers.putAll(headers); + } + + + public Map getQueryParams() { + return queryParams; + } + + public void setQueryParams(Map queryParams) { + this.queryParams = queryParams; + } +} diff --git a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/bootstrap/BootStrapService.java b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/bootstrap/BootStrapService.java new file mode 100644 index 00000000000..505dac988cb --- /dev/null +++ b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/bootstrap/BootStrapService.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.foundation.bootstrap; + +import org.apache.servicecomb.foundation.common.utils.SPIOrder; +import org.springframework.core.env.Environment; + +/** + * A boot strap service is loaded after Spring Environment is created. + * + * In boot strap service, user's can only read configurations from Environment. Dynamic configurations + * + * from config center is not available. + * + * e.g. an authentication service must be connected before connecting to config center. + * e.g. an config center configurations must be read before connecting to config center. + */ +public interface BootStrapService extends SPIOrder { + void startup(Environment environment); +} diff --git a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIEnabled.java b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIEnabled.java new file mode 100644 index 00000000000..adab60adf99 --- /dev/null +++ b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIEnabled.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.foundation.common.utils; + +public interface SPIEnabled { + boolean enabled(); +} 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 new file mode 100644 index 00000000000..adaf18de073 --- /dev/null +++ b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIOrder.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.foundation.common.utils; + +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 new file mode 100644 index 00000000000..abd2dcab32e --- /dev/null +++ b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/common/utils/SPIServiceUtils.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lang.reflect.Method; +import java.util.AbstractMap.SimpleEntry; +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.ServiceLoader; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ReflectionUtils; + +/** + * SPI Service utils + * + * + */ +public final class SPIServiceUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(SPIServiceUtils.class); + + // load one service, maybe trigger load another service + // computeIfAbsent can not support this feature + // so use double check + private static final Object LOCK = new Object(); + + private static final Map, List> cache = new ConcurrentHashMap<>(); + + private SPIServiceUtils() { + + } + + /** + * no cache, return new instances every time. + */ + public static List loadSortedService(Class serviceType) { + List> serviceEntries = new ArrayList<>(); + ServiceLoader serviceLoader = ServiceLoader.load(serviceType); + serviceLoader.forEach(service -> { + int serviceOrder = 0; + Method getOrder = ReflectionUtils.findMethod(service.getClass(), "getOrder"); + if (getOrder != null) { + serviceOrder = (int) ReflectionUtils.invokeMethod(getOrder, service); + } + + Entry entry = new SimpleEntry<>(serviceOrder, service); + serviceEntries.add(entry); + }); + + List services = serviceEntries.stream() + .sorted(Comparator.comparingInt(Entry::getKey)) + .map(Entry::getValue) + .collect(Collectors.toList()); + + StringBuilder info = new StringBuilder(); + for (int idx = 0; idx < services.size(); idx++) { + T service = services.get(idx); + info.append("{").append(idx).append(",").append(service.getClass().getSimpleName()).append("}"); + } + LOGGER.info("Found SPI service {}, services={}.", serviceType.getSimpleName(), info); + + return services; + } + + @SuppressWarnings("unchecked") + public static List getOrLoadSortedService(Class serviceType) { + List services = cache.get(serviceType); + if (services == null) { + synchronized (LOCK) { + services = cache.get(serviceType); + if (services == null) { + services = (List) loadSortedService(serviceType); + cache.put(serviceType, services); + } + } + } + + return (List) services; + } + + /** + * get target service.if target services are array,only random access to a service. + */ + public static T getTargetService(Class serviceType) { + List services = getOrLoadSortedService(serviceType); + if (services.isEmpty()) { + LOGGER.info("Can not find SPI service for {}", serviceType.getName()); + return null; + } + + return services.get(0); + } + + public static List getAllService(Class serviceType) { + return getOrLoadSortedService(serviceType); + } + + public static List getSortedService(Class serviceType) { + return getOrLoadSortedService(serviceType); + } + + public static T getPriorityHighestService(Class serviceType) { + List services = getOrLoadSortedService(serviceType); + if (services.isEmpty()) { + LOGGER.info("Can not find SPI service for {}", serviceType.getName()); + return null; + } + + return services.get(0); + } + + public static Collection getPriorityHighestServices(Function keyFunc, Class serviceType) { + List services = getOrLoadSortedService(serviceType); + if (services.isEmpty()) { + LOGGER.info("Can not find SPI service for {}", serviceType.getName()); + return null; + } + + Map map = new HashMap<>(); + services.forEach(instance -> map.putIfAbsent(keyFunc.apply(instance), instance)); + return map.values(); + } + + @SuppressWarnings("unchecked") + public static IMPL getTargetService(Class serviceType, Class implType) { + List services = getOrLoadSortedService(serviceType); + return (IMPL) services + .stream() + .filter(service -> service.getClass().equals(implType)) + .findFirst() + .orElse(null); + } +} diff --git a/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef.java b/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef.java new file mode 100644 index 00000000000..75f726ebc87 --- /dev/null +++ b/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef.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.foundation.common.utils; + +public interface SPIServiceDef { + +} diff --git a/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef0.java b/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef0.java new file mode 100644 index 00000000000..b661693e810 --- /dev/null +++ b/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef0.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.foundation.common.utils; + +public interface SPIServiceDef0 { + +} diff --git a/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef0Impl.java b/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef0Impl.java new file mode 100644 index 00000000000..08cd5a3d28b --- /dev/null +++ b/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDef0Impl.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.foundation.common.utils; + +public class SPIServiceDef0Impl implements SPIServiceDef0 { + +} diff --git a/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDefImpl.java b/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDefImpl.java new file mode 100644 index 00000000000..c8aff5c188b --- /dev/null +++ b/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/SPIServiceDefImpl.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.foundation.common.utils; + +public class SPIServiceDefImpl implements SPIServiceDef { + +} diff --git a/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/TestSPIServiceUtils.java b/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/TestSPIServiceUtils.java new file mode 100644 index 00000000000..040c840718f --- /dev/null +++ b/foundations/foundation-spi/src/test/java/org/apache/servicecomb/foundation/common/utils/TestSPIServiceUtils.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.mockito.Mockito; +import org.springframework.core.Ordered; + +import mockit.Deencapsulation; +import mockit.Expectations; + +/** + * Test SPIServiceUtils + * + * + */ +public class TestSPIServiceUtils { + @Test + public void testGetTargetServiceNull() { + SPIServiceDef0 service = SPIServiceUtils.getTargetService(SPIServiceDef0.class); + Assertions.assertNull(service); + } + + @Test + public void testGetTargetServiceNotNull() { + SPIServiceDef service = SPIServiceUtils.getTargetService(SPIServiceDef.class); + Assertions.assertNotNull(service); + + Assertions.assertSame(service, SPIServiceUtils.getTargetService(SPIServiceDef.class)); + } + + @Test + public void testGetTargetService_special_null() { + Assertions.assertNull(SPIServiceUtils.getTargetService(SPIServiceDef0.class, SPIServiceDef0Impl.class)); + } + + @Test + public void testGetTargetService_special_notNull() { + SPIServiceDef service = SPIServiceUtils.getTargetService(SPIServiceDef.class, SPIServiceDefImpl.class); + Assertions.assertNotNull(service); + } + + @Test + @EnabledOnJre(JRE.JAVA_8) + public void testSort() { + Ordered o1 = Mockito.mock(Ordered.class); + Ordered o2 = Mockito.mock(Ordered.class); + Map map = new LinkedHashMap<>(); + map.put("a", o1); + map.put("b", o2); + + ServiceLoader serviceLoader = ServiceLoader.load(Ordered.class); + Deencapsulation.setField(serviceLoader, "providers", map); + new Expectations(ServiceLoader.class) { + { + o1.getOrder(); + result = -1; + o2.getOrder(); + result = Integer.MAX_VALUE; + ServiceLoader.load(Ordered.class); + result = serviceLoader; + } + }; + + MatcherAssert.assertThat(SPIServiceUtils.getSortedService(Ordered.class), Matchers.contains(o1, o2)); + MatcherAssert.assertThat(SPIServiceUtils.getAllService(Ordered.class), Matchers.contains(o1, o2)); + MatcherAssert.assertThat(SPIServiceUtils.getPriorityHighestService(Ordered.class), Matchers.is(o1)); + + Map, List> cache = Deencapsulation.getField(SPIServiceUtils.class, "cache"); + cache.clear(); + } + + @Test + public void getPriorityHighestService_null() { + Assertions.assertNull(SPIServiceUtils.getPriorityHighestService(SPIServiceDef0.class)); + } + + interface PriorityIntf { + String getName(); + + int getOrder(); + } + + public static class PriorityImpl implements PriorityIntf { + private final String name; + + private final int order; + + public PriorityImpl(String name, int order) { + this.name = name; + this.order = order; + } + + @Override + public String getName() { + return name; + } + + @Override + public int getOrder() { + return order; + } + + @Override + public String toString() { + return "PriorityImpl{" + + "name='" + name + '\'' + + ", order=" + order + + '}'; + } + } + + @Test + @EnabledOnJre(JRE.JAVA_8) + public void getPriorityHighestServices() { + Map instances = new LinkedHashMap<>(); + instances.putIfAbsent("1", new PriorityImpl("n1", 0)); + instances.putIfAbsent("2", new PriorityImpl("n1", -1)); + instances.putIfAbsent("3", new PriorityImpl("n1", 1)); + instances.putIfAbsent("4", new PriorityImpl("n2", 0)); + instances.putIfAbsent("5", new PriorityImpl("n2", -1)); + instances.putIfAbsent("6", new PriorityImpl("n2", 1)); + + ServiceLoader serviceLoader = ServiceLoader.load(PriorityIntf.class); + Deencapsulation.setField(serviceLoader, "providers", instances); + new Expectations(ServiceLoader.class) { + { + ServiceLoader.load(PriorityIntf.class); + result = serviceLoader; + } + }; + + MatcherAssert.assertThat(SPIServiceUtils.getPriorityHighestServices(PriorityIntf::getName, PriorityIntf.class), + Matchers.containsInAnyOrder(instances.get("2"), instances.get("5"))); + + Map, List> cache = Deencapsulation.getField(SPIServiceUtils.class, "cache"); + cache.clear(); + } +} 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 new file mode 100644 index 00000000000..7d8b6d492a4 --- /dev/null +++ b/foundations/foundation-spi/src/test/resources/META-INF/services/org.apache.servicecomb.foundation.common.utils.SPIServiceDef @@ -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.foundation.common.utils.SPIServiceDefImpl diff --git a/foundations/foundation-ssl/pom.xml b/foundations/foundation-ssl/pom.xml index bb6053ce76c..3cf7cdcb485 100644 --- a/foundations/foundation-ssl/pom.xml +++ b/foundations/foundation-ssl/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - foundations - 0.3.0-SNAPSHOT - - foundation-ssl + 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 + foundations + 3.4.0-SNAPSHOT + + foundation-ssl + Java Chassis::Foundations::SSL - - - com.netflix.archaius - archaius-core - - - commons-configuration - commons-configuration - - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-log4j12 - test - - - org.jmockit - jmockit - test - - - org.mockito - mockito-core - test - - - io.servicecomb - foundation-config - test - - + + + org.springframework + spring-context + + + org.apache.commons + commons-lang3 + + + org.slf4j + slf4j-api + + + io.netty + netty-tcnative-boringssl-static + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.servicecomb + foundation-config + test + + + org.jmockit + jmockit + test + + diff --git a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/KeyStoreUtil.java b/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/KeyStoreUtil.java deleted file mode 100644 index af566f0ecb1..00000000000 --- a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/KeyStoreUtil.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.security.KeyStore; -import java.security.cert.CRL; -import java.security.cert.CRLException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.util.Collection; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; - -public final class KeyStoreUtil { - private KeyStoreUtil() { - - } - - public static KeyStore createKeyStore(String storename, String storetype, - char[] storevalue) { - InputStream is = null; - try { - KeyStore keystore = KeyStore.getInstance(storetype); - is = new FileInputStream(storename); - keystore.load(is, storevalue); - return keystore; - } catch (Exception e) { - throw new IllegalArgumentException("Bad key store or value." - + e.getMessage()); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - ignore(); - } - } - } - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public static CRL[] createCRL(String crlfile) { - InputStream is = null; - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - is = new FileInputStream(crlfile); - Collection c = cf.generateCRLs(is); - CRL[] crls = (CRL[]) c.toArray(new CRL[c.size()]); - return crls; - } catch (CertificateException e) { - throw new IllegalArgumentException("bad cert file."); - } catch (FileNotFoundException e) { - throw new IllegalArgumentException("crl file not found."); - } catch (CRLException e) { - throw new IllegalArgumentException("bad crl file."); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - ignore(); - } - } - } - } - - public static KeyManager[] createKeyManagers(final KeyStore keystore, - char[] keyvalue) { - try { - KeyManagerFactory kmfactory = - KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmfactory.init(keystore, keyvalue); - return kmfactory.getKeyManagers(); - } catch (Exception e) { - throw new IllegalArgumentException("Bad key store." - + e.getMessage()); - } - } - - public static TrustManager[] createTrustManagers(final KeyStore keystore) { - try { - TrustManagerFactory tmfactory = - TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - tmfactory.init(keystore); - TrustManager[] trustmanagers = tmfactory.getTrustManagers(); - return trustmanagers; - } catch (Exception e) { - throw new IllegalArgumentException("Bad trust store." - + e.getMessage()); - } - } - - private static void ignore() { - } -} diff --git a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLCustom.java b/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLCustom.java deleted file mode 100644 index ff6c6f19ff8..00000000000 --- a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLCustom.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * 和应用相关的信息,方便定制使用。 目前主要包含密码解密、证书路径、IP和Port等内容。 - * - */ -public abstract class SSLCustom { - private static final Logger LOG = LoggerFactory.getLogger(SSLCustom.class); - - public static SSLCustom defaultSSLCustom() { - final SSLCustom custom = new SSLCustom() { - @Override - public char[] decode(char[] encrypted) { - return encrypted; - } - - @Override - public String getFullPath(String filename) { - return filename; - } - }; - return custom; - } - - public static SSLCustom createSSLCustom(String name) { - try { - if (name != null && !name.isEmpty()) { - return (SSLCustom) Class.forName(name).newInstance(); - } - } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { - LOG.warn("init SSLCustom class failed, name is " + name); - } - return defaultSSLCustom(); - } - - public abstract char[] decode(char[] encrypted); - - public abstract String getFullPath(String filename); - - public String getHost() { - return null; - } -} diff --git a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLManager.java b/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLManager.java deleted file mode 100644 index 7a9dc8fd162..00000000000 --- a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLManager.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -import java.io.File; -import java.io.IOException; -import java.net.UnknownHostException; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLServerSocketFactory; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509ExtendedTrustManager; - -/** - * 根据传递的SSLOption构造SSL上下文。请参考JSSE获取相关API的层次参考。 - * - */ -public final class SSLManager { - private SSLManager() { - - } - - public static SSLContext createSSLContext(SSLOption option, SSLCustom custom) { - try { - String keyStoreName = custom.getFullPath(option.getKeyStore()); - KeyManager[] keymanager; - if (keyStoreName != null && new File(keyStoreName).exists()) { - char[] keyStoreValue = - custom.decode(option.getKeyStoreValue().toCharArray()); - KeyStore keyStore = - KeyStoreUtil.createKeyStore(keyStoreName, - option.getKeyStoreType(), - keyStoreValue); - keymanager = - KeyStoreUtil.createKeyManagers(keyStore, keyStoreValue); - } else { - keymanager = null; - } - - String trustStoreName = custom.getFullPath(option.getTrustStore()); - TrustManager[] trustManager; - if (trustStoreName != null && new File(trustStoreName).exists()) { - char[] trustStoreValue = - custom.decode(option.getTrustStoreValue().toCharArray()); - KeyStore trustStore = - KeyStoreUtil.createKeyStore(trustStoreName, - option.getTrustStoreType(), - trustStoreValue); - trustManager = - KeyStoreUtil.createTrustManagers(trustStore); - } else { - trustManager = new TrustManager[] {new TrustAllManager()}; - } - - TrustManager[] wrapped = new TrustManager[trustManager.length]; - for (int i = 0; i < trustManager.length; i++) { - wrapped[i] = - new TrustManagerExt((X509ExtendedTrustManager) trustManager[i], - option, custom); - } - - // ?: ssl context version - SSLContext context = SSLContext.getInstance("TLS"); - context.init(keymanager, wrapped, new SecureRandom()); - return context; - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException("NoSuchAlgorithmException." - + e.getMessage()); - } catch (KeyManagementException e) { - throw new IllegalArgumentException("KeyManagementException." - + e.getMessage()); - } - } - - public static SSLSocketFactory createSSLSocketFactory(SSLOption option, SSLCustom custom) { - SSLContext context = createSSLContext(option, custom); - SSLSocketFactory factory = context.getSocketFactory(); - String[] supported = factory.getSupportedCipherSuites(); - String[] eanbled = option.getCiphers().split(","); - return new SSLSocketFactoryExt(factory, getEnabledCiphers(supported, eanbled), - option.getProtocols().split(",")); - } - - public static SSLEngine createSSLEngine(SSLOption option, SSLCustom custom) { - SSLContext context = createSSLContext(option, custom); - SSLEngine engine = - context.createSSLEngine(); - engine.setEnabledProtocols(option.getProtocols().split(",")); - String[] supported = engine.getSupportedCipherSuites(); - String[] eanbled = option.getCiphers().split(","); - engine.setEnabledCipherSuites(getEnabledCiphers(supported, eanbled)); - engine.setNeedClientAuth(option.isAuthPeer()); - return engine; - } - - public static SSLEngine createSSLEngine(SSLOption option, SSLCustom custom, String peerHost, int peerPort) { - SSLContext context = createSSLContext(option, custom); - SSLEngine engine = - context.createSSLEngine(peerHost, peerPort); - engine.setEnabledProtocols(option.getProtocols().split(",")); - String[] supported = engine.getSupportedCipherSuites(); - String[] eanbled = option.getCiphers().split(","); - engine.setEnabledCipherSuites(getEnabledCiphers(supported, eanbled)); - engine.setNeedClientAuth(option.isAuthPeer()); - return engine; - } - - public static SSLServerSocket createSSLServerSocket(SSLOption option, - SSLCustom custom) { - try { - SSLContext context = createSSLContext(option, custom); - SSLServerSocketFactory factory = context.getServerSocketFactory(); - SSLServerSocket socket = - (SSLServerSocket) factory.createServerSocket(); - socket.setEnabledProtocols(option.getProtocols().split(",")); - String[] supported = socket.getSupportedCipherSuites(); - String[] eanbled = option.getCiphers().split(","); - socket.setEnabledCipherSuites(getEnabledCiphers(supported, eanbled)); - socket.setNeedClientAuth(option.isAuthPeer()); - return socket; - } catch (UnknownHostException e) { - throw new IllegalArgumentException("unkown host"); - } catch (IOException e) { - throw new IllegalArgumentException("unable create socket"); - } - } - - public static SSLSocket createSSLSocket(SSLOption option, SSLCustom custom) { - try { - SSLContext context = createSSLContext(option, custom); - SSLSocketFactory facroty = context.getSocketFactory(); - SSLSocket socket = - (SSLSocket) facroty.createSocket(); - socket.setEnabledProtocols(option.getProtocols().split(",")); - String[] supported = socket.getSupportedCipherSuites(); - String[] eanbled = option.getCiphers().split(","); - socket.setEnabledCipherSuites(getEnabledCiphers(supported, eanbled)); - return socket; - } catch (UnknownHostException e) { - throw new IllegalArgumentException("unkown host"); - } catch (IOException e) { - throw new IllegalArgumentException("unable create socket"); - } - } - - private static String[] getEnabledCiphers(String[] supported, - String[] enabled) { - String[] result = new String[enabled.length]; - int count = 0; - for (String e : enabled) { - for (String s : supported) { - if (e.equals(s)) { - result[count++] = e; - break; - } - } - } - - if (count == 0) { - throw new IllegalArgumentException("no enabled cipher suits."); - } - - String[] r = new String[count]; - System.arraycopy(result, 0, r, 0, count); - return r; - } - - public static String[] getEnalbedCiphers(String enabledCiphers) { - SSLOption option = new SSLOption(); - option.setProtocols("TLSv1.2"); - option.setCiphers(enabledCiphers); - SSLCustom custom = SSLCustom.defaultSSLCustom(); - SSLSocket socket = createSSLSocket(option, custom); - return socket.getEnabledCipherSuites(); - } -} diff --git a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLOption.java b/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLOption.java deleted file mode 100644 index a34b1ce8594..00000000000 --- a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLOption.java +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.Charset; -import java.util.List; -import java.util.Properties; - -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.DynamicPropertyFactory; - -/** - * SSL配置选项。 - * - */ -public final class SSLOption { - private static final SSLOption DEFAULT_OPTION = new SSLOption(); - - public static final String DEFAUL_CIPHERS = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384," - + "TLS_RSA_WITH_AES_256_GCM_SHA384," - + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256," - + "TLS_RSA_WITH_AES_128_GCM_SHA256"; - - static { - DEFAULT_OPTION.setProtocols("TLSv1.2"); - DEFAULT_OPTION.setCiphers(DEFAUL_CIPHERS); - DEFAULT_OPTION.setAuthPeer(false); - DEFAULT_OPTION.setCheckCNHost(false); - DEFAULT_OPTION.setCheckCNWhite(false); - DEFAULT_OPTION.setCheckCNWhiteFile("white.list"); - DEFAULT_OPTION.setAllowRenegociate(true); - DEFAULT_OPTION.setStorePath("internal"); - DEFAULT_OPTION.setTrustStore("trust.jks"); - DEFAULT_OPTION.setTrustStoreType("JKS"); - DEFAULT_OPTION.setTrustStoreValue("trustStoreValue"); - DEFAULT_OPTION.setKeyStore("server.p12"); - DEFAULT_OPTION.setKeyStoreType("PKCS12"); - DEFAULT_OPTION.setKeyStoreValue("keyStoreValue"); - DEFAULT_OPTION.setCrl("revoke.crl"); - } - - private String protocols; - - private String ciphers; - - private boolean authPeer; - - private boolean checkCNHost; - - private boolean checkCNWhite; - - private String checkCNWhiteFile; - - private boolean allowRenegociate; - - private String storePath; - - private String trustStore; - - private String trustStoreType; - - private String trustStoreValue; - - private String keyStore; - - private String keyStoreType; - - private String keyStoreValue; - - private String crl; - - private String sslCustomClass; - - public void setProtocols(String protocols) { - this.protocols = protocols; - } - - public void setCiphers(String ciphers) { - this.ciphers = ciphers; - } - - public void setAuthPeer(boolean authPeer) { - this.authPeer = authPeer; - } - - public void setCheckCNHost(boolean checkCNHost) { - this.checkCNHost = checkCNHost; - } - - public void setCheckCNWhite(boolean checkCNWhite) { - this.checkCNWhite = checkCNWhite; - } - - public void setCheckCNWhiteFile(String checkCNWhiteFile) { - this.checkCNWhiteFile = checkCNWhiteFile; - } - - public void setAllowRenegociate(boolean allowRenegociate) { - this.allowRenegociate = allowRenegociate; - } - - public void setStorePath(String storePath) { - this.storePath = storePath; - } - - public void setTrustStore(String trustStore) { - this.trustStore = trustStore; - } - - public void setTrustStoreType(String trustStoreType) { - this.trustStoreType = trustStoreType; - } - - public void setTrustStoreValue(String trustStoreValue) { - this.trustStoreValue = trustStoreValue; - } - - public void setKeyStore(String keyStore) { - this.keyStore = keyStore; - } - - public void setKeyStoreType(String keyStoreType) { - this.keyStoreType = keyStoreType; - } - - public void setKeyStoreValue(String keyStoreValue) { - this.keyStoreValue = keyStoreValue; - } - - public void setCrl(String crl) { - this.crl = crl; - } - - public String getProtocols() { - return protocols; - } - - public String getCiphers() { - return ciphers; - } - - public boolean isAuthPeer() { - return authPeer; - } - - public boolean isCheckCNHost() { - return checkCNHost; - } - - public boolean isCheckCNWhite() { - return checkCNWhite; - } - - public String getCheckCNWhiteFile() { - return checkCNWhiteFile; - } - - public boolean isAllowRenegociate() { - return allowRenegociate; - } - - public String getStorePath() { - return storePath; - } - - public String getTrustStore() { - return trustStore; - } - - public String getTrustStoreType() { - return trustStoreType; - } - - public String getTrustStoreValue() { - return trustStoreValue; - } - - public String getKeyStore() { - return keyStore; - } - - public String getKeyStoreType() { - return keyStoreType; - } - - public String getKeyStoreValue() { - return keyStoreValue; - } - - public String getCrl() { - return crl; - } - - public static SSLOption build(String optionfile) { - File file = new File(optionfile); - if (!file.isFile()) { - throw new IllegalArgumentException("Bad file name."); - } - - try { - SSLOption option = new SSLOption(); - option.load(file.getCanonicalPath()); - return option; - } catch (IOException e) { - throw new IllegalArgumentException("Bad file name."); - } - } - - public static SSLOption build(InputStream inputStream) { - SSLOption option = new SSLOption(); - option.load(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, - String... keys) { - String property = null; - for (String key : keys) { - if (configSource != null) { - Object v = configSource.getProperty(key); - if (List.class.isInstance(v)) { - property = listToString(((List) v).toArray()); - } else { - property = (String) configSource.getProperty(key); - } - } else { - property = DynamicPropertyFactory.getInstance().getStringProperty(key, null).get(); - } - if (property != null) { - break; - } - } - - if (property != null) { - return property; - } else { - return defaultValue; - } - } - - private static boolean getBooleanProperty(ConcurrentCompositeConfiguration configSource, boolean defaultValue, - String... keys) { - String property = null; - for (String key : keys) { - if (configSource != null) { - if (configSource.getProperty(key) != null) { - return (Boolean) configSource.getProperty(key); - } - } else { - property = DynamicPropertyFactory.getInstance().getStringProperty(key, null).get(); - } - if (property != null) { - break; - } - } - - if (property != null) { - return Boolean.parseBoolean(property); - } else { - return defaultValue; - } - } - - public static SSLOption buildFromYaml(String tag, ConcurrentCompositeConfiguration configSource) { - SSLOption option = new SSLOption(); - option.protocols = - getStringProperty(configSource, - DEFAULT_OPTION.getProtocols(), - "ssl." + tag + ".protocols", - "ssl.protocols"); - option.ciphers = - getStringProperty(configSource, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers"); - option.authPeer = - getBooleanProperty(configSource, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer"); - option.checkCNHost = - getBooleanProperty(configSource, - DEFAULT_OPTION.isCheckCNHost(), - "ssl." + tag + ".checkCN.host", - "ssl.checkCN.host"); - option.checkCNWhite = - getBooleanProperty(configSource, - DEFAULT_OPTION.isCheckCNWhite(), - "ssl." + tag + ".checkCN.white", - "ssl.checkCN.white"); - option.checkCNWhiteFile = getStringProperty(configSource, - 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.storePath = - getStringProperty(configSource, - DEFAULT_OPTION.getStorePath(), - "ssl." + tag + ".storePath", - "ssl.storePath"); - option.trustStore = - getStringProperty(configSource, - DEFAULT_OPTION.getTrustStore(), - "ssl." + tag + ".trustStore", - "ssl.trustStore"); - option.trustStoreType = getStringProperty(configSource, - DEFAULT_OPTION.getTrustStoreType(), - "ssl." + tag + ".trustStoreType", - "ssl.trustStoreType"); - option.trustStoreValue = getStringProperty(configSource, - DEFAULT_OPTION.getTrustStoreValue(), - "ssl." + tag + ".trustStoreValue", - "ssl.trustStoreValue"); - option.keyStore = - getStringProperty(configSource, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore"); - option.keyStoreType = - getStringProperty(configSource, - DEFAULT_OPTION.getKeyStoreType(), - "ssl." + tag + ".keyStoreType", - "ssl.keyStoreType"); - option.keyStoreValue = getStringProperty(configSource, - DEFAULT_OPTION.getKeyStoreValue(), - "ssl." + tag + ".keyStoreValue", - "ssl.keyStoreValue"); - option.crl = getStringProperty(configSource, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl"); - option.sslCustomClass = - getStringProperty(configSource, 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"); - this.authPeer = propBoolean(props, "ssl.authPeer"); - 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.storePath = propString(props, "ssl.storePath"); - this.trustStore = propString(props, "ssl.trustStore"); - this.trustStoreType = propString(props, "ssl.trustStoreType"); - this.trustStoreValue = propString(props, "ssl.trustStoreValue"); - this.keyStore = propString(props, "ssl.keyStore"); - this.keyStoreType = propString(props, "ssl.keyStoreType"); - this.keyStoreValue = propString(props, "ssl.keyStoreValue"); - this.crl = propString(props, "ssl.crl"); - this.sslCustomClass = props.getProperty("ssl.sslCustomClass"); - } - - private String propString(Properties props, String key) { - String s = props.getProperty(key); - if (s == null) { - throw new IllegalArgumentException("No key :" + key); - } - return s; - } - - private boolean propBoolean(Properties props, String key) { - String s = props.getProperty(key); - if (s == null) { - throw new IllegalArgumentException("No key :" + key); - } - return Boolean.parseBoolean(s); - } - - private void load(InputStream inputStream) { - Properties props = new Properties(); - Reader reader = null; - try { - reader = - new InputStreamReader(inputStream, Charset.forName("UTF-8")); - props.load(reader); - fromProperty(props); - } catch (IOException e) { - throw new IllegalArgumentException( - "Can not read ssl client config file"); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - ignore(); - } - } - - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException e) { - ignore(); - } - } - } - } - - private void load(String path) { - try { - load(new FileInputStream(path)); - } catch (FileNotFoundException e) { - throw new IllegalArgumentException( - "Can not read ssl client config file: " + path); - } - } - - private void ignore() { - } - - public String getSslCustomClass() { - return this.sslCustomClass; - } - - public void setSslCustomClass(String sslCustomClass) { - this.sslCustomClass = sslCustomClass; - } -} diff --git a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLOptionFactory.java b/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLOptionFactory.java deleted file mode 100644 index 6fc8bc907ea..00000000000 --- a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLOptionFactory.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -import com.netflix.config.ConcurrentCompositeConfiguration; - -public interface SSLOptionFactory { - static SSLOptionFactory createSSLOptionFactory(String tag, ConcurrentCompositeConfiguration configSource) { - String name = SSLOption.getStringProperty(configSource, - null, - "ssl." + tag + ".sslOptionFactory", - "ssl.sslOptionFactory"); - return createSSLOptionFactory(name); - } - - static SSLOptionFactory createSSLOptionFactory(String className) { - if (className != null && !className.isEmpty()) { - try { - return (SSLOptionFactory) Class.forName(className).newInstance(); - } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { - return null; - } - } - return null; - } - - SSLOption createSSLOption(); -} diff --git a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLSocketFactoryExt.java b/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLSocketFactoryExt.java deleted file mode 100644 index 6f5ef3971a7..00000000000 --- a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/SSLSocketFactoryExt.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.Socket; -import java.net.UnknownHostException; - -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; - -/** - * 扩展SSLSocketFactory,设置算法和协议列表。 - * - */ -public class SSLSocketFactoryExt extends SSLSocketFactory { - private SSLSocketFactory sslSocketFactory; - - private String[] ciphers; - - private String[] protos; - - public SSLSocketFactoryExt(SSLSocketFactory factory, String[] ciphers, String[] protos) { - this.sslSocketFactory = factory; - this.ciphers = ciphers; - this.protos = protos; - } - - @Override - public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { - return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(s, host, port, autoClose)); - } - - @Override - public String[] getDefaultCipherSuites() { - return this.sslSocketFactory.getDefaultCipherSuites(); - } - - @Override - public String[] getSupportedCipherSuites() { - return this.sslSocketFactory.getSupportedCipherSuites(); - } - - @Override - public Socket createSocket(String host, int port) throws IOException, UnknownHostException { - return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(host, port)); - } - - @Override - public Socket createSocket(InetAddress host, int port) throws IOException { - return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(host, port)); - } - - @Override - public Socket createSocket(String host, int port, InetAddress localHost, - int localPort) throws IOException, UnknownHostException { - return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(host, - port, - localHost, - localPort)); - } - - @Override - public Socket createSocket(InetAddress address, int port, InetAddress localAddress, - int localPort) throws IOException { - return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(address, - port, - localAddress, - localPort)); - } - - private SSLSocket wrapSocket(SSLSocket socket) { - socket.setEnabledProtocols(protos); - socket.setEnabledCipherSuites(ciphers); - return socket; - } -} diff --git a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/TrustAllManager.java b/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/TrustAllManager.java deleted file mode 100644 index b0962e29caa..00000000000 --- a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/TrustAllManager.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -import java.net.Socket; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.X509ExtendedTrustManager; - -public class TrustAllManager extends X509ExtendedTrustManager { - - @Override - public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { - } - - @Override - public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType, - Socket socket) throws CertificateException { - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType, - SSLEngine engine) throws CertificateException { - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType, - Socket socket) throws CertificateException { - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType, - SSLEngine engine) throws CertificateException { - } -} diff --git a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/CertificateUtil.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/CertificateUtil.java similarity index 86% rename from foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/CertificateUtil.java rename to foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/CertificateUtil.java index 5a8d3626507..8fcf41af10a 100644 --- a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/CertificateUtil.java +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/CertificateUtil.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +15,7 @@ * limitations under the License. */ -package io.servicecomb.foundation.ssl; +package org.apache.servicecomb.foundation.ssl; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; @@ -50,8 +51,7 @@ private static X509Certificate[] sort(X509Certificate[] cerChain) { for (int i = 1; i < chain.length; i++) { X509Certificate parent = chain[i - 1]; - for (int j = 0; j < cerChain.length; j++) { - X509Certificate child = cerChain[j]; + for (X509Certificate child : cerChain) { String parentDN = parent.getSubjectX500Principal().getName(); String childDN = child.getSubjectX500Principal().getName(); if (parentDN.equals(childDN)) { @@ -105,7 +105,7 @@ public static X509Certificate findOwner(X509Certificate[] cerChain) { } public static Set getCN(X509Certificate cert) { - Set names = new HashSet(); + Set names = new HashSet<>(); // 读取CN String subjectDN = cert.getSubjectX500Principal().getName(); @@ -126,7 +126,7 @@ public static Set getCN(X509Certificate cert) { Object key = list.get(0); Object value = list.get(1); if (key instanceof Integer && value instanceof String) { - int intKey = ((Integer) key).intValue(); + int intKey = (Integer) key; String strValue = (String) value; if (intKey == SUBALTNAME_DNSNAME || intKey == SUBALTNAME_IPADDRESS) { names.add(strValue); diff --git a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/ClientAuth.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/ClientAuth.java new file mode 100644 index 00000000000..b80d0033ec9 --- /dev/null +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/ClientAuth.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.foundation.ssl; + +public final class ClientAuth { + public static final String REQUIRED = "REQUIRED"; + + public static final String REQUEST = "REQUEST"; + + public static final String NONE = "NONE"; +} diff --git a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtil.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtil.java new file mode 100644 index 00000000000..0666365a2f0 --- /dev/null +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtil.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.foundation.ssl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.KeyStore; +import java.security.cert.CRL; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.Collection; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; + +public final class KeyStoreUtil { + private KeyStoreUtil() { + + } + + public static KeyStore createKeyStore(String storeName, String storeType, + char[] storeValue) { + if (storeName == null) { + return null; + } + + File storeFile = new File(storeName); + + try { + if (storeFile.isFile()) { + return createKeyStore(new FileInputStream(storeFile), storeType, storeValue); + } + + ClassLoader classLoader = + Thread.currentThread().getContextClassLoader() == null ? KeyStoreUtil.class.getClassLoader() + : Thread.currentThread().getContextClassLoader(); + URL resource = classLoader.getResource(storeName); + if (resource != null) { + return createKeyStore(resource.openStream(), storeType, storeValue); + } + } catch (IOException e) { + throw new IllegalArgumentException("Bad key store or value." + + e.getMessage()); + } + + return null; + } + + public static KeyStore createKeyStore(InputStream store, String storeType, + char[] storeValue) { + try (InputStream is = store) { + KeyStore keystore = KeyStore.getInstance(storeType); + keystore.load(is, storeValue); + return keystore; + } catch (Exception e) { + throw new IllegalArgumentException("Bad key store or value." + + e.getMessage()); + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public static CRL[] createCRL(String crlfile) { + InputStream is = null; + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + is = new FileInputStream(crlfile); + Collection c = cf.generateCRLs(is); + return (CRL[]) c.toArray(new CRL[0]); + } catch (CertificateException e) { + throw new IllegalArgumentException("bad cert file."); + } catch (FileNotFoundException e) { + throw new IllegalArgumentException("crl file not found."); + } catch (CRLException e) { + throw new IllegalArgumentException("bad crl file."); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + ignore(); + } + } + } + } + + public static KeyManager[] createKeyManagers(final KeyStore keystore, + char[] keyvalue) { + try { + KeyManagerFactory kmfactory = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmfactory.init(keystore, keyvalue); + return kmfactory.getKeyManagers(); + } catch (Exception e) { + throw new IllegalArgumentException("Bad key store." + + e.getMessage()); + } + } + + public static TrustManager[] createTrustManagers(final KeyStore keystore) { + try { + TrustManagerFactory tmfactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmfactory.init(keystore); + return tmfactory.getTrustManagers(); + } catch (Exception e) { + throw new IllegalArgumentException("Bad trust store." + + e.getMessage()); + } + } + + private static void ignore() { + } +} diff --git a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLCustom.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLCustom.java new file mode 100644 index 00000000000..4c116649d84 --- /dev/null +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLCustom.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.foundation.ssl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 和应用相关的信息,方便定制使用。 目前主要包含密码解密、证书路径、IP和Port等内容。 + * + */ +public abstract class SSLCustom { + private static final Logger LOG = LoggerFactory.getLogger(SSLCustom.class); + + public static SSLCustom defaultSSLCustom() { + final SSLCustom custom = new SSLCustom() { + @Override + public char[] decode(char[] encrypted) { + return encrypted; + } + + @Override + public String getFullPath(String filename) { + return filename; + } + }; + return custom; + } + + public static SSLCustom createSSLCustom(String name) { + try { + if (name != null && !name.isEmpty()) { + return (SSLCustom) Class.forName(name).getDeclaredConstructor().newInstance(); + } + } catch (ReflectiveOperationException e) { + LOG.warn("init SSLCustom class failed, name is " + name); + } + return defaultSSLCustom(); + } + + public abstract char[] decode(char[] encrypted); + + public abstract String getFullPath(String filename); + + public String getHost() { + return null; + } +} 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 new file mode 100644 index 00000000000..b7136f7752e --- /dev/null +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLManager.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ssl; + +import java.io.IOException; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509ExtendedTrustManager; + +import org.apache.commons.lang3.StringUtils; + + +/** + * 根据传递的SSLOption构造SSL上下文。请参考JSSE获取相关API的层次参考。 + * + */ +public final class SSLManager { + private SSLManager() { + + } + + public static SSLContext createSSLContext(SSLOption option, SSLCustom custom) { + try { + String keyStoreName = custom.getFullPath(option.getKeyStore()); + char[] keyStoreValue = option.getKeyStoreValue() == null ? new char[0] : + custom.decode(option.getKeyStoreValue().toCharArray()); + KeyStore keyStore = + KeyStoreUtil.createKeyStore(keyStoreName, + option.getKeyStoreType(), + keyStoreValue); + + KeyManager[] keyManager = null; + if (keyStore != null) { + keyManager = + KeyStoreUtil.createKeyManagers(keyStore, keyStoreValue); + } + + String trustStoreName = custom.getFullPath(option.getTrustStore()); + char[] trustStoreValue = option.getTrustStoreValue() == null ? new char[0] : + custom.decode(option.getTrustStoreValue().toCharArray()); + KeyStore trustStore = + KeyStoreUtil.createKeyStore(trustStoreName, + option.getTrustStoreType(), + trustStoreValue); + + TrustManager[] trustManager; + if (trustStore != null) { + trustManager = + KeyStoreUtil.createTrustManagers(trustStore); + } else { + trustManager = new TrustManager[] {new TrustAllManager()}; + } + + TrustManager[] wrapped = new TrustManager[trustManager.length]; + for (int i = 0; i < trustManager.length; i++) { + wrapped[i] = + new TrustManagerExt((X509ExtendedTrustManager) trustManager[i], + option, custom); + } + + // ?: ssl context version + SSLContext context = SSLContext.getInstance("TLS"); + context.init(keyManager, wrapped, new SecureRandom()); + return context; + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException("NoSuchAlgorithmException." + + e.getMessage()); + } catch (KeyManagementException e) { + throw new IllegalArgumentException("KeyManagementException." + + e.getMessage()); + } + } + + public static SSLSocketFactory createSSLSocketFactory(SSLOption option, SSLCustom custom) { + SSLContext context = createSSLContext(option, custom); + SSLSocketFactory factory = context.getSocketFactory(); + String[] supported = factory.getSupportedCipherSuites(); + String[] enabled = option.getCiphers().split(","); + return new SSLSocketFactoryExt(factory, getEnabledCiphers(supported, enabled), + option.getProtocols().split(",")); + } + + public static SSLEngine createSSLEngine(SSLOption option, SSLCustom custom) { + SSLContext context = createSSLContext(option, custom); + SSLEngine engine = + context.createSSLEngine(); + engine.setEnabledProtocols(option.getProtocols().split(",")); + String[] supported = engine.getSupportedCipherSuites(); + String[] enabled = option.getCiphers().split(","); + engine.setEnabledCipherSuites(getEnabledCiphers(supported, enabled)); + setClientAuth(option, engine); + return engine; + } + + public static SSLEngine createSSLEngine(SSLOption option, SSLCustom custom, String peerHost, int peerPort) { + SSLContext context = createSSLContext(option, custom); + SSLEngine engine = + context.createSSLEngine(peerHost, peerPort); + engine.setEnabledProtocols(option.getProtocols().split(",")); + String[] supported = engine.getSupportedCipherSuites(); + String[] enabled = option.getCiphers().split(","); + engine.setEnabledCipherSuites(getEnabledCiphers(supported, enabled)); + setClientAuth(option, engine); + return engine; + } + + private static void setClientAuth(SSLOption option, SSLEngine engine) { + if (option.isAuthPeer() || ClientAuth.REQUIRED.equals(option.getClientAuth())) { + engine.setNeedClientAuth(true); + return; + } + if (ClientAuth.NONE.equals(option.getClientAuth())) { + engine.setNeedClientAuth(false); + engine.setWantClientAuth(false); + return; + } + engine.setWantClientAuth(true); + } + + private static void setClientAuth(SSLOption option, SSLServerSocket serverSocket) { + if (option.isAuthPeer() || ClientAuth.REQUIRED.equals(option.getClientAuth())) { + serverSocket.setNeedClientAuth(true); + return; + } + if (ClientAuth.NONE.equals(option.getClientAuth())) { + serverSocket.setNeedClientAuth(false); + serverSocket.setWantClientAuth(false); + return; + } + serverSocket.setWantClientAuth(true); + } + + public static SSLServerSocket createSSLServerSocket(SSLOption option, + SSLCustom custom) { + try { + SSLContext context = createSSLContext(option, custom); + SSLServerSocketFactory factory = context.getServerSocketFactory(); + SSLServerSocket socket = + (SSLServerSocket) factory.createServerSocket(); + socket.setEnabledProtocols(option.getProtocols().split(",")); + String[] supported = socket.getSupportedCipherSuites(); + String[] enabled = option.getCiphers().split(","); + socket.setEnabledCipherSuites(getEnabledCiphers(supported, enabled)); + setClientAuth(option, socket); + return socket; + } catch (UnknownHostException e) { + throw new IllegalArgumentException("unknown host"); + } catch (IOException e) { + throw new IllegalArgumentException("unable create socket"); + } + } + + public static SSLSocket createSSLSocket(SSLOption option, SSLCustom custom) { + try { + SSLContext context = createSSLContext(option, custom); + SSLSocketFactory factory = context.getSocketFactory(); + SSLSocket socket = + (SSLSocket) factory.createSocket(); + socket.setEnabledProtocols(option.getProtocols().split(",")); + String[] supported = socket.getSupportedCipherSuites(); + String[] enabled = option.getCiphers().split("\\s*,\\s*"); + socket.setEnabledCipherSuites(getEnabledCiphers(supported, enabled)); + return socket; + } catch (UnknownHostException e) { + throw new IllegalArgumentException("unknown host"); + } catch (IOException e) { + throw new IllegalArgumentException("unable create socket"); + } + } + + private static String[] getEnabledCiphers(String[] supported, + String[] enabled) { + String[] result = new String[enabled.length]; + int count = 0; + for (String e : enabled) { + for (String s : supported) { + if (e.equals(s)) { + result[count++] = e; + break; + } + } + } + + if (count == 0) { + throw new IllegalArgumentException("no enabled cipher suits."); + } + + String[] r = new String[count]; + System.arraycopy(result, 0, r, 0, count); + return r; + } + + public static String[] getEnabledCiphers(SSLOption sslOption) { + SSLOption option = new SSLOption(); + if (StringUtils.isNotEmpty(sslOption.getProtocols())) { + option.setProtocols(sslOption.getProtocols()); + } else { + option.setProtocols("TLSv1.2"); + } + option.setCiphers(sslOption.getCiphers()); + SSLCustom custom = SSLCustom.defaultSSLCustom(); + SSLSocket socket = createSSLSocket(option, custom); + return socket.getEnabledCipherSuites(); + } +} 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 new file mode 100644 index 00000000000..19160bb3927 --- /dev/null +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOption.java @@ -0,0 +1,451 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ssl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +import org.springframework.core.env.Environment; + +/** + * SSL配置选项。 + * + */ +public final class SSLOption { + public static final SSLOption DEFAULT_OPTION = new SSLOption(); + + public static final String DEFAULT_CIPHERS = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384," + + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"; + + static { + DEFAULT_OPTION.setEngine("jdk"); + DEFAULT_OPTION.setProtocols("TLSv1.2"); + DEFAULT_OPTION.setCiphers(DEFAULT_CIPHERS); + DEFAULT_OPTION.setAuthPeer(false); + DEFAULT_OPTION.setCheckCNHost(false); + DEFAULT_OPTION.setCheckCNWhite(false); + DEFAULT_OPTION.setCheckCNWhiteFile("white.list"); + DEFAULT_OPTION.setAllowRenegotiate(true); + DEFAULT_OPTION.setStorePath("internal"); + DEFAULT_OPTION.setTrustStore("trust.jks"); + DEFAULT_OPTION.setTrustStoreType("JKS"); + DEFAULT_OPTION.setTrustStoreValue("trustStoreValue"); + DEFAULT_OPTION.setKeyStore("server.p12"); + DEFAULT_OPTION.setKeyStoreType("PKCS12"); + DEFAULT_OPTION.setKeyStoreValue("keyStoreValue"); + DEFAULT_OPTION.setCrl("revoke.crl"); + } + + private String engine; + + private String protocols; + + private String ciphers; + + private boolean authPeer; + + private boolean checkCNHost; + + private boolean checkCNWhite; + + private String checkCNWhiteFile; + + private boolean allowRenegotiate; + + private String clientAuth; + + private String storePath; + + private String trustStore; + + private String trustStoreType; + + private String trustStoreValue; + + private String keyStore; + + private String keyStoreType; + + private String keyStoreValue; + + private String crl; + + private String sslCustomClass; + + public String getEngine() { + return engine; + } + + public void setEngine(String engine) { + this.engine = engine; + } + + public void setProtocols(String protocols) { + this.protocols = protocols; + } + + public void setCiphers(String ciphers) { + this.ciphers = ciphers; + } + + public void setAuthPeer(boolean authPeer) { + this.authPeer = authPeer; + } + + public void setCheckCNHost(boolean checkCNHost) { + this.checkCNHost = checkCNHost; + } + + public void setCheckCNWhite(boolean checkCNWhite) { + this.checkCNWhite = checkCNWhite; + } + + public void setCheckCNWhiteFile(String checkCNWhiteFile) { + this.checkCNWhiteFile = checkCNWhiteFile; + } + + public void setAllowRenegotiate(boolean allowRenegotiate) { + this.allowRenegotiate = allowRenegotiate; + } + + public void setStorePath(String storePath) { + this.storePath = storePath; + } + + public void setTrustStore(String trustStore) { + this.trustStore = trustStore; + } + + public void setTrustStoreType(String trustStoreType) { + this.trustStoreType = trustStoreType; + } + + public void setTrustStoreValue(String trustStoreValue) { + this.trustStoreValue = trustStoreValue; + } + + public void setKeyStore(String keyStore) { + this.keyStore = keyStore; + } + + public void setKeyStoreType(String keyStoreType) { + this.keyStoreType = keyStoreType; + } + + public void setKeyStoreValue(String keyStoreValue) { + this.keyStoreValue = keyStoreValue; + } + + public void setCrl(String crl) { + this.crl = crl; + } + + public String getProtocols() { + return protocols; + } + + public String getCiphers() { + return ciphers; + } + + public boolean isAuthPeer() { + return authPeer; + } + + public boolean isCheckCNHost() { + return checkCNHost; + } + + public boolean isCheckCNWhite() { + return checkCNWhite; + } + + public String getCheckCNWhiteFile() { + return checkCNWhiteFile; + } + + public boolean isAllowRenegotiate() { + return allowRenegotiate; + } + + public String getStorePath() { + return storePath; + } + + public String getClientAuth() { + return this.clientAuth; + } + + public void setClientAuth(String clientAuth) { + this.clientAuth = clientAuth; + } + + public String getTrustStore() { + return trustStore; + } + + public String getTrustStoreType() { + return trustStoreType; + } + + public String getTrustStoreValue() { + return trustStoreValue; + } + + public String getKeyStore() { + return keyStore; + } + + public String getKeyStoreType() { + return keyStoreType; + } + + public String getKeyStoreValue() { + return keyStoreValue; + } + + public String getCrl() { + return crl; + } + + public static SSLOption build(String optionfile) { + File file = new File(optionfile); + if (!file.isFile()) { + throw new IllegalArgumentException("Bad file name."); + } + + try { + SSLOption option = new SSLOption(); + option.load(file.getCanonicalPath()); + return option; + } catch (IOException e) { + throw new IllegalArgumentException("Bad file name."); + } + } + + public static SSLOption build(InputStream inputStream) { + SSLOption option = new SSLOption(); + option.load(inputStream); + return option; + } + + public static String getStringProperty(Environment environment, String defaultValue, + String... keys) { + String property = null; + for (String key : keys) { + property = environment.getProperty(key); + if (property != null) { + break; + } + } + + if (property != null) { + return property; + } else { + return defaultValue; + } + } + + private static boolean getBooleanProperty(Environment environment, boolean defaultValue, + String... keys) { + String property = null; + for (String key : keys) { + property = environment.getProperty(key); + if (property != null) { + break; + } + } + + if (property != null) { + return Boolean.parseBoolean(property); + } else { + return defaultValue; + } + } + + public static SSLOption build(String tag, Environment environment) { + SSLOption option = new SSLOption(); + option.engine = getStringProperty(environment, + DEFAULT_OPTION.getEngine(), + "ssl." + tag + ".engine", + "ssl.engine"); + option.protocols = + getStringProperty(environment, + DEFAULT_OPTION.getProtocols(), + "ssl." + tag + ".protocols", + "ssl.protocols"); + option.ciphers = + getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".ciphers", "ssl.ciphers"); + option.authPeer = + getBooleanProperty(environment, DEFAULT_OPTION.isAuthPeer(), "ssl." + tag + ".authPeer", "ssl.authPeer"); + option.checkCNHost = + getBooleanProperty(environment, + DEFAULT_OPTION.isCheckCNHost(), + "ssl." + tag + ".checkCN.host", + "ssl.checkCN.host"); + option.checkCNWhite = + getBooleanProperty(environment, + DEFAULT_OPTION.isCheckCNWhite(), + "ssl." + tag + ".checkCN.white", + "ssl.checkCN.white"); + option.checkCNWhiteFile = getStringProperty(environment, + DEFAULT_OPTION.getCiphers(), + "ssl." + tag + ".checkCN.white.file", + "ssl.checkCN.white.file"); + option.allowRenegotiate = getBooleanProperty(environment, + DEFAULT_OPTION.isAllowRenegotiate(), + "ssl." + tag + ".allowRenegotiate", + "ssl.allowRenegotiate"); + option.storePath = + getStringProperty(environment, + DEFAULT_OPTION.getStorePath(), + "ssl." + tag + ".storePath", + "ssl.storePath"); + option.clientAuth = + getStringProperty(environment, + DEFAULT_OPTION.getClientAuth(), + "ssl." + tag + ".storePath", + "ssl.clientAuth"); + option.trustStore = + getStringProperty(environment, + DEFAULT_OPTION.getTrustStore(), + "ssl." + tag + ".trustStore", + "ssl.trustStore"); + option.trustStoreType = getStringProperty(environment, + DEFAULT_OPTION.getTrustStoreType(), + "ssl." + tag + ".trustStoreType", + "ssl.trustStoreType"); + option.trustStoreValue = getStringProperty(environment, + DEFAULT_OPTION.getTrustStoreValue(), + "ssl." + tag + ".trustStoreValue", + "ssl.trustStoreValue"); + option.keyStore = + getStringProperty(environment, DEFAULT_OPTION.getKeyStore(), "ssl." + tag + ".keyStore", "ssl.keyStore"); + option.keyStoreType = + getStringProperty(environment, + DEFAULT_OPTION.getKeyStoreType(), + "ssl." + tag + ".keyStoreType", + "ssl.keyStoreType"); + option.keyStoreValue = getStringProperty(environment, + DEFAULT_OPTION.getKeyStoreValue(), + "ssl." + tag + ".keyStoreValue", + "ssl.keyStoreValue"); + option.crl = getStringProperty(environment, DEFAULT_OPTION.getCrl(), "ssl." + tag + ".crl", "ssl.crl"); + option.sslCustomClass = + getStringProperty(environment, null, "ssl." + tag + ".sslCustomClass", "ssl.sslCustomClass"); + return option; + } + + private void fromProperty(Properties props) { + this.protocols = propString(props, "ssl.protocols"); + this.ciphers = propString(props, "ssl.ciphers"); + this.authPeer = propBoolean(props, "ssl.authPeer"); + this.checkCNHost = propBoolean(props, "ssl.checkCN.host"); + this.checkCNWhite = propBoolean(props, "ssl.checkCN.white"); + this.checkCNWhiteFile = propString(props, "ssl.checkCN.white.file"); + 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"); + this.trustStoreType = propString(props, "ssl.trustStoreType"); + this.trustStoreValue = propString(props, "ssl.trustStoreValue"); + this.keyStore = propString(props, "ssl.keyStore"); + this.keyStoreType = propString(props, "ssl.keyStoreType"); + this.keyStoreValue = propString(props, "ssl.keyStoreValue"); + this.crl = propString(props, "ssl.crl"); + this.sslCustomClass = props.getProperty("ssl.sslCustomClass"); + } + + private String propString(Properties props, String key) { + return propString(props, key, true); + } + + private String propString(Properties props, String key, boolean required) { + String s = props.getProperty(key); + if (s == null && required) { + throw new IllegalArgumentException("No key :" + key); + } + return s; + } + + private boolean propBoolean(Properties props, String key) { + String s = props.getProperty(key); + if (s == null) { + throw new IllegalArgumentException("No key :" + key); + } + return Boolean.parseBoolean(s); + } + + private void load(InputStream inputStream) { + Properties props = new Properties(); + Reader reader = null; + try { + reader = + new InputStreamReader(inputStream, StandardCharsets.UTF_8); + props.load(reader); + fromProperty(props); + } catch (IOException e) { + throw new IllegalArgumentException( + "Can not read ssl client config file"); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + ignore(); + } + } + + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + ignore(); + } + } + } + } + + // visible for testing + void load(String path) { + try { + load(new FileInputStream(path)); + } catch (FileNotFoundException e) { + throw new IllegalArgumentException( + "Can not read ssl client config file: " + path); + } + } + + private void ignore() { + } + + public String getSslCustomClass() { + return this.sslCustomClass; + } + + public void setSslCustomClass(String sslCustomClass) { + this.sslCustomClass = sslCustomClass; + } +} 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 new file mode 100644 index 00000000000..49cdb3ab15b --- /dev/null +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOptionFactory.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.foundation.ssl; + +import org.springframework.core.env.Environment; + +public interface SSLOptionFactory { + static SSLOptionFactory createSSLOptionFactory(String tag, Environment environment) { + String name = SSLOption.getStringProperty(environment, + null, + "ssl." + tag + ".sslOptionFactory", + "ssl.sslOptionFactory"); + return createSSLOptionFactory(name); + } + + static SSLOptionFactory createSSLOptionFactory(String className) { + if (className != null && !className.isEmpty()) { + try { + return (SSLOptionFactory) Class.forName(className).getDeclaredConstructor().newInstance(); + } catch (ReflectiveOperationException e) { + throw new IllegalStateException("Failed to create SSLOptionFactory.", e); + } + } + return null; + } + + SSLOption createSSLOption(); +} diff --git a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLSocketFactoryExt.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLSocketFactoryExt.java new file mode 100644 index 00000000000..e0cc0f24d8d --- /dev/null +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLSocketFactoryExt.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.ssl; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +/** + * 扩展SSLSocketFactory,设置算法和协议列表。 + * + */ +public class SSLSocketFactoryExt extends SSLSocketFactory { + private final SSLSocketFactory sslSocketFactory; + + private final String[] ciphers; + + private final String[] protos; + + public SSLSocketFactoryExt(SSLSocketFactory factory, String[] ciphers, String[] protos) { + this.sslSocketFactory = factory; + this.ciphers = ciphers; + this.protos = protos; + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(s, host, port, autoClose)); + } + + @Override + public String[] getDefaultCipherSuites() { + return this.sslSocketFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return this.sslSocketFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, + int localPort) throws IOException, UnknownHostException { + return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(host, + port, + localHost, + localPort)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, + int localPort) throws IOException { + return wrapSocket((SSLSocket) this.sslSocketFactory.createSocket(address, + port, + localAddress, + localPort)); + } + + private SSLSocket wrapSocket(SSLSocket socket) { + socket.setEnabledProtocols(protos); + socket.setEnabledCipherSuites(ciphers); + return socket; + } +} diff --git a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustAllManager.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustAllManager.java new file mode 100644 index 00000000000..b978ede4b9d --- /dev/null +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustAllManager.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.foundation.ssl; + +import java.net.Socket; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.X509ExtendedTrustManager; + +/** it is not secure */ +public class TrustAllManager extends X509ExtendedTrustManager { + + @Override + public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + } +} diff --git a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/TrustManagerExt.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustManagerExt.java similarity index 91% rename from foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/TrustManagerExt.java rename to foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustManagerExt.java index 6ca01b75381..597894571c5 100644 --- a/foundations/foundation-ssl/src/main/java/io/servicecomb/foundation/ssl/TrustManagerExt.java +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustManagerExt.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +15,7 @@ * limitations under the License. */ -package io.servicecomb.foundation.ssl; +package org.apache.servicecomb.foundation.ssl; import java.io.File; import java.io.FileInputStream; @@ -49,11 +50,11 @@ public class TrustManagerExt extends X509ExtendedTrustManager { private static final int WHITE_SIZE = 1024; - private X509ExtendedTrustManager trustManager; + private final X509ExtendedTrustManager trustManager; - private SSLOption option; + private final SSLOption option; - private SSLCustom custom; + private final SSLCustom custom; public TrustManagerExt(X509ExtendedTrustManager manager, SSLOption option, SSLCustom custom) { @@ -188,14 +189,14 @@ 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; } - LOG.error("CN does not match IP: e=" + cns.toString() + LOG.error("CN does not match IP: e=" + cns + ",t=" + ip); - throw new CertificateException("CN does not match IP: e=" + cns.toString() + throw new CertificateException("CN does not match IP: e=" + cns + ",t=" + ip); } } diff --git a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/KeyStoreUtilTest.java b/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/KeyStoreUtilTest.java deleted file mode 100644 index 0d2bb0f03a0..00000000000 --- a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/KeyStoreUtilTest.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.security.KeyStore; -import java.security.cert.CRL; -import java.security.cert.CRLException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.util.Collection; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import mockit.Mock; -import mockit.MockUp; - -public class KeyStoreUtilTest { - final String strFilePath = Thread.currentThread().getContextClassLoader().getResource("").getPath(); - - @Test - public void testCreateKeyStoreException() { - String storename = ""; - String storetype = "testType"; - char[] storevalue = "Changeme_123".toCharArray(); - - try { - KeyStoreUtil.createKeyStore(storename, storetype, storevalue); - } catch (IllegalArgumentException e) { - Assert.assertEquals("Bad key store or value.testType not found", e.getMessage()); - } - } - - @Test - public void testCreateKeyStoreException2() { - String storename = strFilePath + "/ssl/trust.jks"; - String storetype = "PKCS12"; - char[] storevalue = "Changeme_123".toCharArray(); - - try { - KeyStoreUtil.createKeyStore(storename, storetype, storevalue); - } catch (IllegalArgumentException e) { - Assert.assertEquals("Bad key store or value.DerInputStream.getLength(): lengthTag=109, too big.", - e.getMessage()); - } - } - - @SuppressWarnings("unused") - @Test - public void testCreateKeyManagersException() { - KeyStore keystore; - char[] keyvalue; - - String storename = strFilePath + "/ssl/server.p12"; - String storetype = "PKCS12"; - char[] storevalue = "Changeme_123".toCharArray(); - - keystore = KeyStoreUtil.createKeyStore(storename, storetype, storevalue); - - char[] storeKeyValue = null; - try { - KeyStoreUtil.createKeyManagers(keystore, storeKeyValue); - } catch (IllegalArgumentException e) { - Assert.assertEquals("Bad key store.Get Key failed: null", - e.getMessage()); - } - } - - @Test - public void testCreateCRL() { - String crlfile = strFilePath + "/ssl/server.p12"; - mockGenerateCRLs(); - boolean validAssert = true; - try { - CRL[] crl = KeyStoreUtil.createCRL(crlfile); - Assert.assertNull(crl); - } catch (Exception e) { - validAssert = false; - } - Assert.assertTrue(validAssert); - } - - @Test - public void testCreateCRLException() { - String crlfile = strFilePath + "/ssl/server.p12"; - boolean validAssert = true; - try { - new MockUp() { - @Mock - public final CertificateFactory getInstance(String type) throws CertificateException { - throw new CertificateException(); - } - }; - - KeyStoreUtil.createCRL(crlfile); - } catch (Exception e) { - validAssert = false; - } - Assert.assertFalse(validAssert); - } - - @Test - public void testExceptionFileNotFound() { - String crlfile = strFilePath + "/ssl/server.p12"; - boolean validAssert = true; - try { - new MockUp() { - @Mock - public final CertificateFactory getInstance( - String type) throws CertificateException, FileNotFoundException { - throw new FileNotFoundException(); - } - }; - KeyStoreUtil.createCRL(crlfile); - } catch (Exception e) { - validAssert = false; - Assert.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); - } - Assert.assertFalse(validAssert); - } - - @Test - public void testExceptionCRLException() { - String crlfile = strFilePath + "/ssl/server.p12"; - boolean validAssert = true; - try { - new MockUp() { - @Mock - public final CertificateFactory getInstance(String type) throws CertificateException, CRLException { - throw new CRLException(); - } - }; - KeyStoreUtil.createCRL(crlfile); - } catch (Exception e) { - validAssert = false; - Assert.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); - } - Assert.assertFalse(validAssert); - } - - private void mockGenerateCRLs() { - new MockUp() { - @SuppressWarnings("unchecked") - @Mock - public final Collection generateCRLs(InputStream inStream) throws CRLException { - return Mockito.mock(Collection.class); - } - }; - } -} diff --git a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/MyOptionFactory.java b/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/MyOptionFactory.java deleted file mode 100644 index 007d4190769..00000000000 --- a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/MyOptionFactory.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -public class MyOptionFactory implements SSLOptionFactory { - - @Override - public SSLOption createSSLOption() { - SSLOption option = new SSLOption(); - option.setProtocols("TLSv1.2"); - option.setCiphers("TLS_RSA_WITH_AES_128_CBC_SHA256"); - option.setAuthPeer(false); - option.setCheckCNHost(false); - option.setCheckCNWhite(false); - option.setCheckCNWhiteFile("white.list"); - return option; - } -} diff --git a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/SSLManagerTest.java b/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/SSLManagerTest.java deleted file mode 100644 index 9eb521442cc..00000000000 --- a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/SSLManagerTest.java +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -import java.io.IOException; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.NetworkInterface; -import java.net.UnknownHostException; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.util.Enumeration; - -import javax.net.ssl.HandshakeCompletedEvent; -import javax.net.ssl.HandshakeCompletedListener; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; - -import org.junit.Assert; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class SSLManagerTest { - private final String DIR = Thread.currentThread().getContextClassLoader().getResource("").getPath(); - - @Test - public void testSSLManagerServerAndClient(final @Mocked NetworkInterface nif) throws Exception { - final InetAddress ia = Inet4Address.getByName("10.57.65.225"); - final Enumeration interfaces = new Enumeration() { - int count = 1; - - int cur = 0; - - @Override - public boolean hasMoreElements() { - if (cur < count) { - cur++; - return true; - } - return false; - } - - @Override - public NetworkInterface nextElement() { - return nif; - } - }; - - final Enumeration ias = new Enumeration() { - int count = 1; - - int cur = 0; - - @Override - public boolean hasMoreElements() { - if (cur < count) { - cur++; - return true; - } - return false; - } - - @Override - public InetAddress nextElement() { - return ia; - } - }; - - new Expectations() { - @Mocked - NetworkInterface nif; - - { - NetworkInterface.getNetworkInterfaces(); - result = interfaces; - } - }; - new Expectations() { - { - nif.getInetAddresses(); - result = ias; - ia.getHostAddress(); - result = "10.57.65.225"; - } - }; - - SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); - SSLCustom custom = new SSLCustom() { - @Override - public String getFullPath(String filename) { - return DIR + "/ssl/" + filename; - } - - @Override - public char[] decode(char[] encrypted) { - return encrypted; - } - }; - final SSLServerSocket serverSocket = SSLManager.createSSLServerSocket(option, custom); - serverSocket.bind(new InetSocketAddress("127.0.0.1", 8886)); - String[] protos = serverSocket.getEnabledCipherSuites(); - String[] protosExpected = - "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" - .split(","); - Assert.assertArrayEquals(protos, protosExpected); - String[] ciphers = serverSocket.getEnabledCipherSuites(); - String[] ciphersExpected = - "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" - .split(","); - Assert.assertArrayEquals(ciphers, ciphersExpected); - Assert.assertEquals(serverSocket.getNeedClientAuth(), true); - - SSLOption clientoption = SSLOption.build(DIR + "/client.ssl.properties"); - SSLSocket clientsocket = SSLManager.createSSLSocket(clientoption, custom); - String[] clientprotos = clientsocket.getEnabledCipherSuites(); - String[] clientprotosExpected = - "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" - .split(","); - Assert.assertArrayEquals(clientprotos, clientprotosExpected); - String[] clientciphers = clientsocket.getEnabledCipherSuites(); - String[] clientciphersExpected = - "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" - .split(","); - Assert.assertArrayEquals(clientciphers, clientciphersExpected); - Assert.assertEquals(clientsocket.getNeedClientAuth(), false); - boolean validAssert = true; - try { - clientsocket.connect(new InetSocketAddress("127.0.0.1", 8886)); - - new Thread() { - public void run() { - - try { - SSLSocket s = (SSLSocket) serverSocket.accept(); - s.addHandshakeCompletedListener(new HandshakeCompletedListener() { - - @Override - public void handshakeCompleted(HandshakeCompletedEvent arg0) { - - } - }); - s.getOutputStream().write(new byte[] {0, 1}); - } catch (IOException e) { - e.printStackTrace(); - // this should not happen, do a false assert - Assert.assertEquals(false, true); - } - } - }.start(); - - clientsocket.startHandshake(); - clientsocket.close(); - serverSocket.close(); - - // socked successfully opened and closed - } catch (Exception e) { - e.printStackTrace(); - validAssert = false; - } - Assert.assertTrue(validAssert); - } - - @Test - public void testCreateSSLEngine() { - SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); - SSLCustom custom = new SSLCustom() { - @Override - public String getFullPath(String filename) { - return DIR + "/ssl/" + filename; - } - - @Override - public char[] decode(char[] encrypted) { - return encrypted; - } - }; - - SSLEngine aSSLEngine = SSLManager.createSSLEngine(option, custom); - Assert.assertEquals(false, aSSLEngine.getUseClientMode()); - Assert.assertNotNull(aSSLEngine); - } - - @Test - public void testCreateSSLEnginewithPort() { - SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); - SSLCustom custom = new SSLCustom() { - @Override - public String getFullPath(String filename) { - return DIR + "/ssl/" + filename; - } - - @Override - public char[] decode(char[] encrypted) { - return encrypted; - } - }; - - int port = 39093; - String peerHost = "host1"; - SSLEngine aSSLEngine = SSLManager.createSSLEngine(option, custom, peerHost, port); - Assert.assertNotNull(aSSLEngine); - Assert.assertEquals("host1", aSSLEngine.getPeerHost().toString()); - } - - @Test - public void testCreateSSLContextException() { - SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); - - SSLCustom custom = new SSLCustom() { - @Override - public String getFullPath(String filename) { - return DIR + "/ssl/" + filename; - } - - @Override - public char[] decode(char[] encrypted) { - return encrypted; - } - }; - - new MockUp() { - @Mock - public final SSLContext getInstance(String type) throws NoSuchAlgorithmException { - throw new NoSuchAlgorithmException(); - } - }; - - try { - SSLContext context = SSLManager.createSSLContext(option, custom); - Assert.assertNotNull(context); - } catch (Exception e) { - Assert.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); - } - } - - @Test - public void testCreateSSLContextKeyManagementException() { - SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); - - SSLCustom custom = new SSLCustom() { - @Override - public String getFullPath(String filename) { - return DIR + "/ssl/" + filename; - } - - @Override - public char[] decode(char[] encrypted) { - return encrypted; - } - }; - - new MockUp() { - @Mock - public final SSLContext getInstance(String type) throws KeyManagementException { - throw new KeyManagementException(); - } - }; - - try { - SSLContext context = SSLManager.createSSLContext(option, custom); - Assert.assertNotNull(context); - } catch (Exception e) { - Assert.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); - } - } - - @Test - public void testCreateSSLServerSocketException() { - SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); - - SSLCustom custom = new SSLCustom() { - @Override - public String getFullPath(String filename) { - return DIR + "/ssl/" + filename; - } - - @Override - public char[] decode(char[] encrypted) { - return encrypted; - } - }; - - new MockUp() { - @Mock - public final SSLContext getInstance(String type) throws UnknownHostException { - throw new UnknownHostException(); - } - }; - - try { - SSLServerSocket context = SSLManager.createSSLServerSocket(option, custom); - - Assert.assertNotNull(context); - } catch (Exception e) { - Assert.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); - } - } - - @Test - public void testCreateSSLServerSocketIOException() { - SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); - - SSLCustom custom = new SSLCustom() { - @Override - public String getFullPath(String filename) { - return DIR + "/ssl/" + filename; - } - - @Override - public char[] decode(char[] encrypted) { - return encrypted; - } - }; - - new MockUp() { - @Mock - public final SSLContext getInstance(String type) throws IOException { - throw new IOException(); - } - }; - - try { - SSLServerSocket context = SSLManager.createSSLServerSocket(option, custom); - Assert.assertNotNull(context); - } catch (Exception e) { - Assert.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); - } - } - - @Test - public void testCreateSSLSocketException() { - SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); - - SSLCustom custom = new SSLCustom() { - @Override - public String getFullPath(String filename) { - return DIR + "/ssl/" + filename; - } - - @Override - public char[] decode(char[] encrypted) { - return encrypted; - } - }; - - new MockUp() { - @Mock - public final SSLContext getInstance(String type) throws UnknownHostException { - throw new UnknownHostException(); - } - }; - - try { - SSLSocket context = SSLManager.createSSLSocket(option, custom); - Assert.assertNotNull(context); - } catch (Exception e) { - Assert.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); - } - } - - @Test - public void testCreateSSLSocketIOException() { - SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); - - SSLCustom custom = new SSLCustom() { - @Override - public String getFullPath(String filename) { - return DIR + "/ssl/" + filename; - } - - @Override - public char[] decode(char[] encrypted) { - return encrypted; - } - }; - - new MockUp() { - @Mock - public final SSLContext getInstance(String type) throws IOException { - throw new IOException(); - } - }; - - try { - SSLSocket context = SSLManager.createSSLSocket(option, custom); - Assert.assertNotNull(context); - } catch (Exception e) { - Assert.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); - } - } - - @Test - public void testCreateSSLSocketFactory() { - SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); - SSLCustom custom = new SSLCustom() { - @Override - public String getFullPath(String filename) { - return DIR + "/ssl/" + filename; - } - - @Override - public char[] decode(char[] encrypted) { - return encrypted; - } - }; - - SSLSocketFactory aSSLSocketFactory = SSLManager.createSSLSocketFactory(option, custom); - Assert.assertNotNull(aSSLSocketFactory.getDefaultCipherSuites()[0]); - } - - public void testGetSupportedCiphers() { - String[] ciphers = SSLManager.getEnalbedCiphers("TLS_RSA_WITH_AES_128_GCM_SHA256"); - Assert.assertEquals(ciphers[0], "TLS_RSA_WITH_AES_128_GCM_SHA256"); - } -} diff --git a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/SSLOptionTest.java b/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/SSLOptionTest.java deleted file mode 100644 index 91f5035e140..00000000000 --- a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/SSLOptionTest.java +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -import static io.servicecomb.config.archaius.sources.ConfigSourceMaker.yamlConfigSource; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.net.URL; -import java.util.Properties; - -import org.apache.commons.configuration.SystemConfiguration; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Test; - -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 io.servicecomb.config.archaius.scheduler.NeverStartPollingScheduler; -import mockit.Deencapsulation; -import mockit.Mock; -import mockit.MockUp; - -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); - } - - @Test - public void testSSLOption() { - - SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); - - String protocols = option.getProtocols(); - option.setProtocols(protocols); - Assert.assertEquals("TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello", protocols); - - String ciphers = option.getCiphers(); - option.setCiphers(ciphers); - Assert.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", - ciphers); - - boolean authPeer = option.isAuthPeer(); - option.setAuthPeer(authPeer); - Assert.assertEquals(true, authPeer); - - boolean checkCNHost = option.isCheckCNHost(); - option.setCheckCNHost(checkCNHost); - Assert.assertEquals(true, checkCNHost); - - boolean checkCNWhite = option.isCheckCNWhite(); - option.setCheckCNWhite(checkCNWhite); - Assert.assertEquals(true, checkCNWhite); - - String checkCNWhiteFile = option.getCheckCNWhiteFile(); - option.setCheckCNWhiteFile(checkCNWhiteFile); - Assert.assertEquals("white.list", checkCNWhiteFile); - - boolean allowRenegociate = option.isAllowRenegociate(); - option.setAllowRenegociate(allowRenegociate); - Assert.assertEquals(false, allowRenegociate); - - String storePath = option.getStorePath(); - option.setStorePath(storePath); - Assert.assertEquals("internal", storePath); - - String trustStore = option.getTrustStore(); - option.setTrustStore(trustStore); - Assert.assertEquals("trust.jks", trustStore); - - String trustStoreType = option.getTrustStoreType(); - option.setTrustStoreType(trustStoreType); - Assert.assertEquals("JKS", trustStoreType); - - String trustStoreValue = option.getTrustStoreValue(); - option.setTrustStoreValue(trustStoreValue); - Assert.assertEquals("Changeme_123", trustStoreValue); - - String keyStore = option.getKeyStore(); - option.setKeyStore(keyStore); - Assert.assertEquals("server.p12", keyStore); - - String keyStoreType = option.getKeyStoreType(); - option.setKeyStoreType(keyStoreType); - Assert.assertEquals("PKCS12", keyStoreType); - - String keyStoreValue = option.getKeyStoreValue(); - option.setKeyStoreValue(keyStoreValue); - Assert.assertEquals("Changeme_123", keyStoreValue); - - String crl = option.getCrl(); - option.setCrl(crl); - Assert.assertEquals("revoke.crl", crl); - } - - @Test - public void testSSLOptionYaml() { - // configuration from yaml files: default microservice.yaml - DynamicConfiguration configFromYamlFile = - new DynamicConfiguration(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"); - - String protocols = option.getProtocols(); - option.setProtocols(protocols); - Assert.assertEquals("TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello", protocols); - - String ciphers = option.getCiphers(); - option.setCiphers(ciphers); - Assert.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", - ciphers); - - boolean authPeer = option.isAuthPeer(); - option.setAuthPeer(authPeer); - Assert.assertEquals(true, authPeer); - - boolean checkCNHost = option.isCheckCNHost(); - option.setCheckCNHost(checkCNHost); - Assert.assertEquals(true, checkCNHost); - - boolean checkCNWhite = option.isCheckCNWhite(); - option.setCheckCNWhite(checkCNWhite); - Assert.assertEquals(true, checkCNWhite); - - String checkCNWhiteFile = option.getCheckCNWhiteFile(); - option.setCheckCNWhiteFile(checkCNWhiteFile); - Assert.assertEquals("white.list", checkCNWhiteFile); - - boolean allowRenegociate = option.isAllowRenegociate(); - option.setAllowRenegociate(allowRenegociate); - Assert.assertEquals(false, allowRenegociate); - - String storePath = option.getStorePath(); - option.setStorePath(storePath); - Assert.assertEquals("internal", storePath); - - String trustStore = option.getTrustStore(); - option.setTrustStore(trustStore); - Assert.assertEquals("trust.jks", trustStore); - - String trustStoreType = option.getTrustStoreType(); - option.setTrustStoreType(trustStoreType); - Assert.assertEquals("JKS", trustStoreType); - - String trustStoreValue = option.getTrustStoreValue(); - option.setTrustStoreValue(trustStoreValue); - Assert.assertEquals("Changeme_123", trustStoreValue); - - String keyStore = option.getKeyStore(); - option.setKeyStore(keyStore); - Assert.assertEquals("server.p12", keyStore); - - String keyStoreType = option.getKeyStoreType(); - option.setKeyStoreType(keyStoreType); - Assert.assertEquals("PKCS12", keyStoreType); - - String keyStoreValue = option.getKeyStoreValue(); - option.setKeyStoreValue(keyStoreValue); - Assert.assertEquals("Changeme_123", keyStoreValue); - - String crl = option.getCrl(); - option.setCrl(crl); - Assert.assertEquals("revoke.crl", crl); - - option.setSslCustomClass("123"); - - SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); - Assert.assertArrayEquals(custom.decode("123".toCharArray()), "123".toCharArray()); - } - - @Test - public void testSSLOptionYamlOption2() throws Exception { - System.setProperty("ssl.protocols", "TLSv1.2"); - DynamicConfiguration configFromYamlFile = - new DynamicConfiguration(yamlConfigSource(), new NeverStartPollingScheduler()); - // configuration from system properties - ConcurrentMapConfiguration configFromSystemProperties = - new ConcurrentMapConfiguration(new SystemConfiguration()); - ConcurrentCompositeConfiguration finalConfig = new ConcurrentCompositeConfiguration(); - finalConfig.addConfiguration(configFromSystemProperties, "systemEnvConfig"); - finalConfig.addConfiguration(configFromYamlFile, "configFromYamlFile"); - - SSLOption option = SSLOption.buildFromYaml("server", finalConfig); - - String protocols = option.getProtocols(); - option.setProtocols(protocols); - Assert.assertEquals("TLSv1.2", protocols); - System.clearProperty("ssl.protocols"); - } - - @Test - public void testSSLOptionYamlOption() throws Exception { - DynamicConfiguration configFromYamlFile = - new DynamicConfiguration(yamlConfigSource(), new NeverStartPollingScheduler()); - // configuration from system properties - ConcurrentMapConfiguration configFromSystemProperties = - new ConcurrentMapConfiguration(new SystemConfiguration()); - ConcurrentCompositeConfiguration finalConfig = new ConcurrentCompositeConfiguration(); - finalConfig.addConfiguration(configFromSystemProperties, "systemEnvConfig"); - finalConfig.addConfiguration(configFromYamlFile, "configFromYamlFile"); - - SSLOption option = SSLOption.buildFromYaml("server", finalConfig); - - String protocols = option.getProtocols(); - option.setProtocols(protocols); - Assert.assertEquals("TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello", protocols); - - String ciphers = option.getCiphers(); - option.setCiphers(ciphers); - Assert.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", - ciphers); - - boolean authPeer = option.isAuthPeer(); - option.setAuthPeer(authPeer); - Assert.assertEquals(true, authPeer); - - boolean checkCNHost = option.isCheckCNHost(); - option.setCheckCNHost(checkCNHost); - Assert.assertEquals(true, checkCNHost); - - boolean checkCNWhite = option.isCheckCNWhite(); - option.setCheckCNWhite(checkCNWhite); - Assert.assertEquals(true, checkCNWhite); - - String checkCNWhiteFile = option.getCheckCNWhiteFile(); - option.setCheckCNWhiteFile(checkCNWhiteFile); - Assert.assertEquals("white.list", checkCNWhiteFile); - - boolean allowRenegociate = option.isAllowRenegociate(); - option.setAllowRenegociate(allowRenegociate); - Assert.assertEquals(false, allowRenegociate); - - String storePath = option.getStorePath(); - option.setStorePath(storePath); - Assert.assertEquals("internal", storePath); - - String trustStore = option.getTrustStore(); - option.setTrustStore(trustStore); - Assert.assertEquals("trust.jks", trustStore); - - String trustStoreType = option.getTrustStoreType(); - option.setTrustStoreType(trustStoreType); - Assert.assertEquals("JKS", trustStoreType); - - String trustStoreValue = option.getTrustStoreValue(); - option.setTrustStoreValue(trustStoreValue); - Assert.assertEquals("Changeme_123", trustStoreValue); - - String keyStore = option.getKeyStore(); - option.setKeyStore(keyStore); - Assert.assertEquals("server.p12", keyStore); - - String keyStoreType = option.getKeyStoreType(); - option.setKeyStoreType(keyStoreType); - Assert.assertEquals("PKCS12", keyStoreType); - - String keyStoreValue = option.getKeyStoreValue(); - option.setKeyStoreValue(keyStoreValue); - Assert.assertEquals("Changeme_123", keyStoreValue); - - String crl = option.getCrl(); - option.setCrl(crl); - Assert.assertEquals("revoke.crl", crl); - - option.setSslCustomClass("123"); - - SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); - Assert.assertArrayEquals(custom.decode("123".toCharArray()), "123".toCharArray()); - } - - @SuppressWarnings("unused") - @Test - public void testSSLOptionNull() { - try { - SSLOption option = SSLOption.build(DIR + "/servers.ssl.properties"); - } catch (IllegalArgumentException e) { - Assert.assertEquals("Bad file name.", e.getMessage()); - } - } - - @Test - public void testBuildException() { - new MockUp() { - @Mock - public String getCanonicalPath() throws IOException { - throw new IOException(); - } - }; - - try { - SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); - Assert.assertNotNull(option); - } catch (Exception e) { - Assert.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); - } - } - - @Test - public void testBuildIOException() { - new MockUp() { - @Mock - public synchronized void load(Reader reader) throws IOException { - throw new IOException(); - } - }; - boolean validAssert = true; - try { - SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); - Assert.assertEquals("revoke.crl", option.getCrl()); - } catch (Exception e) { - Assert.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); - validAssert = false; - } - Assert.assertFalse(validAssert); - } - - @Test - public void testBuildInputStream() { - try { - - URL url = this.getClass().getResource("/server.ssl.properties"); - - InputStream inputStream = url.openStream(); - SSLOption option = SSLOption.build(inputStream); - Assert.assertNotNull(option); - } catch (Exception e) { - Assert.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); - } - } - - @Test - public void testPrivateMethodException() { - SSLOption option = new SSLOption(); - boolean validAssert = true; - try { - Deencapsulation.invoke(option, "load", "test"); - } catch (Exception e) { - Assert.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); - validAssert = false; - } - Assert.assertFalse(validAssert); - } -} diff --git a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/TestSSLOptionFactory.java b/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/TestSSLOptionFactory.java deleted file mode 100644 index 511a838ca93..00000000000 --- a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/TestSSLOptionFactory.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -import org.junit.Assert; -import org.junit.Test; - -import com.netflix.config.ConcurrentCompositeConfiguration; - -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestSSLOptionFactory { - @Test - public void testSSLOptionFactory() { - SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory("cc", null); - Assert.assertEquals(factory, null); - } - - @Test - public void testSSLOptionFactoryWrong(@Mocked SSLOption option) { - new Expectations() { - { - SSLOption.getStringProperty((ConcurrentCompositeConfiguration) any, anyString, (String[]) any); - result = "wrong"; - } - }; - SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory("cc", null); - Assert.assertEquals(factory, null); - } - - @Test - public void testSSLOptionFactoryCorrent() { - new MockUp() { - @Mock - public String getStringProperty(ConcurrentCompositeConfiguration configSource, String defaultValue, - String... keys) { - return "io.servicecomb.foundation.ssl.MyOptionFactory"; - } - }; - SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory("cc", null); - Assert.assertEquals(factory.createSSLOption().getProtocols(), "TLSv1.2"); - } -} diff --git a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/TestSSLSocketFactoryExt.java b/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/TestSSLSocketFactoryExt.java deleted file mode 100644 index 89d77c60c64..00000000000 --- a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/TestSSLSocketFactoryExt.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -import javax.net.ssl.SSLSocketFactory; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -public class TestSSLSocketFactoryExt { - - private SSLSocketFactoryExt instance = null; - - @Before - public void setUp() throws Exception { - SSLSocketFactory factory = Mockito.mock(SSLSocketFactoryExt.class); - String[] ciphers = {"test"}; - - String[] protos = {"test"}; - instance = new SSLSocketFactoryExt(factory, ciphers, protos); - } - - @After - public void tearDown() throws Exception { - instance = null; - } - - @Test - public void testCreateSocketException() { - boolean validAssert = true; - try { - instance.createSocket("host", 8080); - } catch (Exception e) { - validAssert = false; - } - Assert.assertFalse(validAssert); - } -} diff --git a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/TestTrustAllManager.java b/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/TestTrustAllManager.java deleted file mode 100644 index 665462bab9d..00000000000 --- a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/TestTrustAllManager.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.ssl; - -import java.net.Socket; -import java.security.cert.X509Certificate; - -import javax.net.ssl.SSLEngine; - -import org.junit.Assert; -import org.junit.Test; - -public class TestTrustAllManager { - @Test - public void testTrustAllManager() throws Exception { - TrustAllManager manager = new TrustAllManager(); - manager.checkClientTrusted((X509Certificate[]) null, (String) null); - manager.checkServerTrusted((X509Certificate[]) null, (String) null); - - manager.checkClientTrusted((X509Certificate[]) null, - (String) null, - (Socket) null); - manager.checkClientTrusted((X509Certificate[]) null, - (String) null, - (SSLEngine) null); - - manager.checkServerTrusted((X509Certificate[]) null, - (String) null, - (Socket) null); - manager.checkServerTrusted((X509Certificate[]) null, - (String) null, - (SSLEngine) null); - Assert.assertEquals(manager.getAcceptedIssuers() == null, true); - } -} diff --git a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/CertificateUtilTest.java b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/CertificateUtilTest.java similarity index 86% rename from foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/CertificateUtilTest.java rename to foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/CertificateUtilTest.java index 7489adfa4ab..118df056a7c 100644 --- a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/CertificateUtilTest.java +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/CertificateUtilTest.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +15,7 @@ * limitations under the License. */ -package io.servicecomb.foundation.ssl; +package org.apache.servicecomb.foundation.ssl; import java.math.BigInteger; import java.security.InvalidKeyException; @@ -34,15 +35,18 @@ import javax.security.auth.x500.X500Principal; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import mockit.Expectations; import mockit.Mocked; +import org.junit.jupiter.api.Assertions; public class CertificateUtilTest { - class MyX509Certificate extends X509Certificate { + @SuppressWarnings("deprecation") + static class MyX509Certificate extends X509Certificate { + private static final long serialVersionUID = -3585440601605666278L; + public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException { } @@ -196,7 +200,7 @@ public void testGetCN(@Mocked X500Principal aX500Principal, @Mocked MyX509Certif MyX509Certificate xxmyX509Certificate = new MyX509Certificate(); Set strExpect = CertificateUtil.getCN(xxmyX509Certificate); - Assert.assertEquals(true, strExpect.contains("Test1234")); + Assertions.assertTrue(strExpect.contains("Test1234")); } @Test @@ -215,9 +219,9 @@ public void testGetCNException(@Mocked X500Principal aX500Principal, try { Set strExpect = CertificateUtil.getCN(xxmyX509Certificate); - Assert.assertEquals(strExpect.size(), 0); + Assertions.assertEquals(strExpect.size(), 0); } catch (IllegalArgumentException e) { - Assert.assertNotNull(null); + Assertions.assertNotNull(null); } } @@ -252,7 +256,7 @@ public void testFindOwner(@Mocked X500Principal aX500Principal1, @Mocked X500Pri xxmyX509Certificate[1] = myX509Certificate2; X509Certificate aX509Certificate = CertificateUtil.findOwner(xxmyX509Certificate); - Assert.assertNull(aX509Certificate); + Assertions.assertNull(aX509Certificate); } @Test @@ -287,9 +291,9 @@ public void testFindRootCAException(@Mocked X500Principal aX500Principal1, @Mock try { X509Certificate aX509Certificate = CertificateUtil.findOwner(xxmyX509Certificate); - Assert.assertNull(aX509Certificate); + Assertions.assertNull(aX509Certificate); } catch (IllegalArgumentException e) { - Assert.assertEquals("bad certificate chain: no root CA.", e.getMessage()); + Assertions.assertEquals("bad certificate chain: no root CA.", e.getMessage()); } } } 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 new file mode 100644 index 00000000000..54955139087 --- /dev/null +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtilTest.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.ssl; + +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.cert.CRL; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.Collection; + +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import mockit.Mock; +import mockit.MockUp; +import org.mockito.Mockito; + +public class KeyStoreUtilTest { + final String strFilePath = Thread.currentThread().getContextClassLoader().getResource("").getPath(); + + @Test + public void testCreateKeyStoreException() { + String storename = ""; + String storetype = "testType"; + char[] storevalue = "Changeme_123".toCharArray(); + + try { + KeyStoreUtil.createKeyStore(storename, storetype, storevalue); + } catch (IllegalArgumentException e) { + Assertions.assertEquals("Bad key store or value.testType not found", e.getMessage()); + } + } + + @Test + public void testCreateKeyStoreException2() { + String storename = strFilePath + "/ssl/trust.jks"; + String storetype = "PKCS12"; + char[] storevalue = "Changeme_123".toCharArray(); + + try { + KeyStoreUtil.createKeyStore(storename, storetype, storevalue); + } catch (IllegalArgumentException e) { + Assertions.assertEquals("Bad key store or value.DerInputStream.getLength(): lengthTag=109, too big.", + e.getMessage()); + } + } + + @Test + public void testCreateKeyManagersException() { + KeyStore keystore; + + String storename = strFilePath + "/ssl/server.p12"; + String storetype = "PKCS12"; + char[] storevalue = "Changeme_123".toCharArray(); + + keystore = KeyStoreUtil.createKeyStore(storename, storetype, storevalue); + + char[] storeKeyValue = null; + try { + KeyStoreUtil.createKeyManagers(keystore, storeKeyValue); + } catch (IllegalArgumentException e) { + Assertions.assertEquals("Bad key store.Get Key failed:" + + " Cannot read the array length because \"password\" is null", + e.getMessage()); + } + } + + @Test + public void testCreateCRL() { + String crlfile = strFilePath + "/ssl/server.p12"; + mockGenerateCRLs(); + boolean validAssert = true; + try { + CRL[] crl = KeyStoreUtil.createCRL(crlfile); + Assertions.assertNull(crl); + } catch (Exception e) { + validAssert = false; + } + Assertions.assertTrue(validAssert); + } + + @Test + public void testCreateCRLException() { + String crlfile = strFilePath + "/ssl/server.p12"; + boolean validAssert = true; + try { + new MockUp() { + @Mock + public CertificateFactory getInstance(String type) throws CertificateException { + throw new CertificateException(); + } + }; + + KeyStoreUtil.createCRL(crlfile); + } catch (Exception e) { + validAssert = false; + } + Assertions.assertFalse(validAssert); + } + + @Test + public void testExceptionFileNotFound() { + String crlfile = strFilePath + "/ssl/server.p12"; + boolean validAssert = true; + try { + new MockUp() { + @Mock + public CertificateFactory getInstance( + String type) throws CertificateException, FileNotFoundException { + throw new FileNotFoundException(); + } + }; + KeyStoreUtil.createCRL(crlfile); + } catch (Exception e) { + validAssert = false; + Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); + } + Assertions.assertFalse(validAssert); + } + + @Test + public void testExceptionCRLException() { + String crlfile = strFilePath + "/ssl/server.p12"; + boolean validAssert = true; + try { + new MockUp() { + @Mock + public CertificateFactory getInstance(String type) throws CertificateException, CRLException { + throw new CRLException(); + } + }; + KeyStoreUtil.createCRL(crlfile); + } catch (Exception e) { + validAssert = false; + Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); + } + Assertions.assertFalse(validAssert); + } + + private void mockGenerateCRLs() { + new MockUp() { + @SuppressWarnings("unchecked") + @Mock + public Collection generateCRLs(InputStream inStream) throws CRLException { + return Mockito.mock(Collection.class); + } + }; + } +} diff --git a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/MyOptionFactory.java b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/MyOptionFactory.java new file mode 100644 index 00000000000..cbca5a04c01 --- /dev/null +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/MyOptionFactory.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.foundation.ssl; + +public class MyOptionFactory implements SSLOptionFactory { + + @Override + public SSLOption createSSLOption() { + SSLOption option = new SSLOption(); + option.setProtocols("TLSv1.2"); + option.setCiphers("TLS_RSA_WITH_AES_128_CBC_SHA256"); + option.setAuthPeer(false); + option.setCheckCNHost(false); + option.setCheckCNWhite(false); + option.setCheckCNWhiteFile("white.list"); + return option; + } +} diff --git a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLManagerTest.java b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLManagerTest.java new file mode 100644 index 00000000000..71758fa3cff --- /dev/null +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLManagerTest.java @@ -0,0 +1,469 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ssl; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.Enumeration; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import mockit.Expectations; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; + +public class SSLManagerTest { + private final String DIR = Thread.currentThread().getContextClassLoader().getResource("").getPath(); + + @Test + public void testSSLManagerServerAndClient(final @Mocked NetworkInterface nif) throws Exception { + final InetAddress ia = Inet4Address.getByName("10.57.65.225"); + final Enumeration interfaces = new Enumeration() { + final int count = 1; + + int cur = 0; + + @Override + public boolean hasMoreElements() { + if (cur < count) { + cur++; + return true; + } + return false; + } + + @Override + public NetworkInterface nextElement() { + return nif; + } + }; + + final Enumeration ias = new Enumeration() { + final int count = 1; + + int cur = 0; + + @Override + public boolean hasMoreElements() { + if (cur < count) { + cur++; + return true; + } + return false; + } + + @Override + public InetAddress nextElement() { + return ia; + } + }; + + new Expectations() { + @Mocked + NetworkInterface nif; + + { + NetworkInterface.getNetworkInterfaces(); + result = interfaces; + } + }; + new Expectations() { + { + nif.getInetAddresses(); + result = ias; + ia.getHostAddress(); + result = "10.57.65.225"; + } + }; + + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + SSLCustom custom = new SSLCustom() { + @Override + public String getFullPath(String filename) { + return DIR + "/ssl/" + filename; + } + + @Override + public char[] decode(char[] encrypted) { + return encrypted; + } + }; + final SSLServerSocket serverSocket = SSLManager.createSSLServerSocket(option, custom); + Assertions.assertTrue(serverSocket.getNeedClientAuth()); + serverSocket.bind(new InetSocketAddress("127.0.0.1", 8886)); + String[] protos = serverSocket.getEnabledCipherSuites(); + String[] protosExpected = + "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" + .split(","); + Assertions.assertArrayEquals(protos, protosExpected); + String[] ciphers = serverSocket.getEnabledCipherSuites(); + String[] ciphersExpected = + "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" + .split(","); + Assertions.assertArrayEquals(ciphers, ciphersExpected); + Assertions.assertTrue(serverSocket.getNeedClientAuth()); + + SSLOption clientoption = SSLOption.build(DIR + "/client.ssl.properties"); + SSLSocket clientsocket = SSLManager.createSSLSocket(clientoption, custom); + String[] clientprotos = clientsocket.getEnabledCipherSuites(); + String[] clientprotosExpected = + "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" + .split(","); + Assertions.assertArrayEquals(clientprotos, clientprotosExpected); + String[] clientciphers = clientsocket.getEnabledCipherSuites(); + String[] clientciphersExpected = + "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" + .split(","); + Assertions.assertArrayEquals(clientciphers, clientciphersExpected); + Assertions.assertFalse(clientsocket.getNeedClientAuth()); + boolean validAssert = true; + try { + clientsocket.connect(new InetSocketAddress("127.0.0.1", 8886)); + + new Thread(() -> { + + try { + SSLSocket s = (SSLSocket) serverSocket.accept(); + s.addHandshakeCompletedListener(arg0 -> { + + }); + s.getOutputStream().write(new byte[] {0, 1}); + } catch (IOException e) { + e.printStackTrace(); + Assertions.fail("this should not happen"); + } + }).start(); + + clientsocket.startHandshake(); + clientsocket.close(); + serverSocket.close(); + + // socked successfully opened and closed + } catch (Exception e) { + e.printStackTrace(); + validAssert = false; + } + Assertions.assertTrue(validAssert); + } + + @Test + public void testCreateSSLEngine() { + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + SSLCustom custom = new SSLCustom() { + @Override + public String getFullPath(String filename) { + return DIR + "/ssl/" + filename; + } + + @Override + public char[] decode(char[] encrypted) { + return encrypted; + } + }; + + SSLEngine aSSLEngine = SSLManager.createSSLEngine(option, custom); + // if client mode may not decided at initialization. Different JDK is different, do not check it. + // Assertions.assertEquals(false, aSSLEngine.getUseClientMode()); + Assertions.assertTrue(aSSLEngine.getNeedClientAuth()); + } + + @Test + public void testCreateSSLEngineClientAuthNone() { + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + option.setClientAuth("NONE"); + option.setAuthPeer(false); + SSLCustom custom = new SSLCustom() { + @Override + public String getFullPath(String filename) { + return DIR + "/ssl/" + filename; + } + + @Override + public char[] decode(char[] encrypted) { + return encrypted; + } + }; + + SSLEngine aSSLEngine = SSLManager.createSSLEngine(option, custom); + // if client mode may not decided at initialization. Different JDK is different, do not check it. + // Assertions.assertEquals(false, aSSLEngine.getUseClientMode()); + Assertions.assertFalse(aSSLEngine.getNeedClientAuth()); + Assertions.assertFalse(aSSLEngine.getWantClientAuth()); + } + + @Test + public void testCreateSSLEnginewithPort() { + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + SSLCustom custom = new SSLCustom() { + @Override + public String getFullPath(String filename) { + return DIR + "/ssl/" + filename; + } + + @Override + public char[] decode(char[] encrypted) { + return encrypted; + } + }; + + int port = 39093; + String peerHost = "host1"; + SSLEngine aSSLEngine = SSLManager.createSSLEngine(option, custom, peerHost, port); + Assertions.assertNotNull(aSSLEngine); + Assertions.assertEquals("host1", aSSLEngine.getPeerHost()); + } + + @Test + public void testCreateSSLContextResource() { + SSLOption option = SSLOption.build(DIR + "/server.ssl.resource.properties"); + + SSLCustom custom = SSLCustom.defaultSSLCustom(); + + SSLContext context = SSLManager.createSSLContext(option, custom); + Assertions.assertNotNull(context); + } + + @Test + public void testCreateSSLContextException() { + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + + SSLCustom custom = new SSLCustom() { + @Override + public String getFullPath(String filename) { + return DIR + "/ssl/" + filename; + } + + @Override + public char[] decode(char[] encrypted) { + return encrypted; + } + }; + + new MockUp() { + @Mock + public SSLContext getInstance(String type) throws NoSuchAlgorithmException { + throw new NoSuchAlgorithmException(); + } + }; + + try { + SSLManager.createSSLContext(option, custom); + Assertions.assertNotNull(null); + } catch (Exception e) { + Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); + } + } + + @Test + public void testCreateSSLContextKeyManagementException() { + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + + SSLCustom custom = new SSLCustom() { + @Override + public String getFullPath(String filename) { + return DIR + "/ssl/" + filename; + } + + @Override + public char[] decode(char[] encrypted) { + return encrypted; + } + }; + + new MockUp() { + @Mock + public SSLContext getInstance(String type) throws KeyManagementException { + throw new KeyManagementException(); + } + }; + + try { + SSLManager.createSSLContext(option, custom); + Assertions.assertNotNull(null); + } catch (Exception e) { + Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); + } + } + + @Test + public void testCreateSSLServerSocketException() { + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + + SSLCustom custom = new SSLCustom() { + @Override + public String getFullPath(String filename) { + return DIR + "/ssl/" + filename; + } + + @Override + public char[] decode(char[] encrypted) { + return encrypted; + } + }; + + new MockUp() { + @Mock + public SSLContext getInstance(String type) throws UnknownHostException { + throw new UnknownHostException(); + } + }; + + try { + SSLManager.createSSLServerSocket(option, custom); + + Assertions.assertNotNull(null); + } catch (Exception e) { + Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); + } + } + + @Test + public void testCreateSSLServerSocketIOException() { + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + + SSLCustom custom = new SSLCustom() { + @Override + public String getFullPath(String filename) { + return DIR + "/ssl/" + filename; + } + + @Override + public char[] decode(char[] encrypted) { + return encrypted; + } + }; + + new MockUp() { + @Mock + public SSLContext getInstance(String type) throws IOException { + throw new IOException(); + } + }; + + try { + SSLManager.createSSLServerSocket(option, custom); + Assertions.assertNotNull(null); + } catch (Exception e) { + Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); + } + } + + @Test + public void testCreateSSLSocketException() { + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + + SSLCustom custom = new SSLCustom() { + @Override + public String getFullPath(String filename) { + return DIR + "/ssl/" + filename; + } + + @Override + public char[] decode(char[] encrypted) { + return encrypted; + } + }; + + new MockUp() { + @Mock + public SSLContext getInstance(String type) throws UnknownHostException { + throw new UnknownHostException(); + } + }; + + try { + SSLManager.createSSLSocket(option, custom); + Assertions.assertNotNull(null); + } catch (Exception e) { + Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); + } + } + + @Test + public void testCreateSSLSocketIOException() { + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + + SSLCustom custom = new SSLCustom() { + @Override + public String getFullPath(String filename) { + return DIR + "/ssl/" + filename; + } + + @Override + public char[] decode(char[] encrypted) { + return encrypted; + } + }; + + new MockUp() { + @Mock + public SSLContext getInstance(String type) throws IOException { + throw new IOException(); + } + }; + + try { + SSLManager.createSSLSocket(option, custom); + Assertions.assertNotNull(null); + } catch (Exception e) { + Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); + } + } + + @Test + public void testCreateSSLSocketFactory() { + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + SSLCustom custom = new SSLCustom() { + @Override + public String getFullPath(String filename) { + return DIR + "/ssl/" + filename; + } + + @Override + public char[] decode(char[] encrypted) { + return encrypted; + } + }; + + SSLSocketFactory aSSLSocketFactory = SSLManager.createSSLSocketFactory(option, custom); + Assertions.assertNotNull(aSSLSocketFactory.getDefaultCipherSuites()[0]); + } + + @Test + public void testGetSupportedCiphers() { + SSLOption option = new SSLOption(); + option.setCiphers("TLS_RSA_WITH_AES_128_GCM_SHA256"); + option.setProtocols("TLSv1.2"); + String[] ciphers = SSLManager.getEnabledCiphers(option); + Assertions.assertEquals(ciphers[0], "TLS_RSA_WITH_AES_128_GCM_SHA256"); + } +} 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 new file mode 100644 index 00000000000..ae91fcbb697 --- /dev/null +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLOptionTest.java @@ -0,0 +1,381 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ssl; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.net.URL; +import java.util.Properties; + +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.Mock; +import mockit.MockUp; + +public class SSLOptionTest { + private static final String DIR = Thread.currentThread().getContextClassLoader().getResource("").getPath(); + + 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 + public void testSSLOption() { + + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + + String protocols = option.getProtocols(); + option.setProtocols(protocols); + Assertions.assertEquals("TLSv1.3,TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello", protocols); + + String ciphers = option.getCiphers(); + option.setCiphers(ciphers); + Assertions.assertEquals( + "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_SH" + + + "A,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA", + ciphers); + + boolean authPeer = option.isAuthPeer(); + option.setAuthPeer(authPeer); + Assertions.assertTrue(authPeer); + + boolean checkCNHost = option.isCheckCNHost(); + option.setCheckCNHost(checkCNHost); + Assertions.assertTrue(checkCNHost); + + boolean checkCNWhite = option.isCheckCNWhite(); + option.setCheckCNWhite(checkCNWhite); + Assertions.assertTrue(checkCNWhite); + + String checkCNWhiteFile = option.getCheckCNWhiteFile(); + option.setCheckCNWhiteFile(checkCNWhiteFile); + Assertions.assertEquals("white.list", checkCNWhiteFile); + + boolean allowRenegotiate = option.isAllowRenegotiate(); + option.setAllowRenegotiate(allowRenegotiate); + Assertions.assertFalse(allowRenegotiate); + + String storePath = option.getStorePath(); + option.setStorePath(storePath); + Assertions.assertEquals("internal", storePath); + + String trustStore = option.getTrustStore(); + option.setTrustStore(trustStore); + Assertions.assertEquals("trust.jks", trustStore); + + String trustStoreType = option.getTrustStoreType(); + option.setTrustStoreType(trustStoreType); + Assertions.assertEquals("JKS", trustStoreType); + + String trustStoreValue = option.getTrustStoreValue(); + option.setTrustStoreValue(trustStoreValue); + Assertions.assertEquals("Changeme_123", trustStoreValue); + + String keyStore = option.getKeyStore(); + option.setKeyStore(keyStore); + Assertions.assertEquals("server.p12", keyStore); + + String keyStoreType = option.getKeyStoreType(); + option.setKeyStoreType(keyStoreType); + Assertions.assertEquals("PKCS12", keyStoreType); + + String keyStoreValue = option.getKeyStoreValue(); + option.setKeyStoreValue(keyStoreValue); + Assertions.assertEquals("Changeme_123", keyStoreValue); + + String crl = option.getCrl(); + option.setCrl(crl); + Assertions.assertEquals("revoke.crl", crl); + } + + @Test + public void testSSLOptionYaml() { + Mockito.when(environment.getProperty("ssl.server.sslCustomClass")).thenReturn("wwrong"); + SSLOption option = SSLOption.build("server", environment); + + String protocols = option.getProtocols(); + option.setProtocols(protocols); + Assertions.assertEquals("TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello", protocols); + + 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", + ciphers); + + boolean authPeer = option.isAuthPeer(); + option.setAuthPeer(authPeer); + Assertions.assertTrue(authPeer); + + boolean checkCNHost = option.isCheckCNHost(); + option.setCheckCNHost(checkCNHost); + Assertions.assertTrue(checkCNHost); + + boolean checkCNWhite = option.isCheckCNWhite(); + option.setCheckCNWhite(checkCNWhite); + Assertions.assertTrue(checkCNWhite); + + String checkCNWhiteFile = option.getCheckCNWhiteFile(); + option.setCheckCNWhiteFile(checkCNWhiteFile); + Assertions.assertEquals("white.list", checkCNWhiteFile); + + boolean allowRenegotiate = option.isAllowRenegotiate(); + option.setAllowRenegotiate(allowRenegotiate); + Assertions.assertFalse(allowRenegotiate); + + String storePath = option.getStorePath(); + option.setStorePath(storePath); + Assertions.assertEquals("internal", storePath); + + String trustStore = option.getTrustStore(); + option.setTrustStore(trustStore); + Assertions.assertEquals("trust.jks", trustStore); + + String trustStoreType = option.getTrustStoreType(); + option.setTrustStoreType(trustStoreType); + Assertions.assertEquals("JKS", trustStoreType); + + String trustStoreValue = option.getTrustStoreValue(); + option.setTrustStoreValue(trustStoreValue); + Assertions.assertEquals("Changeme_123", trustStoreValue); + + String keyStore = option.getKeyStore(); + option.setKeyStore(keyStore); + Assertions.assertEquals("server.p12", keyStore); + + String keyStoreType = option.getKeyStoreType(); + option.setKeyStoreType(keyStoreType); + Assertions.assertEquals("PKCS12", keyStoreType); + + String keyStoreValue = option.getKeyStoreValue(); + option.setKeyStoreValue(keyStoreValue); + Assertions.assertEquals("Changeme_123", keyStoreValue); + + String crl = option.getCrl(); + option.setCrl(crl); + Assertions.assertEquals("revoke.crl", crl); + + option.setSslCustomClass("123"); + + SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); + Assertions.assertArrayEquals(custom.decode("123".toCharArray()), "123".toCharArray()); + } + + @Test + public void testSSLOptionYamlOption2() throws Exception { + Mockito.when(environment.getProperty("ssl.protocols")).thenReturn("TLSv1.2"); + + SSLOption option = SSLOption.build("server", environment); + + String protocols = option.getProtocols(); + option.setProtocols(protocols); + Assertions.assertEquals("TLSv1.2", protocols); + System.clearProperty("ssl.protocols"); + } + + @Test + public void testSSLOptionYamlOptionWithPropertyFalse() throws Exception { + Mockito.when(environment.getProperty("ssl.authPeer")).thenReturn("false"); + + SSLOption option = SSLOption.build("server", environment); + + boolean authPeer = option.isAuthPeer(); + option.setAuthPeer(authPeer); + Assertions.assertFalse(authPeer); + System.getProperties().remove("ssl.authPeer"); + } + + @Test + public void testSSLOptionYamlOptionWithPropertyTrue() throws Exception { + SSLOption option = SSLOption.build("server", environment); + + boolean authPeer = option.isAuthPeer(); + option.setAuthPeer(authPeer); + Assertions.assertTrue(authPeer); + System.getProperties().remove("ssl.authPeer"); + } + + @Test + public void testSSLOptionYamlOption() throws Exception { + SSLOption option = SSLOption.build("server", environment); + + String protocols = option.getProtocols(); + option.setProtocols(protocols); + Assertions.assertEquals("TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello", protocols); + + 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", + ciphers); + + boolean authPeer = option.isAuthPeer(); + option.setAuthPeer(authPeer); + Assertions.assertTrue(authPeer); + + boolean checkCNHost = option.isCheckCNHost(); + option.setCheckCNHost(checkCNHost); + Assertions.assertTrue(checkCNHost); + + boolean checkCNWhite = option.isCheckCNWhite(); + option.setCheckCNWhite(checkCNWhite); + Assertions.assertTrue(checkCNWhite); + + String checkCNWhiteFile = option.getCheckCNWhiteFile(); + option.setCheckCNWhiteFile(checkCNWhiteFile); + Assertions.assertEquals("white.list", checkCNWhiteFile); + + boolean allowRenegotiate = option.isAllowRenegotiate(); + option.setAllowRenegotiate(allowRenegotiate); + Assertions.assertFalse(allowRenegotiate); + + String storePath = option.getStorePath(); + option.setStorePath(storePath); + Assertions.assertEquals("internal", storePath); + + String trustStore = option.getTrustStore(); + option.setTrustStore(trustStore); + Assertions.assertEquals("trust.jks", trustStore); + + String trustStoreType = option.getTrustStoreType(); + option.setTrustStoreType(trustStoreType); + Assertions.assertEquals("JKS", trustStoreType); + + String trustStoreValue = option.getTrustStoreValue(); + option.setTrustStoreValue(trustStoreValue); + Assertions.assertEquals("Changeme_123", trustStoreValue); + + String keyStore = option.getKeyStore(); + option.setKeyStore(keyStore); + Assertions.assertEquals("server.p12", keyStore); + + String keyStoreType = option.getKeyStoreType(); + option.setKeyStoreType(keyStoreType); + Assertions.assertEquals("PKCS12", keyStoreType); + + String keyStoreValue = option.getKeyStoreValue(); + option.setKeyStoreValue(keyStoreValue); + Assertions.assertEquals("Changeme_123", keyStoreValue); + + String crl = option.getCrl(); + option.setCrl(crl); + Assertions.assertEquals("revoke.crl", crl); + + option.setSslCustomClass("123"); + + SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); + Assertions.assertArrayEquals(custom.decode("123".toCharArray()), "123".toCharArray()); + } + + @SuppressWarnings("unused") + @Test + public void testSSLOptionNull() { + try { + SSLOption option = SSLOption.build(DIR + "/servers.ssl.properties"); + } catch (IllegalArgumentException e) { + Assertions.assertEquals("Bad file name.", e.getMessage()); + } + } + + @Test + public void testBuildException() { + new MockUp() { + @Mock + public String getCanonicalPath() throws IOException { + throw new IOException(); + } + }; + + try { + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + Assertions.assertNotNull(option); + } catch (Exception e) { + Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); + } + } + + @Test + public void testBuildIOException() { + new MockUp() { + @Mock + public synchronized void load(Reader reader) throws IOException { + throw new IOException(); + } + }; + boolean validAssert = true; + try { + SSLOption option = SSLOption.build(DIR + "/server.ssl.properties"); + Assertions.assertEquals("revoke.crl", option.getCrl()); + } catch (Exception e) { + Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); + validAssert = false; + } + Assertions.assertFalse(validAssert); + } + + @Test + public void testBuildInputStream() { + try { + + URL url = this.getClass().getResource("/server.ssl.properties"); + + InputStream inputStream = url.openStream(); + SSLOption option = SSLOption.build(inputStream); + Assertions.assertNotNull(option); + } catch (Exception e) { + Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); + } + } + + @Test + public void testPrivateMethodException() { + SSLOption option = new SSLOption(); + boolean validAssert = true; + try { + option.load("test"); + } catch (Exception e) { + Assertions.assertEquals("java.lang.IllegalArgumentException", e.getClass().getName()); + validAssert = false; + } + Assertions.assertFalse(validAssert); + } +} 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 new file mode 100644 index 00000000000..1d3b1b8b229 --- /dev/null +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLOptionFactory.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.foundation.ssl; + +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.Mock; +import mockit.MockUp; +import mockit.Mocked; + +public class TestSSLOptionFactory { + Environment environment = Mockito.mock(Environment.class); + + @Test + public void testSSLOptionFactory() { + SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory("cc", environment); + Assertions.assertNull(factory); + } + + @Test + public void testSSLOptionFactoryWrong(@Mocked SSLOption option) { + new Expectations() { + { + SSLOption.getStringProperty((Environment) any, anyString, (String[]) any); + result = "wrong"; + } + }; + + IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, + () -> SSLOptionFactory.createSSLOptionFactory("cc", null)); + Assertions.assertEquals("Failed to create SSLOptionFactory.", exception.getMessage()); + } + + @Test + public void testSSLOptionFactoryCurrent() { + new MockUp() { + @Mock + public String getStringProperty(Environment environment, String defaultValue, + String... keys) { + return "org.apache.servicecomb.foundation.ssl.MyOptionFactory"; + } + }; + SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory("cc", environment); + Assertions.assertEquals(factory.createSSLOption().getProtocols(), "TLSv1.2"); + } +} diff --git a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLSocketFactoryExt.java b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLSocketFactoryExt.java new file mode 100644 index 00000000000..7c1e8c87724 --- /dev/null +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLSocketFactoryExt.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.foundation.ssl; + +import javax.net.ssl.SSLSocketFactory; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; + +public class TestSSLSocketFactoryExt { + + private SSLSocketFactoryExt instance = null; + + @Before + public void setUp() throws Exception { + SSLSocketFactory factory = Mockito.mock(SSLSocketFactoryExt.class); + String[] ciphers = {"test"}; + + String[] protos = {"test"}; + instance = new SSLSocketFactoryExt(factory, ciphers, protos); + } + + @After + public void tearDown() throws Exception { + instance = null; + } + + @Test + public void testCreateSocketException() { + boolean validAssert = true; + try { + instance.createSocket("host", 8080); + } catch (Exception e) { + validAssert = false; + } + Assertions.assertFalse(validAssert); + } +} diff --git a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestTrustAllManager.java b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestTrustAllManager.java new file mode 100644 index 00000000000..7a5256925c9 --- /dev/null +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestTrustAllManager.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.ssl; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.net.Socket; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLEngine; + +public class TestTrustAllManager { + @Test + public void testTrustAllManager() throws Exception { + TrustAllManager manager = new TrustAllManager(); + manager.checkClientTrusted((X509Certificate[]) null, (String) null); + manager.checkServerTrusted((X509Certificate[]) null, (String) null); + + manager.checkClientTrusted((X509Certificate[]) null, + (String) null, + (Socket) null); + manager.checkClientTrusted((X509Certificate[]) null, + (String) null, + (SSLEngine) null); + + manager.checkServerTrusted((X509Certificate[]) null, + (String) null, + (Socket) null); + manager.checkServerTrusted((X509Certificate[]) null, + (String) null, + (SSLEngine) null); + Assertions.assertNull(manager.getAcceptedIssuers()); + } +} diff --git a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/TrustManagerExtTest.java b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TrustManagerExtTest.java similarity index 75% rename from foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/TrustManagerExtTest.java rename to foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TrustManagerExtTest.java index 757bee09454..d9f65d56aae 100644 --- a/foundations/foundation-ssl/src/test/java/io/servicecomb/foundation/ssl/TrustManagerExtTest.java +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TrustManagerExtTest.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +15,7 @@ * limitations under the License. */ -package io.servicecomb.foundation.ssl; +package org.apache.servicecomb.foundation.ssl; import java.io.IOException; import java.io.InputStreamReader; @@ -40,7 +41,6 @@ import javax.net.ssl.X509ExtendedTrustManager; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -48,11 +48,12 @@ import mockit.Mock; import mockit.MockUp; import mockit.Mocked; +import org.junit.jupiter.api.Assertions; public class TrustManagerExtTest { final String strFilePath = Thread.currentThread().getContextClassLoader().getResource("").getPath(); - class MyX509ExtendedTrustManager extends X509ExtendedTrustManager { + static class MyX509ExtendedTrustManager extends X509ExtendedTrustManager { public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString, Socket paramSocket) throws CertificateException { } @@ -82,7 +83,10 @@ public X509Certificate[] getAcceptedIssuers() { } } - class MyX509Certificate extends X509Certificate { + @SuppressWarnings("deprecation") + static class MyX509Certificate extends X509Certificate { + private static final long serialVersionUID = -3585440601605666276L; + public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException { } @@ -257,26 +261,25 @@ public void testConstructor() { TrustManagerExt trustManagerExt = new TrustManagerExt((X509ExtendedTrustManager) trustManager[0], option, custom); - Assert.assertEquals(3, trustManagerExt.getAcceptedIssuers()[0].getVersion()); - Assert.assertNotNull(trustManagerExt); + Assertions.assertEquals(3, trustManagerExt.getAcceptedIssuers()[0].getVersion()); + Assertions.assertNotNull(trustManagerExt); } - @SuppressWarnings("static-access") @Test - public void testConstructorWithParam(@Mocked CertificateUtil certificateUtil) { + public void testConstructorWithParam() { MyX509Certificate myX509Certificate1 = new MyX509Certificate(); MyX509Certificate myX509Certificate2 = new MyX509Certificate(); - MyX509Certificate[] MyX509CertificateArray = new MyX509Certificate[2]; - MyX509CertificateArray[0] = myX509Certificate1; - MyX509CertificateArray[1] = myX509Certificate2; + MyX509Certificate[] myX509CertificateArray = new MyX509Certificate[2]; + myX509CertificateArray[0] = myX509Certificate1; + myX509CertificateArray[1] = myX509Certificate2; - new Expectations() { + new Expectations(CertificateUtil.class) { { - certificateUtil.findOwner((X509Certificate[]) any); + CertificateUtil.findOwner((X509Certificate[]) any); result = any; - certificateUtil.getCN((X509Certificate) any); + CertificateUtil.getCN((X509Certificate) any); result = "10.67.147.115"; } }; @@ -284,16 +287,16 @@ public void testConstructorWithParam(@Mocked CertificateUtil certificateUtil) { MyX509ExtendedTrustManager myX509ExtendedTrustManager = new MyX509ExtendedTrustManager(); TrustManagerExt trustManagerExt = new TrustManagerExt(myX509ExtendedTrustManager, option, custom); - Assert.assertNotNull(trustManagerExt); + Assertions.assertNotNull(trustManagerExt); boolean validAssert = true; try { - trustManagerExt.checkClientTrusted(MyX509CertificateArray, "pks"); - trustManagerExt.checkServerTrusted(MyX509CertificateArray, "pks"); + trustManagerExt.checkClientTrusted(myX509CertificateArray, "pks"); + trustManagerExt.checkServerTrusted(myX509CertificateArray, "pks"); trustManagerExt.getAcceptedIssuers(); } catch (Exception e) { validAssert = false; } - Assert.assertTrue(validAssert); + Assertions.assertTrue(validAssert); } @Test @@ -301,9 +304,9 @@ public void testCheckClientTrusted(@Mocked CertificateUtil certificateUtil) { MyX509Certificate myX509Certificate1 = new MyX509Certificate(); MyX509Certificate myX509Certificate2 = new MyX509Certificate(); - MyX509Certificate[] MyX509CertificateArray = new MyX509Certificate[2]; - MyX509CertificateArray[0] = myX509Certificate1; - MyX509CertificateArray[1] = myX509Certificate2; + MyX509Certificate[] myX509CertificateArray = new MyX509Certificate[2]; + myX509CertificateArray[0] = myX509Certificate1; + myX509CertificateArray[1] = myX509Certificate2; new Expectations() { { @@ -322,14 +325,14 @@ public void testCheckClientTrusted(@Mocked CertificateUtil certificateUtil) { SSLEngine sslengine = null; boolean validAssert = true; try { - trustManagerExt.checkClientTrusted(MyX509CertificateArray, "pks", socket); - trustManagerExt.checkClientTrusted(MyX509CertificateArray, "pks", sslengine); - trustManagerExt.checkServerTrusted(MyX509CertificateArray, "pks", socket); - trustManagerExt.checkServerTrusted(MyX509CertificateArray, "pks", sslengine); + trustManagerExt.checkClientTrusted(myX509CertificateArray, "pks", socket); + trustManagerExt.checkClientTrusted(myX509CertificateArray, "pks", sslengine); + trustManagerExt.checkServerTrusted(myX509CertificateArray, "pks", socket); + trustManagerExt.checkServerTrusted(myX509CertificateArray, "pks", sslengine); } catch (Exception e) { validAssert = false; } - Assert.assertTrue(validAssert); + Assertions.assertTrue(validAssert); } @Test @@ -337,9 +340,9 @@ public void testCatchException(@Mocked CertificateUtil certificateUtil) { MyX509Certificate myX509Certificate1 = new MyX509Certificate(); MyX509Certificate myX509Certificate2 = new MyX509Certificate(); - MyX509Certificate[] MyX509CertificateArray = new MyX509Certificate[2]; - MyX509CertificateArray[0] = myX509Certificate1; - MyX509CertificateArray[1] = myX509Certificate2; + MyX509Certificate[] myX509CertificateArray = new MyX509Certificate[2]; + myX509CertificateArray[0] = myX509Certificate1; + myX509CertificateArray[1] = myX509Certificate2; new Expectations() { { @@ -355,22 +358,22 @@ public void testCatchException(@Mocked CertificateUtil certificateUtil) { TrustManagerExt trustManagerExt = new TrustManagerExt(myX509ExtendedTrustManager, option, custom); boolean validAssert = true; try { - trustManagerExt.checkClientTrusted(MyX509CertificateArray, "pks"); + trustManagerExt.checkClientTrusted(myX509CertificateArray, "pks"); } catch (CertificateException e) { - Assert.assertEquals("CN does not match IP: e=[10.67.147.114],t=null", e.getMessage()); + Assertions.assertEquals("CN does not match IP: e=[10.67.147.114],t=null", e.getMessage()); validAssert = false; } - Assert.assertFalse(validAssert); + Assertions.assertFalse(validAssert); } @Test - public void testCheckClientTrustedExecption(@Mocked CertificateUtil certificateUtil) { + public void testCheckClientTrustedException(@Mocked CertificateUtil certificateUtil) { MyX509Certificate myX509Certificate1 = new MyX509Certificate(); MyX509Certificate myX509Certificate2 = new MyX509Certificate(); - MyX509Certificate[] MyX509CertificateArray = new MyX509Certificate[2]; - MyX509CertificateArray[0] = myX509Certificate1; - MyX509CertificateArray[1] = myX509Certificate2; + MyX509Certificate[] myX509CertificateArray = new MyX509Certificate[2]; + myX509CertificateArray[0] = myX509Certificate1; + myX509CertificateArray[1] = myX509Certificate2; new Expectations() { { @@ -390,20 +393,20 @@ public void testCheckClientTrustedExecption(@Mocked CertificateUtil certificateU new MockUp() { @Mock - public int read(char cbuf[]) throws IOException { + public int read(char[] cbuf) throws IOException { throw new IOException(); } }; boolean validAssert = true; try { - trustManagerExt.checkClientTrusted(MyX509CertificateArray, "pks", socket); - trustManagerExt.checkClientTrusted(MyX509CertificateArray, "pks", sslengine); - trustManagerExt.checkServerTrusted(MyX509CertificateArray, "pks", socket); - trustManagerExt.checkServerTrusted(MyX509CertificateArray, "pks", sslengine); + trustManagerExt.checkClientTrusted(myX509CertificateArray, "pks", socket); + trustManagerExt.checkClientTrusted(myX509CertificateArray, "pks", sslengine); + trustManagerExt.checkServerTrusted(myX509CertificateArray, "pks", socket); + trustManagerExt.checkServerTrusted(myX509CertificateArray, "pks", sslengine); } catch (Exception e) { - Assert.assertEquals("java.security.cert.CertificateException", e.getClass().getName()); + Assertions.assertEquals("java.security.cert.CertificateException", e.getClass().getName()); validAssert = false; } - Assert.assertFalse(validAssert); + Assertions.assertFalse(validAssert); } } diff --git a/foundations/foundation-ssl/src/test/resources/client.ssl.properties b/foundations/foundation-ssl/src/test/resources/client.ssl.properties index 4c672279307..87808468196 100644 --- a/foundations/foundation-ssl/src/test/resources/client.ssl.properties +++ b/foundations/foundation-ssl/src/test/resources/client.ssl.properties @@ -1,11 +1,12 @@ # -# Copyright 2017 Huawei Technologies Co., Ltd +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,13 +16,14 @@ # #########SSL options -ssl.protocols=TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello -ssl.ciphers=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.protocols=TLSv1.3,TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello +## 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/log4j.properties b/foundations/foundation-ssl/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/foundations/foundation-ssl/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/foundations/foundation-ssl/src/test/resources/microservice.yaml b/foundations/foundation-ssl/src/test/resources/microservice.yaml index dbab9045338..482c8e48ea7 100644 --- a/foundations/foundation-ssl/src/test/resources/microservice.yaml +++ b/foundations/foundation-ssl/src/test/resources/microservice.yaml @@ -1,3 +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. +## --------------------------------------------------------------------------- + #########SSL options ssl: protocols: TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello @@ -6,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 @@ -16,4 +33,4 @@ ssl: keyStoreValue: Changeme_123 crl: revoke.crl server: - sslCustomClass: wwrong \ No newline at end of file + sslCustomClass: wwrong diff --git a/foundations/foundation-ssl/src/test/resources/server.ssl.properties b/foundations/foundation-ssl/src/test/resources/server.ssl.properties index 4fa9d84b8b3..d2a8180ab51 100644 --- a/foundations/foundation-ssl/src/test/resources/server.ssl.properties +++ b/foundations/foundation-ssl/src/test/resources/server.ssl.properties @@ -1,11 +1,12 @@ # -# Copyright 2017 Huawei Technologies Co., Ltd +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -15,13 +16,13 @@ # #########SSL options -ssl.protocols=TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello -ssl.ciphers=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.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 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 new file mode 100644 index 00000000000..01f7380ba71 --- /dev/null +++ b/foundations/foundation-ssl/src/test/resources/server.ssl.resource.properties @@ -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. +# + +#########SSL options +ssl.protocols=TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello +ssl.ciphers=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=true +ssl.checkCN.white=true +ssl.checkCN.white.file=white.list +ssl.allowRenegotiate=false + +#########certificates config +ssl.storePath=internal +ssl.trustStore=ssl/trust.jks +ssl.trustStoreType=JKS +ssl.trustStoreValue=Changeme_123 +ssl.keyStore=ssl/server.p12 +ssl.keyStoreType=PKCS12 +ssl.keyStoreValue=Changeme_123 +ssl.crl=revoke.crl 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 new file mode 100644 index 00000000000..f3feba62148 --- /dev/null +++ b/foundations/foundation-test-scaffolding/pom.xml @@ -0,0 +1,82 @@ + + + + 4.0.0 + + org.apache.servicecomb + foundations + 3.4.0-SNAPSHOT + + foundation-test-scaffolding + Java Chassis::Foundations::Test Scaffolding + + + + org.springframework + spring-context + + + io.vertx + vertx-web + + + io.vertx + vertx-codegen + provided + + + com.google.guava + guava + + + org.apache.logging.log4j + log4j-core + provided + true + + + org.apache.logging.log4j + log4j-api + provided + true + + + com.fasterxml.jackson.core + jackson-annotations + + + javax.inject + javax.inject + + + org.springframework + spring-aspects + + + io.swagger.core.v3 + swagger-core-jakarta + provided + + + org.assertj + assertj-core + compile + + + diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/AssertUtils.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/AssertUtils.java new file mode 100644 index 00000000000..ef310a9da51 --- /dev/null +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/AssertUtils.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.foundation.test.scaffolding; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.assertj.core.api.AbstractStringAssert; + +import io.netty.handler.codec.string.LineSeparator; +import io.vertx.core.json.Json; + +public interface AssertUtils { + static AbstractStringAssert assertPrettyJson(Object value) { + String json = Json.encodePrettily(value) + .replace(LineSeparator.WINDOWS.value(), LineSeparator.UNIX.value()); + return assertThat(json); + } +} diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/exception/RuntimeExceptionWithoutStackTrace.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/exception/RuntimeExceptionWithoutStackTrace.java new file mode 100644 index 00000000000..65f17678548 --- /dev/null +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/exception/RuntimeExceptionWithoutStackTrace.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.foundation.test.scaffolding.exception; + +public class RuntimeExceptionWithoutStackTrace extends RuntimeException { + private static final long serialVersionUID = -1L; + + public RuntimeExceptionWithoutStackTrace() { + } + + public RuntimeExceptionWithoutStackTrace(String message) { + super(message); + } + + @Override + public synchronized Throwable fillInStackTrace() { + return this; + } +} 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 new file mode 100644 index 00000000000..5b435b89829 --- /dev/null +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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; +import org.apache.logging.log4j.core.LogEvent; +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; + +public class LogCollector implements Closeable { + List events = new ArrayList<>(); + + 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); + } + + public LogCollector setLogLevel(String logName, Level level) { + LoggerContext ctx = (LoggerContext) LogManager.getContext(false); + Configuration config = ctx.getConfiguration(); + config.getLoggerConfig(logName).setLevel(level); + return this; + } + + public List getEvents() { + return events; + } + + public LogEvent getEvent(int index) { + return events.get(index); + } + + public LogEvent getLastEvents() { + return events.get(events.size() - 1); + } + + public List getThrowables() { + return events.stream() + .map(LogEvent::getThrown) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + public List getThrowableMessages() { + return events.stream() + .filter(e -> e.getThrown() != null) + .map(e -> e.getThrown().getMessage()) + .collect(Collectors.toList()); + } + + public void tearDown() { + LoggerContext ctx = (LoggerContext) LogManager.getContext(false); + Configuration config = ctx.getConfiguration(); + appender.stop(); + config.getRootLogger().removeAppender(appenderName); + ctx.updateLoggers(config); + } + + public void clear() { + events = new ArrayList<>(); + } + + @Override + public void close() { + tearDown(); + } +} diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/Color.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/Color.java new file mode 100644 index 00000000000..5cca0d9f5d1 --- /dev/null +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/Color.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.foundation.test.scaffolding.model; + +public enum Color { + RED, + YELLOW, + BLUE +} diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/Empty.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/Empty.java new file mode 100644 index 00000000000..318e475c450 --- /dev/null +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/Empty.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.foundation.test.scaffolding.model; + +public class Empty { +} diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/Media.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/Media.java new file mode 100644 index 00000000000..f16acc4bacc --- /dev/null +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/Media.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.foundation.test.scaffolding.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * To test the situation that enum values are specified by {@link JsonProperty}. + */ +public enum Media { + @JsonProperty("AAC") + AAC, + @JsonProperty + FLAC, + @JsonProperty("H.264") + H_264, + @JsonProperty("MPEG-2") + MPEG_2, + WMV +} diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/People.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/People.java new file mode 100644 index 00000000000..e5600ce9024 --- /dev/null +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/People.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.foundation.test.scaffolding.model; + +import java.util.List; + +public class People { + public String name; + + public List friends; +} 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 new file mode 100644 index 00000000000..630d48b0ca1 --- /dev/null +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/model/User.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.foundation.test.scaffolding.model; + +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-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/spring/SpringUtils.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/spring/SpringUtils.java new file mode 100644 index 00000000000..828089633ba --- /dev/null +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/spring/SpringUtils.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.test.scaffolding.spring; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.UUID; + +import javax.inject.Inject; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.util.StringValueResolver; + +public final class SpringUtils { + private SpringUtils() { + } + + public static StringValueResolver createStringValueResolver(Map map) { + StandardEnvironment standardEnvironment = new StandardEnvironment(); + standardEnvironment.getPropertySources().addFirst( + new MapPropertySource(UUID.randomUUID().toString(), map)); + + return standardEnvironment::resolvePlaceholders; + } + + public static void ensureNoInject(Class cls) { + for (Field field : cls.getDeclaredFields()) { + if (field.getAnnotation(Inject.class) != null) { + throw new IllegalStateException(String + .format("field %s:%s has %s annotation", + cls.getName(), + field.getName(), + Inject.class.getName())); + } + + if (field.getAnnotation(Autowired.class) != null) { + throw new IllegalStateException(String + .format("field %s:%s has %s annotation", + cls.getName(), + field.getName(), + Autowired.class.getName())); + } + } + + for (Method method : cls.getDeclaredMethods()) { + if (method.getAnnotation(Inject.class) != null) { + throw new IllegalStateException(String + .format("method %s:%s has %s annotation", + cls.getName(), + method.getName(), + Inject.class.getName())); + } + + if (method.getAnnotation(Autowired.class) != null) { + throw new IllegalStateException(String + .format("method %s:%s has %s annotation", + cls.getName(), + method.getName(), + Autowired.class.getName())); + } + } + } +} diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/time/MockClock.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/time/MockClock.java new file mode 100644 index 00000000000..ba0f3c625e5 --- /dev/null +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/time/MockClock.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.test.scaffolding.time; + +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; + +public class MockClock extends Clock { + private MockValues values; + + public MockClock() { + this(0L); + } + + public MockClock(Long... values) { + this.setValues(values); + } + + public MockClock setValues(Long... values) { + this.values = new MockValues() + .setDefaultValue(0L) + .setValues(values); + return this; + } + + @Override + public ZoneId getZone() { + return null; + } + + @Override + public Clock withZone(ZoneId zone) { + return null; + } + + @Override + public Instant instant() { + return null; + } + + @Override + public long millis() { + return values.read(); + } +} diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/time/MockTicker.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/time/MockTicker.java new file mode 100644 index 00000000000..9bd666d00ed --- /dev/null +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/time/MockTicker.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.foundation.test.scaffolding.time; + +import com.google.common.base.Ticker; + +public class MockTicker extends Ticker { + private MockValues values; + + public MockTicker() { + this(0L); + } + + public MockTicker(Long... values) { + this.setValues(values); + } + + public MockTicker setValues(Long... values) { + this.values = new MockValues() + .setDefaultValue(0L) + .setValues(values); + return this; + } + + @Override + public long read() { + return values.read(); + } +} diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/time/MockValues.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/time/MockValues.java new file mode 100644 index 00000000000..457a8f0c072 --- /dev/null +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/time/MockValues.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.foundation.test.scaffolding.time; + +public class MockValues { + private T defaultValue; + + private T[] values; + + private int index; + + public MockValues setDefaultValue(T defaultValue) { + this.defaultValue = defaultValue; + return this; + } + + public MockValues setValues(T[] values) { + this.values = values; + this.index = 0; + return this; + } + + public T read() { + if (values == null || values.length == 0) { + return defaultValue; + } + + if (index >= values.length) { + return values[values.length - 1]; + } + + T value = values[index]; + index++; + return value; + } +} diff --git a/foundations/foundation-vertx/pom.xml b/foundations/foundation-vertx/pom.xml index 72c82c25633..24556f590f7 100644 --- a/foundations/foundation-vertx/pom.xml +++ b/foundations/foundation-vertx/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - foundations - 0.3.0-SNAPSHOT - - foundation-vertx + 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 + foundations + 3.4.0-SNAPSHOT + + foundation-vertx + Java Chassis::Foundations::Vertx + + + + io.vertx + vertx-core + + + io.vertx + vertx-web + + + org.apache.servicecomb + foundation-ssl + + + 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.jmockit + jmockit + test + + + org.apache.servicecomb + foundation-test-scaffolding + + - - - io.vertx - vertx-core - - - io.vertx - vertx-web - - - io.servicecomb - foundation-ssl - - - io.servicecomb - foundation-common - - - org.slf4j - slf4j-log4j12 - test - - diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/AsyncResultCallback.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/AsyncResultCallback.java deleted file mode 100644 index abb35fa5613..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/AsyncResultCallback.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx; - -import io.vertx.core.AsyncResultHandler; -import io.vertx.core.Future; - -public interface AsyncResultCallback extends AsyncResultHandler { - default void success(T data) { - handle(Future.succeededFuture(data)); - } - - default void fail(Throwable e) { - handle(Future.failedFuture(e)); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/SimpleBodyHandler.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/SimpleBodyHandler.java deleted file mode 100644 index 6aa391b7d5a..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/SimpleBodyHandler.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx; - -import javax.ws.rs.core.Response.Status; - -import org.apache.http.HttpHeaders; - -import io.vertx.ext.web.RoutingContext; -import io.vertx.ext.web.handler.impl.BodyHandlerImpl; - -/** - * 扩展的BodyHandler - * 只支持指定ContentType格式的body数据,关闭了文件上传能力 - */ -public abstract class SimpleBodyHandler extends BodyHandlerImpl { - - @Override - public void handle(RoutingContext context) { - if (this.checkContentType(context)) { - super.handle(context); - } - } - - protected boolean checkContentType(RoutingContext context) { - String contentType = context.request().getHeader(HttpHeaders.CONTENT_TYPE); - if (contentTypeSupported(contentType)) { - return true; - } - Status status = Status.UNSUPPORTED_MEDIA_TYPE; - context.response().setStatusCode(status.getStatusCode()).setStatusMessage(status.getReasonPhrase()); - context.response().end(String.format("Content-Type %s is not supported", contentType)); - return false; - } - - protected abstract boolean contentTypeSupported(String contentType); -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/SimpleJsonObject.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/SimpleJsonObject.java deleted file mode 100644 index 3e8e3da53b6..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/SimpleJsonObject.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx; - -import io.vertx.core.json.JsonObject; - -public class SimpleJsonObject extends JsonObject { - /** - * 直接保存进map,规避原来的put不支持Object的问题 - */ - @Override - public JsonObject put(String key, Object value) { - getMap().put(key, value); - return this; - } - - /** - * 不必复制,直接使用,规避原来的copy不支持Object的问题 - */ - @Override - public JsonObject copy() { - return this; - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/VertxTLSBuilder.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/VertxTLSBuilder.java deleted file mode 100644 index f9ffe9ba2f7..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/VertxTLSBuilder.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx; - -import java.io.File; - -import io.servicecomb.foundation.ssl.SSLCustom; -import io.servicecomb.foundation.ssl.SSLManager; -import io.servicecomb.foundation.ssl.SSLOption; -import io.vertx.core.http.ClientAuth; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.net.ClientOptionsBase; -import io.vertx.core.net.JksOptions; -import io.vertx.core.net.NetServerOptions; -import io.vertx.core.net.PfxOptions; -import io.vertx.core.net.TCPSSLOptions; - -public final class VertxTLSBuilder { - private static final String STORE_PKCS12 = "PKCS12"; - - private static final String STORE_JKS = "JKS"; - - private VertxTLSBuilder() { - - } - - public static NetServerOptions buildNetServerOptions(SSLOption sslOption, SSLCustom sslCustom, - NetServerOptions netServerOptions) { - buildTCPSSLOptions(sslOption, sslCustom, netServerOptions); - if (sslOption.isAuthPeer()) { - netServerOptions.setClientAuth(ClientAuth.REQUIRED); - } else { - netServerOptions.setClientAuth(ClientAuth.REQUEST); - } - return netServerOptions; - } - - public static HttpClientOptions buildHttpClientOptions(SSLOption sslOption, SSLCustom sslCustom, - HttpClientOptions httpClientOptions) { - buildClientOptionsBase(sslOption, sslCustom, httpClientOptions); - httpClientOptions.setVerifyHost(sslOption.isCheckCNHost()); - return httpClientOptions; - } - - public static ClientOptionsBase buildClientOptionsBase(SSLOption sslOption, SSLCustom sslCustom, - ClientOptionsBase clientOptionsBase) { - buildTCPSSLOptions(sslOption, sslCustom, clientOptionsBase); - - if (sslOption.isAuthPeer()) { - clientOptionsBase.setTrustAll(false); - } else { - clientOptionsBase.setTrustAll(true); - } - return clientOptionsBase; - } - - private static TCPSSLOptions buildTCPSSLOptions(SSLOption sslOption, SSLCustom sslCustom, - TCPSSLOptions httpClientOptions) { - httpClientOptions.setSsl(true); - if (isFileExists(sslCustom.getFullPath(sslOption.getKeyStore()))) { - if (STORE_PKCS12.equalsIgnoreCase(sslOption.getKeyStoreType())) { - PfxOptions keyPfxOptions = new PfxOptions(); - keyPfxOptions.setPath(sslCustom.getFullPath(sslOption.getKeyStore())); - keyPfxOptions.setPassword(new String(sslCustom.decode(sslOption.getKeyStoreValue().toCharArray()))); - httpClientOptions.setPfxKeyCertOptions(keyPfxOptions); - } else if (STORE_JKS.equalsIgnoreCase(sslOption.getKeyStoreType())) { - JksOptions keyJksOptions = new JksOptions(); - keyJksOptions.setPath(sslCustom.getFullPath(sslOption.getKeyStore())); - keyJksOptions.setPassword(new String(sslCustom.decode(sslOption.getKeyStoreValue().toCharArray()))); - httpClientOptions.setKeyStoreOptions(keyJksOptions); - } else { - throw new IllegalArgumentException("invalid key store type."); - } - } - - if (isFileExists(sslCustom.getFullPath(sslOption.getTrustStore()))) { - if (STORE_PKCS12.equalsIgnoreCase(sslOption.getTrustStoreType())) { - PfxOptions trustPfxOptions = new PfxOptions(); - trustPfxOptions.setPath(sslCustom.getFullPath(sslOption.getTrustStore())); - trustPfxOptions - .setPassword(new String(sslCustom.decode(sslOption.getTrustStoreValue().toCharArray()))); - httpClientOptions.setPfxTrustOptions(trustPfxOptions); - } else if (STORE_JKS.equalsIgnoreCase(sslOption.getTrustStoreType())) { - JksOptions trustJksOptions = new JksOptions(); - trustJksOptions.setPath(sslCustom.getFullPath(sslOption.getTrustStore())); - trustJksOptions - .setPassword(new String(sslCustom.decode(sslOption.getTrustStoreValue().toCharArray()))); - httpClientOptions.setTrustStoreOptions(trustJksOptions); - } else { - throw new IllegalArgumentException("invalid trust store type."); - } - } - - for (String protocol : sslOption.getProtocols().split(",")) { - httpClientOptions.addEnabledSecureTransportProtocol(protocol); - } - for (String cipher : SSLManager.getEnalbedCiphers(sslOption.getCiphers())) { - httpClientOptions.addEnabledCipherSuite(cipher); - } - - if (isFileExists(sslCustom.getFullPath(sslOption.getCrl()))) { - httpClientOptions.addCrlPath(sslCustom.getFullPath(sslOption.getCrl())); - } - return httpClientOptions; - } - - private static boolean isFileExists(String name) { - if (name == null || name.isEmpty()) { - return false; - } - File f = new File(name); - return f.exists(); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/VertxUtils.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/VertxUtils.java deleted file mode 100644 index 41b1015f29e..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/VertxUtils.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx; - -import java.lang.management.ManagementFactory; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; - -import javax.xml.ws.Holder; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.foundation.vertx.client.AbstractClientVerticle; -import io.servicecomb.foundation.vertx.client.ClientPoolManager; -import io.vertx.core.AbstractVerticle; -import io.vertx.core.Context; -import io.vertx.core.DeploymentOptions; -import io.vertx.core.Vertx; -import io.vertx.core.VertxOptions; -import io.vertx.core.impl.FileResolver; -import io.vertx.core.impl.VertxImplEx; -import io.vertx.core.logging.SLF4JLogDelegateFactory; - -/** - * VertxUtils - * - * - */ -public final class VertxUtils { - static { - // initialize vertx logger, this can be done multiple times - System.setProperty("vertx.logger-delegate-factory-class-name", SLF4JLogDelegateFactory.class.getName()); - io.vertx.core.logging.LoggerFactory.initialise(); - } - - private static final Logger LOGGER = LoggerFactory.getLogger(VertxUtils.class); - - private static final long BLOCKED_THREAD_CHECK_INTERVAL = Long.MAX_VALUE / 2; - - // key为vertx实例名称,以支撑vertx功能分组 - private static Map vertxMap = new ConcurrentHashMap<>(); - - private VertxUtils() { - } - - public static void deployVerticle(Vertx vertx, Class cls, int instanceCount) { - DeploymentOptions options = new DeploymentOptions().setInstances(instanceCount); - - vertx.deployVerticle(cls.getName(), options); - } - - public static DeploymentOptions createClientDeployOptions( - ClientPoolManager clientMgr, - int instanceCount, - int poolCountPerVerticle, CLIENT_OPTIONS clientOptions) { - DeploymentOptions options = new DeploymentOptions().setInstances(instanceCount); - SimpleJsonObject config = new SimpleJsonObject(); - config.put(AbstractClientVerticle.CLIENT_MGR, clientMgr); - config.put(AbstractClientVerticle.POOL_COUNT, poolCountPerVerticle); - config.put(AbstractClientVerticle.CLIENT_OPTIONS, clientOptions); - options.setConfig(config); - - return options; - } - - // deploy Verticle and wait for its success. do not call this method in event-loop thread - public static boolean blockDeploy(Vertx vertx, - Class cls, - DeploymentOptions options) throws InterruptedException { - Holder result = new Holder<>(); - - CountDownLatch latch = new CountDownLatch(1); - vertx.deployVerticle(cls.getName(), options, ar -> { - result.value = ar.succeeded(); - - if (ar.failed()) { - LOGGER.error("deploy vertx failed, cause ", ar.cause()); - } - - latch.countDown(); - }); - - latch.await(); - - return result.value; - } - - public static Vertx getOrCreateVertxByName(String name, VertxOptions vertxOptions) { - Vertx vertx = getVertxByName(name); - if (vertx == null) { - synchronized (VertxUtils.class) { - vertx = getVertxByName(name); - if (vertx == null) { - vertx = init(name, vertxOptions); - vertxMap.put(name, vertx); - } - } - } - - return vertx; - } - - public static Vertx init(VertxOptions vertxOptions) { - return init(null, vertxOptions); - } - - public static Vertx init(String name, VertxOptions vertxOptions) { - if (vertxOptions == null) { - vertxOptions = new VertxOptions(); - } - - boolean isDebug = ManagementFactory.getRuntimeMXBean().getInputArguments().toString().indexOf("jdwp") >= 0; - if (isDebug) { - vertxOptions.setBlockedThreadCheckInterval(BLOCKED_THREAD_CHECK_INTERVAL); - LOGGER.info("in debug mode, disable blocked thread check."); - } - - configureVertxFileCaching(); - return new VertxImplEx(name, vertxOptions); - } - - /** - * 配置vertx的文件缓存功能,默认关闭 - */ - protected static void configureVertxFileCaching() { - if (System.getProperty(FileResolver.DISABLE_CP_RESOLVING_PROP_NAME) == null) { - System.setProperty(FileResolver.DISABLE_CP_RESOLVING_PROP_NAME, "true"); - } - } - - public static Vertx currentVertx() { - Context context = Vertx.currentContext(); - if (context == null) { - throw new RuntimeException("get currentVertx error, currentContext is null."); - } - - return context.owner(); - } - - public static Vertx getVertxByName(String name) { - return vertxMap.get(name); - } - - public static void runInContext(Context context, AsyncResultCallback callback, T result, Throwable e) { - if (context == Vertx.currentContext()) { - complete(callback, result, e); - } else { - context.runOnContext(v -> complete(callback, result, e)); - } - } - - private static void complete(AsyncResultCallback callback, T result, Throwable e) { - if (e != null) { - callback.fail(e.getCause()); - return; - } - - callback.success(result); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/AbstractClientVerticle.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/AbstractClientVerticle.java deleted file mode 100644 index 14c73d77bfa..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/AbstractClientVerticle.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client; - -import io.vertx.core.AbstractVerticle; - -public abstract class AbstractClientVerticle extends AbstractVerticle - implements ClientPoolFactory { - public static final String CLIENT_MGR = "clientMgr"; - - public static final String POOL_COUNT = "poolCount"; - - public static final String CLIENT_OPTIONS = "clientOptions"; - - @SuppressWarnings("unchecked") - @Override - public void start() throws Exception { - ClientPoolManager clientMgr = (ClientPoolManager) config().getValue(CLIENT_MGR); - Integer poolCount = config().getInteger(POOL_COUNT); - - NetThreadData netThreadData = new NetThreadData<>(this, poolCount); - clientMgr.addNetThread(netThreadData); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/ClientPoolFactory.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/ClientPoolFactory.java deleted file mode 100644 index d9e38b30e61..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/ClientPoolFactory.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client; - -public interface ClientPoolFactory { - CLIENT_POOL createClientPool(); -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/ClientPoolManager.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/ClientPoolManager.java deleted file mode 100644 index 7ac3e308cf8..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/ClientPoolManager.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * CLIENT_POOL是一个完备的连接池,支持向同一个目标建立一个或多个连接 - * 之所以再包装一层,是因为多个线程使用一个连接池的场景下 - * 会导致多个线程抢连接池的同一把锁 - * 包装之后,允许使用m个网络线程,每个线程中有n个连接池 - */ -public class ClientPoolManager { - // 多个网络线程 - private List> netThreads = new ArrayList<>(); - - private AtomicInteger bindIndex = new AtomicInteger(); - - // send的调用线程与CLIENT_POOL的绑定关系,不直接用hash,是担心分配不均 - // key是调用者的线程id - // TODO:要不要考虑已经绑定的线程消失了的场景? - private Map threadBindMap = new ConcurrentHashMap<>(); - - private static final Object LOCK = new Object(); - - public void addNetThread(NetThreadData netThread) { - synchronized (LOCK) { - netThreads.add(netThread); - } - } - - public CLIENT_POOL findThreadBindClientPool() { - long threadId = Thread.currentThread().getId(); - CLIENT_POOL clientPool = threadBindMap.get(threadId); - if (clientPool == null) { - synchronized (LOCK) { - clientPool = threadBindMap.get(threadId); - if (clientPool == null) { - int idx = bindIndex.getAndIncrement() % netThreads.size(); - NetThreadData netThread = netThreads.get(idx); - clientPool = netThread.selectClientPool(); - threadBindMap.put(threadId, clientPool); - } - } - } - - return clientPool; - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/NetThreadData.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/NetThreadData.java deleted file mode 100644 index 0c8360abaa4..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/NetThreadData.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client; - -import java.util.concurrent.atomic.AtomicInteger; - -public class NetThreadData { - // 每个网络线程提供一个factory - private ClientPoolFactory factory; - - // 每个网络线程有多个连接池 - private CLIENT_POOL[] pools; - - private AtomicInteger bindIndex = new AtomicInteger(); - - @SuppressWarnings("unchecked") - public NetThreadData(ClientPoolFactory factory, int poolCount) { - this.factory = factory; - pools = (CLIENT_POOL[]) new Object[poolCount]; - } - - public ClientPoolFactory getFactory() { - return factory; - } - - public CLIENT_POOL[] getPools() { - return pools; - } - - public AtomicInteger getBindIndex() { - return bindIndex; - } - - /** - * 在ClientPoolManager中被调用,是被锁保护的 - */ - public CLIENT_POOL selectClientPool() { - int idx = bindIndex.getAndIncrement() % pools.length; - CLIENT_POOL clientPool = pools[idx]; - if (clientPool == null) { - clientPool = factory.createClientPool(); - pools[idx] = clientPool; - } - return clientPool; - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/http/HttpClientVerticle.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/http/HttpClientVerticle.java deleted file mode 100644 index bcbe5e86587..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/http/HttpClientVerticle.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.http; - -import io.servicecomb.foundation.vertx.client.AbstractClientVerticle; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientOptions; - -public class HttpClientVerticle extends AbstractClientVerticle { - @Override - public HttpClientWithContext createClientPool() { - HttpClientOptions httpClientOptions = (HttpClientOptions) config().getValue(CLIENT_OPTIONS); - HttpClient httpClient = vertx.createHttpClient(httpClientOptions); - - return new HttpClientWithContext(httpClient, context); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/http/HttpClientWithContext.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/http/HttpClientWithContext.java deleted file mode 100644 index a3c6c08b43d..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/http/HttpClientWithContext.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.http; - -import io.vertx.core.Context; -import io.vertx.core.http.HttpClient; - -public class HttpClientWithContext { - public interface RunHandler { - void run(HttpClient httpClient); - } - - private HttpClient httpClient; - - private Context context; - - public HttpClientWithContext(HttpClient httpClient, Context context) { - this.httpClient = httpClient; - this.context = context; - } - - public HttpClient getHttpClient() { - return httpClient; - } - - public void runOnContext(RunHandler handler) { - context.runOnContext((v) -> { - handler.run(httpClient); - }); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientConnectionPool.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientConnectionPool.java deleted file mode 100644 index 568f9da76b8..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientConnectionPool.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.tcp; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import io.vertx.core.Context; -import io.vertx.core.net.NetClient; - -public abstract class AbstractTcpClientConnectionPool { - // 是在哪个context中创建的 - protected Context context; - - protected TcpClientConfig clientConfig; - - protected NetClient netClient; - - // key为address - protected Map tcpClientMap = new ConcurrentHashMap<>(); - - public AbstractTcpClientConnectionPool(TcpClientConfig clientConfig, Context context, NetClient netClient) { - this.clientConfig = clientConfig; - this.context = context; - this.netClient = netClient; - - startCheckTimeout(clientConfig, context); - } - - protected void startCheckTimeout(TcpClientConfig clientConfig, Context context) { - context.owner().setPeriodic(clientConfig.getRequestTimeoutMillis(), this::onCheckTimeout); - } - - private void onCheckTimeout(Long event) { - for (TcpClientConnection client : tcpClientMap.values()) { - client.checkTimeout(); - } - } - - public void send(TcpClientConnection tcpClient, AbstractTcpClientPackage tcpClientPackage, - TcpResonseCallback callback) { - tcpClient.send(tcpClientPackage, clientConfig.getRequestTimeoutMillis(), callback); - } - - public T findOrCreateClient(String endpoint) { - T tcpClient = tcpClientMap.get(endpoint); - if (tcpClient == null) { - synchronized (this) { - tcpClient = tcpClientMap.get(endpoint); - if (tcpClient == null) { - tcpClient = create(endpoint); - tcpClientMap.put(endpoint, tcpClient); - } - } - } - - return tcpClient; - } - - protected abstract T create(String endpoint); -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPackage.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPackage.java deleted file mode 100644 index 2824f3e38e1..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPackage.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.foundation.vertx.client.tcp; - -import java.util.concurrent.atomic.AtomicLong; - -import io.servicecomb.foundation.vertx.tcp.TcpOutputStream; - -public abstract class AbstractTcpClientPackage { - private static AtomicLong reqId = new AtomicLong(); - - public static long getAndIncRequestId() { - return reqId.getAndIncrement(); - } - - protected long msgId = getAndIncRequestId(); - - public long getMsgId() { - return msgId; - } - - public abstract TcpOutputStream createStream(); -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientVerticle.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientVerticle.java deleted file mode 100644 index 89a1d44eac6..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientVerticle.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.tcp; - -import io.servicecomb.foundation.vertx.client.AbstractClientVerticle; -import io.vertx.core.impl.VertxInternal; -import io.vertx.core.net.NetClient; -import io.vertx.core.net.impl.NetClientImpl; - -public abstract class AbstractTcpClientVerticle> - extends AbstractClientVerticle

{ - protected TcpClientConfig clientConfig; - - // 每线程一个实例即可 - protected NetClient netClient; - - @Override - public void start() throws Exception { - super.start(); - clientConfig = (TcpClientConfig) config().getValue(CLIENT_OPTIONS); - - // vertx.createNetClient()创建出来的netClient不支持跨线程调用 - netClient = new NetClientImpl((VertxInternal) vertx, clientConfig, false); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientConfig.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientConfig.java deleted file mode 100644 index deafb9fbbb0..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientConfig.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.tcp; - -import io.vertx.core.net.NetClientOptions; - -public class TcpClientConfig extends NetClientOptions { - private static final int DEFAULT_TIMEOUT = 30000; - - private long msRequestTimeout; - - public TcpClientConfig() { - // 30 second - msRequestTimeout = DEFAULT_TIMEOUT; - } - - public long getRequestTimeoutMillis() { - return msRequestTimeout; - } - - public void setRequestTimeoutMillis(long msTimeout) { - this.msRequestTimeout = msTimeout; - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java deleted file mode 100644 index d7b33329aa7..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.tcp; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Queue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.TimeoutException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.foundation.vertx.server.TcpParser; -import io.servicecomb.foundation.vertx.tcp.TcpConnection; -import io.servicecomb.foundation.vertx.tcp.TcpConst; -import io.servicecomb.foundation.vertx.tcp.TcpOutputStream; -import io.vertx.core.AsyncResult; -import io.vertx.core.Context; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.net.NetClient; -import io.vertx.core.net.NetSocket; -import io.vertx.core.net.impl.NetSocketImpl; - -public class TcpClientConnection extends TcpConnection { - private static final Logger LOGGER = LoggerFactory.getLogger(TcpClientConnection.class); - - enum Status { - CONNECTING, - DISCONNECTED, - TRY_LOGIN, - WORKING - } - - private NetClient netClient; - - private TcpClientConfig clientConfig; - - private InetSocketAddress socketAddress; - - private boolean localSupportLogin = false; - - private boolean remoteSupportLogin; - - private volatile Status status = Status.DISCONNECTED; - - // save msg before login success. - // before login, we can not know parameters, like: zip/codec compatibl, and so on - // so can only save package, can not save byteBuf - private Queue packageQueue = new ConcurrentLinkedQueue<>(); - - private volatile Map requestMap = new ConcurrentHashMap<>(); - - public TcpClientConnection(Context context, NetClient netClient, String endpoint, TcpClientConfig clientConfig) { - this.setContext(context); - - this.netClient = netClient; - URIEndpointObject ipPort = new URIEndpointObject(endpoint); - this.socketAddress = ipPort.getSocketAddress(); - this.remoteSupportLogin = Boolean.parseBoolean(ipPort.getFirst(TcpConst.LOGIN)); - this.clientConfig = clientConfig; - } - - public boolean isLocalSupportLogin() { - return localSupportLogin; - } - - public void setLocalSupportLogin(boolean localSupportLogin) { - this.localSupportLogin = localSupportLogin; - } - - protected TcpOutputStream createLogin() { - return null; - } - - protected boolean onLoginResponse(Buffer bodyBuffer) { - return true; - } - - public void send(AbstractTcpClientPackage tcpClientPackage, long msTimeout, - TcpResonseCallback callback) { - requestMap.put(tcpClientPackage.getMsgId(), new TcpRequest(msTimeout, callback)); - - if (writeToBufferQueue(tcpClientPackage)) { - return; - } - - // before login success, no optimize, just make sure do not lost data - context.runOnContext(v -> { - if (!writeToBufferQueue(tcpClientPackage)) { - packageQueue.add(tcpClientPackage); - } - - // connct must call in eventloop thread - // otherwise vertx will create a new eventloop thread for it if count - // of eventloop thread is not up to the limit. - if (Status.DISCONNECTED.equals(status)) { - connect(); - } - }); - } - - private boolean writeToBufferQueue(AbstractTcpClientPackage tcpClientPackage) { - // read status maybe out of eventloop thread, it's not exact - // just optimize for main scenes - if (Status.WORKING.equals(status)) { - // encode in sender thread - try (TcpOutputStream os = tcpClientPackage.createStream()) { - write(os.getByteBuf()); - } - return true; - } - - return false; - } - - @Override - protected void writeInContext() { - writePackageInContext(); - - super.writeInContext(); - } - - private void writePackageInContext() { - for (;;) { - AbstractTcpClientPackage pkg = packageQueue.poll(); - if (pkg == null) { - break; - } - - try (TcpOutputStream os = pkg.createStream()) { - Buffer buf = os.getBuffer(); - netSocket.write(buf); - } - } - } - - private void connect() { - this.status = Status.CONNECTING; - LOGGER.info("connecting to address {}", socketAddress.toString()); - - netClient.connect(socketAddress.getPort(), socketAddress.getHostString(), ar -> { - if (ar.succeeded()) { - onConnectSuccess(ar.result()); - return; - } - - onConnectFailed(ar.cause()); - }); - } - - private void onConnectSuccess(NetSocket socket) { - LOGGER.info("connectd to address {} success in thread {}.", - socketAddress.toString(), - Thread.currentThread().getName()); - // currently, socket always be NetSocketImpl - this.initNetSocket((NetSocketImpl) socket); - socket.handler(new TcpParser(this::onReply)); - - socket.exceptionHandler(this::onException); - socket.closeHandler(this::onClosed); - - // 开始登录 - tryLogin(); - } - - private void onClosed(Void v) { - onDisconnected(new IOException("socket closed")); - } - - // 异常断连时,先触发onException,再触发onClosed - // 正常断连时,只触发onClosed - private void onException(Throwable e) { - LOGGER.error("{} disconnected from {}, in thread {}, cause {}", - netSocket.localAddress().toString(), - socketAddress.toString(), - Thread.currentThread().getName(), - e.getMessage()); - } - - private void onDisconnected(Throwable e) { - this.status = Status.DISCONNECTED; - LOGGER.error("{} disconnected from {}, in thread {}, cause {}", - netSocket.localAddress().toString(), - socketAddress.toString(), - Thread.currentThread().getName(), - e.getMessage()); - - clearCachedRequest(e); - } - - protected void tryLogin() { - if (!localSupportLogin || !remoteSupportLogin) { - LOGGER.error( - "local or remote not support login, address={}, localSupportLogin={}, remoteSupportLogin={}.", - socketAddress.toString(), - localSupportLogin, - remoteSupportLogin); - onLoginSuccess(); - return; - } - - this.status = Status.TRY_LOGIN; - LOGGER.info("try login to address {}", socketAddress.toString()); - - try (TcpOutputStream os = createLogin()) { - requestMap.put(os.getMsgId(), - new TcpRequest(clientConfig.getRequestTimeoutMillis(), this::onLoginResponse)); - netSocket.write(os.getBuffer()); - } - } - - private void onLoginResponse(AsyncResult asyncResult) { - if (asyncResult.failed()) { - LOGGER.error("login failed, address {}", socketAddress.toString(), asyncResult.cause()); - // 在相应回调中设置状态 - netSocket.close(); - return; - } - - if (!onLoginResponse(asyncResult.result().getBodyBuffer())) { - LOGGER.error("login failed, address {}", socketAddress.toString()); - // 在相应回调中设置状态 - netSocket.close(); - return; - } - - LOGGER.info("login success, address {}", socketAddress.toString()); - onLoginSuccess(); - } - - private void onLoginSuccess() { - this.status = Status.WORKING; - writeInContext(); - } - - private void onConnectFailed(Throwable cause) { - // 连接失败 - this.status = Status.DISCONNECTED; - String msg = String.format("connect to address %s failed.", - socketAddress.toString()); - LOGGER.error(msg, cause); - - clearCachedRequest(cause); - } - - protected void clearCachedRequest(Throwable cause) { - // 在onSendError,用户可能发起一次新的调用,需要避免作多余的清理 - Map oldMap = requestMap; - requestMap = new ConcurrentHashMap<>(); - - for (TcpRequest request : oldMap.values()) { - request.onSendError(cause); - } - oldMap.clear(); - } - - protected void onReply(long msgId, Buffer headerBuffer, Buffer bodyBuffer) { - TcpRequest request = requestMap.remove(msgId); - if (request == null) { - LOGGER.error("Unknown reply msgId {}, waiting count {}", msgId, requestMap.size()); - return; - } - - request.onReply(headerBuffer, bodyBuffer); - } - - public void checkTimeout() { - for (Entry entry : requestMap.entrySet()) { - TcpRequest request = entry.getValue(); - if (request.isTimeout()) { - // 可能正好收到reply,且被处理了,所以这里的remove不一定有效 - // 是否有效,根据remove的结果来决定 - request = requestMap.remove(entry.getKey()); - if (request != null) { - String msg = - String.format("request timeout, msgId=%d, address=%s", entry.getKey(), socketAddress); - LOGGER.error(msg); - - request.onTimeout(new TimeoutException(msg)); - } - } - } - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientConnectionPool.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientConnectionPool.java deleted file mode 100644 index 5b026ea0329..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientConnectionPool.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.foundation.vertx.client.tcp; - -import io.vertx.core.Context; -import io.vertx.core.net.NetClient; - -public class TcpClientConnectionPool extends AbstractTcpClientConnectionPool { - public TcpClientConnectionPool(TcpClientConfig clientConfig, Context context, NetClient netClient) { - super(clientConfig, context, netClient); - } - - /** - * {@inheritDoc} - */ - @Override - protected TcpClientConnection create(String endpoint) { - return new TcpClientConnection(context, netClient, endpoint, clientConfig); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientPackage.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientPackage.java deleted file mode 100644 index 809c1cddfb9..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientPackage.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.foundation.vertx.client.tcp; - -import io.servicecomb.foundation.vertx.tcp.TcpOutputStream; - -public class TcpClientPackage extends AbstractTcpClientPackage { - private TcpOutputStream os; - - public TcpClientPackage(TcpOutputStream os) { - this.os = os; - } - - @Override - public TcpOutputStream createStream() { - return os; - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientVerticle.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientVerticle.java deleted file mode 100644 index b5fa1525fb1..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientVerticle.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.foundation.vertx.client.tcp; - -public class TcpClientVerticle extends AbstractTcpClientVerticle { - - @Override - public TcpClientConnectionPool createClientPool() { - return new TcpClientConnectionPool(clientConfig, context, netClient); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpData.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpData.java deleted file mode 100644 index 35bf34c7503..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpData.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.tcp; - -import io.vertx.core.buffer.Buffer; - -public class TcpData { - private Buffer headerBuffer; - - private Buffer bodyBuffer; - - public TcpData(Buffer headerBuffer, Buffer bodyBuffer) { - this.headerBuffer = headerBuffer; - this.bodyBuffer = bodyBuffer; - } - - public Buffer getHeaderBuffer() { - return headerBuffer; - } - - public void setHeaderBuffer(Buffer headerBuffer) { - this.headerBuffer = headerBuffer; - } - - public Buffer getBodyBuffer() { - return bodyBuffer; - } - - public void setBodyBuffer(Buffer bodyBuffer) { - this.bodyBuffer = bodyBuffer; - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpRequest.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpRequest.java deleted file mode 100644 index eaa5f0d7fd9..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpRequest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.tcp; - -import java.util.concurrent.TimeoutException; - -import io.vertx.core.Context; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; - -public class TcpRequest { - private long begin; - - private long msTimeout; - - private Context callContext; - - private long threadId; - - private TcpResonseCallback responseCallback; - - public TcpRequest(long msTimeout, TcpResonseCallback responseCallback) { - callContext = Vertx.currentContext(); - threadId = Thread.currentThread().getId(); - this.begin = System.currentTimeMillis(); - this.msTimeout = msTimeout; - this.responseCallback = responseCallback; - } - - public void onReply(Buffer headerBuffer, Buffer bodyBuffer) { - TcpData tcpData = new TcpData(headerBuffer, bodyBuffer); - - if (callContext == null || threadId == Thread.currentThread().getId()) { - responseCallback.success(tcpData); - return; - } - - callContext.runOnContext(Void -> { - responseCallback.success(tcpData); - }); - } - - public void onSendError(Throwable e) { - responseCallback.fail(e); - } - - public boolean isTimeout() { - return System.currentTimeMillis() - begin >= msTimeout; - } - - public void onTimeout(TimeoutException e) { - responseCallback.fail(e); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpResonseCallback.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpResonseCallback.java deleted file mode 100644 index bc19ec4bdaf..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/client/tcp/TcpResonseCallback.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.tcp; - -import io.servicecomb.foundation.vertx.AsyncResultCallback; - -public interface TcpResonseCallback extends AsyncResultCallback { - -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/executor/VertxWorkerExecutor.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/executor/VertxWorkerExecutor.java deleted file mode 100644 index 93f64d5cead..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/executor/VertxWorkerExecutor.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.executor; - -import java.util.concurrent.Executor; - -import io.vertx.core.Vertx; - -public class VertxWorkerExecutor implements Executor { - - @Override - public void execute(Runnable command) { - Vertx.currentContext().owner().executeBlocking(future -> { - command.run(); - }, - false, - null); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/server/TcpBufferHandler.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/server/TcpBufferHandler.java deleted file mode 100644 index 8cce7b7be3b..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/server/TcpBufferHandler.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.server; - -import io.vertx.core.buffer.Buffer; - -/** - * TcpBufferHandler - * - * - */ -public interface TcpBufferHandler { - void handle(long msgId, Buffer headerBuffer, Buffer bodyBuffer); -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/server/TcpParser.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/server/TcpParser.java deleted file mode 100644 index 7820fc6e478..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/server/TcpParser.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.server; - -import java.io.UnsupportedEncodingException; - -import io.netty.buffer.ByteBuf; -import io.vertx.core.Handler; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.parsetools.RecordParser; -import io.vertx.core.parsetools.impl.RecordParserImpl; - -/** - * TcpParser - * - * - */ -public class TcpParser implements Handler { - public static final byte[] TCP_MAGIC; - - public static final int TCP_HEADER_LENGTH = 23; - - static { - try { - TCP_MAGIC = "CSE.TCP".getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - - enum ParseStatus { - TCP_HEADER, - TCP_PAYLOAD - } - - private TcpBufferHandler outputHandler; - - private RecordParser parser; - - private ParseStatus status; - - private long msgId; - - // 仅仅是header + body,不包括headerLen本身 - private int totalLen; - - private int headerLen; - - public TcpParser(TcpBufferHandler output) { - this.outputHandler = output; - - reset(); - } - - /** - * 在解析出错时,通过重新创建parser对象,将整个缓冲区重置 - */ - protected void reset() { - parser = RecordParserImpl.newFixed(TCP_HEADER_LENGTH, this::onParse); - status = ParseStatus.TCP_HEADER; - - parser.handle(Buffer.buffer(0)); - } - - public boolean firstNEqual(byte[] a, byte[] b, int n) { - assert a.length >= n && b.length >= n; - - for (int i = 0; i < n; i++) { - if (a[i] != b[i]) { - return false; - } - } - return true; - } - - protected void onParse(Buffer buffer) { - switch (status) { - case TCP_HEADER: - ByteBuf buf = buffer.getByteBuf(); - if (!firstNEqual(TCP_MAGIC, buf.array(), TCP_MAGIC.length)) { - reset(); - return; - } - - buf.skipBytes(TCP_MAGIC.length); - msgId = buf.readLong(); - totalLen = buf.readInt(); - headerLen = buf.readInt(); - - if (totalLen == 0) { - onReadOnePackage(null, null); - return; - } - - parser.fixedSizeMode(totalLen); - status = ParseStatus.TCP_PAYLOAD; - break; - - case TCP_PAYLOAD: - Buffer headerBuffer = buffer.slice(0, headerLen); - Buffer bodyBuffer = buffer.slice(headerLen, buffer.length()); - onReadOnePackage(headerBuffer, bodyBuffer); - break; - - default: - break; - } - } - - private void onReadOnePackage(Buffer headerBuffer, Buffer bodyBuffer) { - outputHandler.handle(msgId, headerBuffer, bodyBuffer); - - parser.fixedSizeMode(TCP_HEADER_LENGTH); - status = ParseStatus.TCP_HEADER; - } - - public void handle(Buffer buf) { - parser.handle(buf); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/server/TcpServer.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/server/TcpServer.java deleted file mode 100644 index c1dcad5d90c..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/server/TcpServer.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.server; - -import java.net.InetSocketAddress; - -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.foundation.ssl.SSLCustom; -import io.servicecomb.foundation.ssl.SSLOption; -import io.servicecomb.foundation.ssl.SSLOptionFactory; -import io.servicecomb.foundation.vertx.AsyncResultCallback; -import io.servicecomb.foundation.vertx.VertxTLSBuilder; -import io.vertx.core.Vertx; -import io.vertx.core.net.NetServer; -import io.vertx.core.net.NetServerOptions; - -public class TcpServer { - private URIEndpointObject endpointObject; - - public TcpServer(URIEndpointObject endpointObject) { - this.endpointObject = endpointObject; - } - - public void init(Vertx vertx, String sslKey, AsyncResultCallback callback) { - NetServer netServer; - if (endpointObject.isSslEnabled()) { - SSLOptionFactory factory = - SSLOptionFactory.createSSLOptionFactory(sslKey, null); - SSLOption sslOption; - if (factory == null) { - sslOption = SSLOption.buildFromYaml(sslKey); - } else { - sslOption = factory.createSSLOption(); - } - SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); - NetServerOptions serverOptions = new NetServerOptions(); - VertxTLSBuilder.buildNetServerOptions(sslOption, sslCustom, serverOptions); - netServer = vertx.createNetServer(serverOptions); - } else { - netServer = vertx.createNetServer(); - } - - netServer.connectHandler(netSocket -> { - TcpServerConnection connection = createTcpServerConnection(); - connection.init(netSocket); - }); - - InetSocketAddress socketAddress = endpointObject.getSocketAddress(); - netServer.listen(socketAddress.getPort(), socketAddress.getHostString(), ar -> { - if (ar.succeeded()) { - callback.success(socketAddress); - return; - } - - // 监听失败 - String msg = String.format("listen failed, address=%s", socketAddress.toString()); - callback.fail(new Exception(msg, ar.cause())); - }); - } - - protected TcpServerConnection createTcpServerConnection() { - return new TcpServerConnection(); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/server/TcpServerConnection.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/server/TcpServerConnection.java deleted file mode 100644 index a865934e7c9..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/server/TcpServerConnection.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.foundation.vertx.server; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.foundation.vertx.tcp.TcpConnection; -import io.vertx.core.net.NetSocket; -import io.vertx.core.net.impl.NetSocketImpl; - -public class TcpServerConnection extends TcpConnection { - private static final Logger LOGGER = LoggerFactory.getLogger(TcpServerConnection.class); - - protected TcpParser splitter; - - public void init(NetSocket netSocket) { - // currently, socket always be NetSocketImpl - this.initNetSocket((NetSocketImpl) netSocket); - - String remoteAddress = netSocket.remoteAddress().toString(); - LOGGER.info("connect from {}, in thread {}", - remoteAddress, - Thread.currentThread().getName()); - netSocket.exceptionHandler(e -> { - LOGGER.error("disconected from {}, in thread {}, cause {}", - remoteAddress, - Thread.currentThread().getName(), - e.getMessage()); - }); - netSocket.closeHandler(Void -> { - LOGGER.error("disconected from {}, in thread {}", - remoteAddress, - Thread.currentThread().getName()); - }); - - netSocket.handler(splitter); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/stream/BufferInputStream.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/stream/BufferInputStream.java deleted file mode 100644 index bcf9a3814fc..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/stream/BufferInputStream.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.stream; - -import java.io.InputStream; -import java.nio.charset.StandardCharsets; - -import io.netty.buffer.ByteBuf; - -/** - * 本stream不必close - * - * - */ -public class BufferInputStream extends InputStream { - private ByteBuf byteBuf; - - public BufferInputStream(ByteBuf buffer) { - this.byteBuf = buffer; - } - - @Override - public long skip(long len) { - int skipLen = Math.min((int) len, available()); - byteBuf.skipBytes(skipLen); - return skipLen; - } - - public byte readByte() { - return byteBuf.readByte(); - } - - @Override - public int read() { - return byteBuf.readByte(); - } - - public boolean readBoolean() { - return byteBuf.readBoolean(); - } - - public short readShort() { - return byteBuf.readShort(); - } - - public int readInt() { - return byteBuf.readInt(); - } - - public long readLong() { - return byteBuf.readLong(); - } - - public int getIndex() { - return byteBuf.readerIndex(); - } - - public String readString() { - int length = readInt(); - byte[] bytes = new byte[length]; - byteBuf.readBytes(bytes); - return new String(bytes, StandardCharsets.UTF_8); - } - - @Override - public int read(byte[] b) { - return read(b, 0, b.length); - } - - @Override - public int read(byte[] b, int off, int len) { - int avail = available(); - if (len > avail) { - len = avail; - } - - if (len == 0) { - return -1; - } - - byteBuf.readBytes(b, off, len); - return len; - } - - @Override - public int available() { - return byteBuf.readableBytes(); - } - - @Override - public void close() { - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/stream/BufferOutputStream.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/stream/BufferOutputStream.java deleted file mode 100644 index 0de9a99e7c5..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/stream/BufferOutputStream.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.stream; - -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; - -import io.netty.buffer.ByteBuf; -import io.vertx.core.buffer.Buffer; - -/** - * BufferOutputStream - * - * - */ -public class BufferOutputStream extends OutputStream { - private static final int DIRECT_BUFFER_SIZE = 1024; - - protected ByteBuf byteBuf; - - private boolean needReleaseBuffer; - - public BufferOutputStream() { - // TODO:默认大小加配置项 - // TODO:如何与pool配合起来,vertx中默认都是unpool的,我们的阻塞模式下,申请与释放也不在一个线程,估计更用不上? - // 后续通道没问题了,再来验证这个问题 - // this(PooledByteBufAllocator.DEFAULT.directBuffer()); - - // this(PooledByteBufAllocator.DEFAULT.directBuffer(DIRECT_BUFFER_SIZE)); - - // this(UnpooledByteBufAllocator.DEFAULT.directBuffer(DIRECT_BUFFER_SIZE)); - // needReleaseBuffer = false; - - // this(UnpooledByteBufAllocator.DEFAULT.heapBuffer(DIRECT_BUFFER_SIZE)); - - this(Buffer.buffer(DIRECT_BUFFER_SIZE).getByteBuf()); - needReleaseBuffer = false; - } - - - public BufferOutputStream(ByteBuf buffer) { - this.byteBuf = buffer; - } - - public ByteBuf getByteBuf() { - return byteBuf; - } - - public Buffer getBuffer() { - return Buffer.buffer(byteBuf); - } - - public int length() { - return byteBuf.readableBytes(); - } - - public void writeByte(byte value) { - byteBuf.writeByte(value); - } - - // 实际是写byte - @Override - public void write(int byteValue) { - byteBuf.writeByte((byte) byteValue); - } - - public void write(boolean value) { - byteBuf.writeBoolean(value); - } - - public void writeInt(int pos, int value) { - byteBuf.setInt(pos, value); - } - - public void writeShort(short value) { - byteBuf.writeShort(value); - } - - public void writeInt(int value) { - byteBuf.writeInt(value); - } - - public void writeLong(long value) { - byteBuf.writeLong(value); - } - - public void writeString(String value) { - byteBuf.writeInt(value.length()); - byteBuf.writeCharSequence(value, StandardCharsets.UTF_8); - } - - public void write(byte[] b) { - write(b, 0, b.length); - } - - @Override - public void write(byte[] bytes, int offset, int len) { - byteBuf.writeBytes(bytes, offset, len); - } - - @Override - public void close() { - if (needReleaseBuffer && byteBuf != null) { - byteBuf.release(); - } - } - - public int writerIndex() { - return byteBuf.writerIndex(); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/tcp/TcpConnection.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/tcp/TcpConnection.java deleted file mode 100644 index c45de186bed..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/tcp/TcpConnection.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.foundation.vertx.tcp; - -import java.util.Queue; -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; -import io.vertx.core.net.impl.NetSocketImpl; -import io.vertx.core.net.impl.VertxHelper; - -public class TcpConnection { - protected String protocol; - - // 压缩算法名字 - protected String zipName; - - protected NetSocket netSocket; - - // context of netSocket - protected Context context; - - // vertx's wrap for netty is ugly: write always lock first - // so we save buf in a CAS queue, and notify eventloop thread to do the real write - // - // netty: - // if write caller is not in network IO thread - // always wrap the write as a task, and put it to IO thread queue(MPSC queue) - // - // so this optimization: - // 1.avoid vertx's lock - // 2.reduce netty's task schedule - private Queue writeQueue = new ConcurrentLinkedQueue<>(); - - private AtomicLong writeQueueSize = new AtomicLong(); - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - public String getZipName() { - return zipName; - } - - public void setZipName(String zipName) { - this.zipName = zipName; - } - - public void setContext(Context context) { - this.context = context; - } - - public NetSocket getNetSocket() { - return netSocket; - } - - public void initNetSocket(NetSocketImpl netSocket) { - this.netSocket = netSocket; - - this.context = VertxHelper.getConnectionContext(netSocket); - } - - public void write(ByteBuf buf) { - writeQueue.add(buf); - long oldSize = writeQueueSize.getAndIncrement(); - if (oldSize == 0) { - scheduleWrite(); - } - } - - // notify context thread to write - protected void scheduleWrite() { - context.runOnContext(v -> { - writeInContext(); - }); - } - - protected void writeInContext() { - CompositeByteBuf cbb = ByteBufAllocator.DEFAULT.compositeBuffer(); - for (;;) { - ByteBuf buf = writeQueue.poll(); - if (buf == null) { - break; - } - - writeQueueSize.decrementAndGet(); - cbb.addComponent(true, buf); - - if (cbb.numComponents() == cbb.maxNumComponents()) { - netSocket.write(Buffer.buffer(cbb)); - cbb = ByteBufAllocator.DEFAULT.compositeBuffer(); - } - } - if (cbb.isReadable()) { - netSocket.write(Buffer.buffer(cbb)); - } - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/tcp/TcpConst.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/tcp/TcpConst.java deleted file mode 100644 index 543752ba480..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/tcp/TcpConst.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.foundation.vertx.tcp; - -public final class TcpConst { - private TcpConst() { - } - - public static final String LOGIN = "login"; -} diff --git a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/tcp/TcpOutputStream.java b/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/tcp/TcpOutputStream.java deleted file mode 100644 index 56a90d44e1e..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/servicecomb/foundation/vertx/tcp/TcpOutputStream.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.tcp; - -import io.servicecomb.foundation.vertx.server.TcpParser; -import io.servicecomb.foundation.vertx.stream.BufferOutputStream; - -/** - * TcpOutputStream - * - * - */ -public class TcpOutputStream extends BufferOutputStream { - private long msgId; - - public TcpOutputStream(long msgId) { - super(); - - this.msgId = msgId; - write(TcpParser.TCP_MAGIC); - writeLong(msgId); - } - - public long getMsgId() { - return msgId; - } - - public void writeLength(int totalLen, int headerLen) { - writeInt(totalLen); - writeInt(headerLen); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/vertx/core/impl/VertxImplEx.java b/foundations/foundation-vertx/src/main/java/io/vertx/core/impl/VertxImplEx.java deleted file mode 100644 index 705b3901064..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/vertx/core/impl/VertxImplEx.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.vertx.core.impl; - -import java.lang.reflect.Field; - -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; - -import io.vertx.core.VertxOptions; - -public class VertxImplEx extends VertxImpl { - public VertxImplEx(String name, VertxOptions vertxOptions) { - super(vertxOptions); - - if (StringUtils.isEmpty(name)) { - return; - } - - Field field = ReflectionUtils.findField(VertxImpl.class, "eventLoopThreadFactory"); - field.setAccessible(true); - VertxThreadFactory eventLoopThreadFactory = (VertxThreadFactory) ReflectionUtils.getField(field, this); - - field = ReflectionUtils.findField(eventLoopThreadFactory.getClass(), "prefix"); - field.setAccessible(true); - - String prefix = (String) ReflectionUtils.getField(field, eventLoopThreadFactory); - ReflectionUtils.setField(field, eventLoopThreadFactory, name + "-" + prefix); - } -} diff --git a/foundations/foundation-vertx/src/main/java/io/vertx/core/net/impl/VertxHelper.java b/foundations/foundation-vertx/src/main/java/io/vertx/core/net/impl/VertxHelper.java deleted file mode 100644 index 667a5d5c769..00000000000 --- a/foundations/foundation-vertx/src/main/java/io/vertx/core/net/impl/VertxHelper.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.vertx.core.net.impl; - -import io.vertx.core.Context; - -public final class VertxHelper { - private VertxHelper() { - } - - public static Context getConnectionContext(ConnectionBase connection) { - return connection.getContext(); - } -} 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 new file mode 100644 index 00000000000..2d4ee389642 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AddressResolverConfig.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.vertx; + +import java.util.Arrays; +import java.util.List; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.dns.AddressResolverOptions; + +public class AddressResolverConfig { + + private static final Logger LOGGER = LoggerFactory.getLogger(AddressResolverConfig.class); + + /** + * get the target endpoints with custom address resolve config + * @param tag config tag, such as sc.consumer or cc.consumer + * @return AddressResolverOptions + */ + public static AddressResolverOptions getAddressResolverOptions(String tag) { + AddressResolverOptions addressResolverOptions = new AddressResolverOptions(); + addressResolverOptions + .setServers(getStringListProperty( + AddressResolverOptions.DEFAULT_SERVERS, + "addressResolver." + tag + ".servers", + "addressResolver.servers")); + addressResolverOptions + .setOptResourceEnabled(getBooleanProperty( + AddressResolverOptions.DEFAULT_OPT_RESOURCE_ENABLED, + "addressResolver." + tag + ".optResourceEnabled", + "addressResolver.optResourceEnabled")); + addressResolverOptions + .setCacheMinTimeToLive(getPositiveIntProperty( + AddressResolverOptions.DEFAULT_CACHE_MIN_TIME_TO_LIVE, + "addressResolver." + tag + ".cacheMinTimeToLive", + "addressResolver.cacheMinTimeToLive")); + addressResolverOptions + .setCacheMaxTimeToLive(getPositiveIntProperty( + AddressResolverOptions.DEFAULT_CACHE_MAX_TIME_TO_LIVE, + "addressResolver." + tag + ".cacheMaxTimeToLive", + "addressResolver.cacheMaxTimeToLive")); + addressResolverOptions + .setCacheNegativeTimeToLive(getPositiveIntProperty( + AddressResolverOptions.DEFAULT_CACHE_NEGATIVE_TIME_TO_LIVE, + "addressResolver." + tag + ".cacheNegativeTimeToLive", + "addressResolver.cacheNegativeTimeToLive")); + addressResolverOptions + .setQueryTimeout(getPositiveIntProperty( + AddressResolverOptions.DEFAULT_QUERY_TIMEOUT, + "addressResolver." + tag + ".queryTimeout", + "addressResolver.queryTimeout")); + addressResolverOptions + .setMaxQueries(getPositiveIntProperty( + AddressResolverOptions.DEFAULT_MAX_QUERIES, + "addressResolver." + tag + ".maxQueries", + "addressResolver.maxQueries")); + addressResolverOptions + .setRdFlag(getBooleanProperty( + AddressResolverOptions.DEFAULT_RD_FLAG, + "addressResolver." + tag + ".rdFlag", + "addressResolver.rdFlag")); + addressResolverOptions + .setSearchDomains(getStringListProperty( + AddressResolverOptions.DEFAULT_SEARCH_DOMAINS, + "addressResolver." + tag + ".searchDomains", + "addressResolver.searchDomains")); + addressResolverOptions + .setNdots(getPositiveIntProperty( + AddressResolverOptions.DEFAULT_NDOTS, + "addressResolver." + tag + ".ndots", + "addressResolver.ndots")); + addressResolverOptions + .setRotateServers(getBooleanProperty( + AddressResolverOptions.DEFAULT_ROTATE_SERVERS, + "addressResolver." + tag + ".rotateServers", + "addressResolver.rotateServers")); + return addressResolverOptions; + } + + private static List getStringListProperty(List defaultValue, String... keys) { + for (String key : keys) { + String[] vals = LegacyPropertyFactory.getProperty(key, String[].class); + if (vals != null && vals.length > 0) { + return Arrays.asList(vals); + } + } + return defaultValue; + } + + private static int getPositiveIntProperty(int defaultValue, String... keys) { + for (String key : keys) { + Integer val = LegacyPropertyFactory.getProperty(key, Integer.class); + if (val != null && val <= 0) { + LOGGER.warn("Address resolver key:{}'s value:{} is not positive, please check!", key, val); + continue; + } + if (val != null) { + return val; + } + } + return defaultValue; + } + + private static boolean getBooleanProperty(boolean defaultValue, + String... keys) { + for (String key : keys) { + Boolean val = LegacyPropertyFactory.getProperty(key, Boolean.class); + if (val != null) { + return val; + } + } + return defaultValue; + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AsyncResultCallback.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AsyncResultCallback.java new file mode 100644 index 00000000000..1a3cd3043b0 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AsyncResultCallback.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.foundation.vertx; + +import io.vertx.core.AsyncResult; +import io.vertx.core.Future; +import io.vertx.core.Handler; + +public interface AsyncResultCallback extends Handler> { + default void success(T data) { + handle(Future.succeededFuture(data)); + } + + default void fail(Throwable e) { + handle(Future.failedFuture(e)); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/ConnectionEvent.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/ConnectionEvent.java new file mode 100644 index 00000000000..359f9285337 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/ConnectionEvent.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.foundation.vertx; + +public enum ConnectionEvent { + Connected, + Closed +} 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 new file mode 100644 index 00000000000..9f2d3af797b --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SharedVertxFactory.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.foundation.vertx; + +import org.apache.servicecomb.foundation.vertx.metrics.DefaultVertxMetricsFactory; +import org.apache.servicecomb.foundation.vertx.metrics.MetricsOptionsEx; +import org.springframework.core.env.Environment; + +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.shareddata.Shareable; + +public class SharedVertxFactory { + static class SharedVertxInfo implements Shareable { + public VertxOptions vertxOptions = new VertxOptions(); + + public DefaultVertxMetricsFactory metricsFactory = new DefaultVertxMetricsFactory(); + + public MetricsOptionsEx metricsOptionsEx = (MetricsOptionsEx) metricsFactory.newOptions(); + + public SharedVertxInfo(Environment environment) { + vertxOptions.setMetricsOptions(metricsOptionsEx); + vertxOptions.setEventLoopPoolSize(readEventLoopPoolSize(environment, "servicecomb.transport.eventloop.size")); + } + + private static int readEventLoopPoolSize(Environment environment, String key) { + int count = environment.getProperty(key, int.class, -1); + if (count > 0) { + return count; + } + return VertxOptions.DEFAULT_EVENT_LOOP_POOL_SIZE; + } + } + + private static final String LOCAL_MAP_NAME = "scb"; + + private static final String INFO = "transport-vertx-info"; + + 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(Environment environment) { + return VertxUtils.getVertxMap().computeIfAbsent("transport", + key -> createSharedVertx(environment, key)); + } + + private static Vertx createSharedVertx(Environment environment, String name) { + SharedVertxInfo info = new SharedVertxInfo(environment); + + Vertx vertx = VertxUtils.init(name, info.vertxOptions, info.metricsFactory); + info.metricsFactory.setVertx(vertx, info.vertxOptions); + vertx.sharedData().getLocalMap(LOCAL_MAP_NAME).put(INFO, info); + + return vertx; + } +} 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 new file mode 100644 index 00000000000..7f089ead415 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SimpleBodyHandler.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.foundation.vertx; + +import jakarta.ws.rs.core.Response.Status; + +import org.apache.http.HttpHeaders; + +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.impl.BodyHandlerImpl; + +/** + * 扩展的BodyHandler + * 只支持指定ContentType格式的body数据,关闭了文件上传能力 + */ +public abstract class SimpleBodyHandler extends BodyHandlerImpl { + + @Override + public void handle(RoutingContext context) { + if (this.checkContentType(context)) { + super.handle(context); + } + } + + protected boolean checkContentType(RoutingContext context) { + String contentType = context.request().getHeader(HttpHeaders.CONTENT_TYPE); + if (contentTypeSupported(contentType)) { + return true; + } + Status status = Status.UNSUPPORTED_MEDIA_TYPE; + context.response().setStatusCode(status.getStatusCode()).setStatusMessage(status.getReasonPhrase()); + context.response().end(String.format("Content-Type %s is not supported", contentType)); + return false; + } + + protected abstract boolean contentTypeSupported(String contentType); +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SimpleJsonObject.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SimpleJsonObject.java new file mode 100644 index 00000000000..32dbe2680a4 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SimpleJsonObject.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.foundation.vertx; + +import io.vertx.core.json.JsonObject; + +public class SimpleJsonObject extends JsonObject { + /** + * 直接保存进map,规避原来的put不支持Object的问题 + */ + @Override + public JsonObject put(String key, Object value) { + getMap().put(key, value); + return this; + } + + /** + * 不必复制,直接使用,规避原来的copy不支持Object的问题 + */ + @Override + public JsonObject copy() { + return this; + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/TransportType.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/TransportType.java new file mode 100644 index 00000000000..0d8489dc032 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/TransportType.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.foundation.vertx; + +public enum TransportType { + Highway, + Rest +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxConst.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxConst.java new file mode 100644 index 00000000000..1ee2afa27d2 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxConst.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.foundation.vertx; + +public final class VertxConst { + public static final String PROXY_PRE_NAME = "servicecomb.proxy."; + + public static final String PROXY_ENABLE = PROXY_PRE_NAME + "enable"; + + public static final String PROXY_HOST = PROXY_PRE_NAME + "host"; + + public static final String PROXY_PORT = PROXY_PRE_NAME + "port"; + + public static final String PROXY_USERNAME = PROXY_PRE_NAME + "username"; + + public static final String PROXY_PASSWD = PROXY_PRE_NAME + "passwd"; +} 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 new file mode 100644 index 00000000000..ba2203338e6 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxTLSBuilder.java @@ -0,0 +1,207 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.File; +import java.net.URL; +import java.util.Arrays; +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; +import org.apache.servicecomb.foundation.ssl.SSLOptionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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; +import io.vertx.core.net.OpenSSLEngineOptions; +import io.vertx.core.net.PfxOptions; +import io.vertx.core.net.TCPSSLOptions; + +public final class VertxTLSBuilder { + private static final Logger LOGGER = LoggerFactory.getLogger(VertxTLSBuilder.class); + + private static final String STORE_PKCS12 = "PKCS12"; + + private static final String STORE_JKS = "JKS"; + + private VertxTLSBuilder() { + + } + + public static NetServerOptions buildNetServerOptions(SSLOption sslOption, SSLCustom sslCustom, + NetServerOptions netServerOptions) { + buildTCPSSLOptions(sslOption, sslCustom, netServerOptions); + setClientAuth(sslOption, netServerOptions); + return netServerOptions; + } + + private static void setClientAuth(SSLOption sslOption, NetServerOptions netServerOptions) { + if (sslOption.isAuthPeer() || org.apache.servicecomb.foundation.ssl.ClientAuth.REQUIRED + .equals(sslOption.getClientAuth())) { + netServerOptions.setClientAuth(ClientAuth.REQUIRED); + return; + } + + if (org.apache.servicecomb.foundation.ssl.ClientAuth.NONE + .equals(sslOption.getClientAuth())) { + netServerOptions.setClientAuth(ClientAuth.NONE); + return; + } + + netServerOptions.setClientAuth(ClientAuth.REQUEST); + return; + } + + public static void buildHttpClientOptions(String sslKey, HttpClientOptions httpClientOptions) { + 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()); + 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); + httpClientOptions.setVerifyHost(sslOption.isCheckCNHost()); + 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); + + if (sslOption.isAuthPeer()) { + clientOptionsBase.setTrustAll(false); + } else { + clientOptionsBase.setTrustAll(true); + } + return clientOptionsBase; + } + + private static TCPSSLOptions buildTCPSSLOptions(SSLOption sslOption, SSLCustom sslCustom, + TCPSSLOptions tcpClientOptions) { + tcpClientOptions.setSsl(true); + + if (sslOption.getEngine().equalsIgnoreCase("openssl")) { + tcpClientOptions.setSslEngineOptions(new OpenSSLEngineOptions()); + } + String fullKeyStore = sslCustom.getFullPath(sslOption.getKeyStore()); + if (isFileExists(fullKeyStore)) { + if (STORE_PKCS12.equalsIgnoreCase(sslOption.getKeyStoreType())) { + PfxOptions keyPfxOptions = new PfxOptions(); + keyPfxOptions.setPath(fullKeyStore); + keyPfxOptions.setPassword(new String(sslCustom.decode(sslOption.getKeyStoreValue().toCharArray()))); + 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.setKeyCertOptions(keyJksOptions); + } else { + throw new IllegalArgumentException("invalid key store type."); + } + } else { + LOGGER.warn("keyStore [" + fullKeyStore + "] file not exist, please check!"); + } + String fullTrustStore = sslCustom.getFullPath(sslOption.getTrustStore()); + if (isFileExists(fullTrustStore)) { + if (STORE_PKCS12.equalsIgnoreCase(sslOption.getTrustStoreType())) { + PfxOptions trustPfxOptions = new PfxOptions(); + trustPfxOptions.setPath(fullTrustStore); + trustPfxOptions + .setPassword(new String(sslCustom.decode(sslOption.getTrustStoreValue().toCharArray()))); + 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.setTrustOptions(trustJksOptions); + } else { + throw new IllegalArgumentException("invalid trust store type."); + } + } else { + LOGGER.warn("trustStore [" + fullTrustStore + "] file not exist, please check!"); + } + + tcpClientOptions + .setEnabledSecureTransportProtocols(new HashSet<>(Arrays.asList(sslOption.getProtocols().split(",")))); + + for (String cipher : SSLManager.getEnabledCiphers(sslOption)) { + tcpClientOptions.addEnabledCipherSuite(cipher); + } + + if (isFileExists(sslCustom.getFullPath(sslOption.getCrl()))) { + tcpClientOptions.addCrlPath(sslCustom.getFullPath(sslOption.getCrl())); + } + return tcpClientOptions; + } + + private static boolean isFileExists(String name) { + if (StringUtils.isEmpty(name)) { + return false; + } + File f = new File(name); + if (f.isFile()) { + return true; + } + + 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 new file mode 100644 index 00000000000..5eceac0b24d --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxUtils.java @@ -0,0 +1,245 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 org.apache.commons.io.IOUtils; +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; +import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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.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 + * + * + */ +public final class VertxUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(VertxUtils.class); + + private static final long BLOCKED_THREAD_CHECK_INTERVAL = Long.MAX_VALUE / 2; + + // key为vertx实例名称,以支撑vertx功能分组 + private static final Map vertxMap = new ConcurrentHashMapEx<>(); + + private VertxUtils() { + } + + public static Map getVertxMap() { + return vertxMap; + } + + public static void deployVerticle(Vertx vertx, Class cls, int instanceCount) { + DeploymentOptions options = new DeploymentOptions().setInstances(instanceCount); + vertx.deployVerticle(cls.getName(), options); + } + + public static DeploymentOptions createClientDeployOptions( + ClientPoolManager clientMgr, + int instanceCount) { + DeploymentOptions options = new DeploymentOptions().setInstances(instanceCount); + SimpleJsonObject config = new SimpleJsonObject(); + config.put(ClientVerticle.CLIENT_MGR, clientMgr); + options.setConfig(config); + + return options; + } + + // deploy Verticle and wait for its success. do not call this method in event-loop thread + public static Map blockDeploy(Vertx vertx, + Class cls, + DeploymentOptions options) throws InterruptedException { + Map result = new HashMap<>(); + + CountDownLatch latch = new CountDownLatch(1); + Future future = vertx.deployVerticle(cls.getName(), options); + future.onComplete((success, failure) -> { + result.put("code", failure == null); + + if (failure != null) { + result.put("message", failure.getMessage()); + LOGGER.error("deploy vertx failed, cause ", failure); + } + + latch.countDown(); + }); + + latch.await(); + + return result; + } + + 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, VertxMetricsFactory metricsFactory) { + if (vertxOptions == null) { + vertxOptions = new VertxOptions(); + } + + boolean isDebug = ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("jdwp"); + if (isDebug) { + vertxOptions.setBlockedThreadCheckInterval(BLOCKED_THREAD_CHECK_INTERVAL); + LOGGER.info("in debug mode, disable blocked thread check."); + } + + configureVertxFileCaching(vertxOptions); + + 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 = LegacyPropertyFactory + .getBooleanProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, true); + vertxOptions.getFileSystemOptions().setClassPathResolvingEnabled(!disableFileCPResolving); + } + + // try to reference byte[] + // otherwise copy byte[] + public static byte[] getBytesFast(InputStream inputStream) throws IOException { + if (inputStream instanceof BufferInputStream) { + return getBytesFast(((BufferInputStream) inputStream).getByteBuf()); + } + + return IOUtils.toByteArray(inputStream); + } + + public static byte[] getBytesFast(Buffer buffer) { + byte[] arr = new byte[buffer.length()]; + buffer.getBytes(arr, 0); + return arr; + } + + public static byte[] getBytesFast(ByteBuf byteBuf) { + if (byteBuf.hasArray()) { + return byteBuf.array(); + } + + byte[] arr = new byte[byteBuf.writerIndex()]; + byteBuf.getBytes(0, arr); + return arr; + } + + public static CompletableFuture closeVertxByName(String name) { + LOGGER.info("Closing vertx {}.", name); + CompletableFuture future = new CompletableFuture<>(); + Vertx vertx = vertxMap.remove(name); + if (vertx == null) { + LOGGER.info("Vertx {} not exist.", name); + future.complete(null); + return future; + } + + Future closeFuture = vertx.close(); + closeFuture.onComplete((succ, fail) -> { + if (fail == null) { + LOGGER.info("Success to close vertx {}.", name); + future.complete(null); + return; + } + + future.completeExceptionally(fail); + }); + + return future; + } + + public static void blockCloseVertxByName(String name) { + CompletableFuture future = closeVertxByName(name); + try { + future.get(30, TimeUnit.SECONDS); + } catch (Throwable e) { + LOGGER.error("Failed to wait close vertx {}.", name, e); + } + } + + public static void blockCloseVertx(Vertx vertx) { + CountDownLatch latch = new CountDownLatch(1); + 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); + } + + latch.countDown(); + }); + + try { + 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/ClientPoolFactory.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/ClientPoolFactory.java new file mode 100644 index 00000000000..a8f38bd150d --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/ClientPoolFactory.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.foundation.vertx.client; + +import io.vertx.core.Context; + +public interface ClientPoolFactory { + CLIENT_POOL createClientPool(Context context); +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/ClientPoolManager.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/ClientPoolManager.java new file mode 100644 index 00000000000..900f994c161 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/ClientPoolManager.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.foundation.vertx.client; + +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicInteger; + +import io.vertx.core.Context; +import io.vertx.core.Vertx; + +/** + * CLIENT_POOL是一个完备的连接池,支持向同一个目标建立一个或多个连接 + * 之所以再包装一层,是因为多个线程使用一个连接池的场景下 + * 会导致多个线程抢连接池的同一把锁 + * 包装之后,允许使用m个网络线程,每个线程中有1个连接池 + * + * support both sync and reactive invoke. + * 1.sync invoke, bind to a net thread + * 2.async but not in eventloop, select but not bind to a net thread + * 3.async and in eventloop, use clientPool in self thread + * + * sync/async is not about net operation, just about consumer invoke mode. + */ +public class ClientPoolManager { + private final Vertx vertx; + + private final String id = UUID.randomUUID().toString(); + + private final ClientPoolFactory factory; + + private final List pools = new CopyOnWriteArrayList<>(); + + // reactive mode, when call from other thread, must select a context for it + // if we use threadId to hash a context, will always select the same context from one thread + private final AtomicInteger reactiveNextIndex = new AtomicInteger(); + + public ClientPoolManager(Vertx vertx, ClientPoolFactory factory) { + this.vertx = vertx; + this.factory = factory; + } + + public CLIENT_POOL createClientPool(Context context) { + CLIENT_POOL pool = factory.createClientPool(context); + addPool(context, pool); + return pool; + } + + protected void addPool(Context context, CLIENT_POOL pool) { + context.put(id, pool); + pools.add(pool); + } + + public CLIENT_POOL findClientPool(boolean sync) { + return findClientPool(sync, null); + } + + public CLIENT_POOL findClientPool(boolean sync, Context targetContext) { + if (sync) { + return findThreadBindClientPool(); + } + + // reactive mode + return findByContext(targetContext); + } + + protected CLIENT_POOL findByContext() { + return findByContext(null); + } + + protected CLIENT_POOL findByContext(Context targetContext) { + Context currentContext = targetContext != null ? targetContext : Vertx.currentContext(); + if (currentContext != null + && currentContext.owner() == vertx + && currentContext.isEventLoopContext()) { + // standard reactive mode + CLIENT_POOL clientPool = currentContext.get(id); + if (clientPool != null) { + return clientPool; + } + + // Maybe executed in a call back of a reactive call. + // The Context is created in a non-event thread and passed to the event loop + // thread by vert.x. + } + + // not in correct context: + // 1.normal thread + // 2.vertx worker thread + // 3.other vertx thread + // select a existing context + assertPoolsInitialized(); + int idx = reactiveNextIndex.getAndIncrement() % pools.size(); + if (idx < 0) { + idx = -idx; + } + return pools.get(idx); + } + + public CLIENT_POOL findThreadBindClientPool() { + assertPoolsInitialized(); + int idx = (int) (Thread.currentThread().getId() % pools.size()); + return pools.get(idx); + } + + private void assertPoolsInitialized() { + if (pools.isEmpty()) { + throw new IllegalStateException("client pool not initialized successfully when making calls." + + "Please check if system boot up is ready or some errors happened when startup."); + } + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/ClientVerticle.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/ClientVerticle.java new file mode 100644 index 00000000000..69044aec98a --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/ClientVerticle.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.foundation.vertx.client; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.AbstractVerticle; + +public class ClientVerticle extends AbstractVerticle { + private static final Logger LOGGER = LoggerFactory.getLogger(ClientVerticle.class); + + public static final String CLIENT_MGR = "clientMgr"; + + @SuppressWarnings("unchecked") + @Override + public void start() throws Exception { + try { + ClientPoolManager clientMgr = (ClientPoolManager) config().getValue(CLIENT_MGR); + clientMgr.createClientPool(context); + } catch (Throwable e) { + // vert.x got some states that not print error and execute call back in VertexUtils.blockDeploy, we add a log our self. + LOGGER.error("", e); + throw e; + } + } +} 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 new file mode 100644 index 00000000000..83bf28db9ca --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientOptionsSPI.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 org.apache.servicecomb.foundation.common.encrypt.Encryptions; +import org.apache.servicecomb.foundation.vertx.VertxTLSBuilder; + +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; + +/** + * common Http Client Options must be set by implementations + */ +public interface HttpClientOptionsSPI { + /* unique name in this service */ + String clientName(); + + /* loading order */ + int getOrder(); + + /* can turn off the client */ + 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. */ + String getConfigTag(); + + /***************** vert.x common settings ***************************/ + int getEventLoopPoolSize(); + + boolean useSharedVertx(); + + /***************** vert.x vertical common settings ***************************/ + int getInstanceCount(); + + boolean isWorker(); + + String getWorkerPoolName(); + + int getWorkerPoolSize(); + + /***************** http common settings ***************************/ + HttpVersion getHttpVersion(); + + int getConnectTimeoutInMillis(); + + int getIdleTimeoutInSeconds(); + + boolean isTryUseCompression(); + + int getMaxWaitQueueSize(); + + int getMaxPoolSize(); + + boolean isKeepAlive(); + + int getMaxHeaderSize(); + + int getKeepAliveTimeout(); + + boolean enableLogActivity(); + + /***************** http 2 settings ****************************/ + int getHttp2MultiplexingLimit(); + + int getHttp2MaxPoolSize(); + + boolean isUseAlpn(); + + /***************** proxy settings ***************************/ + boolean isProxyEnable(); + + String getProxyHost(); + + int getProxyPort(); + + String getProxyUsername(); + + String getProxyPassword(); + + /***************** ssl settings ***************************/ + boolean isSsl(); + + static void buildClientOptionsBase(HttpClientOptionsSPI spi, ClientOptionsBase httpClientOptions) { + httpClientOptions.setConnectTimeout(spi.getConnectTimeoutInMillis()); + httpClientOptions.setIdleTimeout(spi.getIdleTimeoutInSeconds()); + httpClientOptions.setLogActivity(spi.enableLogActivity()); + + if (spi.isProxyEnable()) { + ProxyOptions proxy = new ProxyOptions(); + proxy.setHost(spi.getProxyHost()); + proxy.setPort(spi.getProxyPort()); + proxy.setUsername(spi.getProxyUsername()); + proxy.setPassword( + Encryptions.decode(spi.getProxyPassword(), spi.getConfigTag())); + httpClientOptions.setProxyOptions(proxy); + } + + if (spi.getHttpVersion() == HttpVersion.HTTP_2) { + 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.setHttp2KeepAliveTimeout(spi.getKeepAliveTimeout()); + } else { + httpClientOptions.setKeepAliveTimeout(spi.getKeepAliveTimeout()); + } + + if (spi.isSsl()) { + VertxTLSBuilder.buildHttpClientOptions(spi.getConfigTag(), httpClientOptions); + } + + 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 new file mode 100644 index 00000000000..69f15e6e397 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientPoolFactory.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.foundation.vertx.client.http; + +import org.apache.servicecomb.foundation.vertx.client.ClientPoolFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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 { + private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientPoolFactory.class); + + private final HttpClientOptions httpClientOptions; + + private final PoolOptions poolOptions; + + public HttpClientPoolFactory(HttpClientOptions httpClientOptions, PoolOptions poolOptions) { + this.httpClientOptions = httpClientOptions; + this.poolOptions = poolOptions; + } + + @Override + public HttpClientWithContext createClientPool(Context context) { + 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/HttpClientWithContext.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientWithContext.java new file mode 100644 index 00000000000..f9dab51e83a --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientWithContext.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.foundation.vertx.client.http; + +import io.vertx.core.Context; +import io.vertx.core.http.HttpClient; + +public class HttpClientWithContext { + public interface RunHandler { + void run(HttpClient httpClient); + } + + private final HttpClient httpClient; + + private final Context context; + + public HttpClientWithContext(HttpClient httpClient, Context context) { + this.httpClient = httpClient; + this.context = context; + } + + public HttpClient getHttpClient() { + return httpClient; + } + + public void runOnContext(RunHandler handler) { + context.runOnContext((v) -> handler.run(httpClient)); + } + + public Context context() { + return 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 new file mode 100644 index 00000000000..b1ef46436ec --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClients.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.HashMap; +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; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager; +import org.apache.servicecomb.foundation.vertx.client.ClientVerticle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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. + */ +public class HttpClients { + private static final Logger LOGGER = LoggerFactory.getLogger(HttpClients.class); + + private static final Map> httpClients = new HashMap<>(); + + /* load at boot up, call this method once and only once. */ + public static void load() { + List clientOptionsList = SPIServiceUtils.getOrLoadSortedService(HttpClientOptionsSPI.class); + clientOptionsList.forEach(option -> { + if (option.enabled()) { + ClientPoolManager clientPoolManager = httpClients.get(option.clientName()); + if (clientPoolManager != null) { + LOGGER.warn("client pool {} initialized again.", option.clientName()); + } + httpClients.put(option.clientName(), createClientPoolManager(option)); + } + }); + } + + /* destroy at shutdown. */ + public static void destroy() { + httpClients.clear(); + List clientOptionsList = SPIServiceUtils.getOrLoadSortedService(HttpClientOptionsSPI.class); + clientOptionsList.forEach(option -> VertxUtils.blockCloseVertxByName(option.clientName())); + } + + 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), poolOptions)); + + DeploymentOptions deployOptions = VertxUtils.createClientDeployOptions(clientPoolManager, + option.getInstanceCount()) + .setThreadingModel(option.isWorker() ? ThreadingModel.WORKER : ThreadingModel.EVENT_LOOP) + .setWorkerPoolName(option.getWorkerPoolName()) + .setWorkerPoolSize(option.getWorkerPoolSize()); + try { + VertxUtils.blockDeploy(vertx, ClientVerticle.class, deployOptions); + return clientPoolManager; + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + } + + 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(LegacyPropertyFactory.getEnvironment()); + } + + AddressResolverOptions resolverOptions = AddressResolverConfig + .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, null); + } + + /** + * get client instance by name + * @param clientName instance name + * @return the deployed instance name + */ + public static HttpClientWithContext getClient(String clientName) { + return getClient(clientName, true); + } + + /** + * get client instance by name + * @param clientName instance name + * @param sync reactive or not. false for reactive. + * @return the deployed instance name + */ + public static HttpClientWithContext getClient(String clientName, boolean sync) { + return getClient(clientName, sync, null); + } + + /** + * get client instance by name + * @param clientName instance name + * @param sync reactive or not. false for reactive. + * @param targetContext running context + * @return the deployed instance name + */ + public static HttpClientWithContext getClient(String clientName, boolean sync, Context targetContext) { + ClientPoolManager poolManager = httpClients.get(clientName); + if (poolManager == null) { + LOGGER.error("client name [{}] not exists, should only happen in tests.", clientName); + return null; + } + return poolManager.findClientPool(sync, targetContext); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientConnectionPool.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientConnectionPool.java new file mode 100644 index 00000000000..f3613685f38 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientConnectionPool.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.foundation.vertx.client.tcp; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; + +import io.vertx.core.Context; + +public abstract class AbstractTcpClientConnectionPool { + // 是在哪个context中创建的 + protected Context context; + + protected NetClientWrapper netClientWrapper; + + // key为address + protected Map tcpClientMap = new ConcurrentHashMapEx<>(); + + public AbstractTcpClientConnectionPool(Context context, + NetClientWrapper netClientWrapper) { + this.context = context; + this.netClientWrapper = netClientWrapper; + + startCheckTimeout(context); + } + + protected void startCheckTimeout(Context context) { + context.owner().setPeriodic(TimeUnit.SECONDS.toMillis(1), this::onCheckTimeout); + } + + private void onCheckTimeout(Long event) { + for (TcpClientConnection client : tcpClientMap.values()) { + client.checkTimeout(); + } + } + + public T findOrCreateClient(String endpoint) { + return tcpClientMap.computeIfAbsent(endpoint, this::create); + } + + protected abstract T create(String endpoint); +} 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 new file mode 100644 index 00000000000..ef6717f8968 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPackage.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.foundation.vertx.client.tcp; + +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; + +public abstract class AbstractTcpClientPackage { + private static final AtomicLong reqId = new AtomicLong(); + + public static long getAndIncRequestId() { + return reqId.getAndIncrement(); + } + + private volatile long msRequestTimeout; + + private volatile long finishWriteToBuffer; + + protected long msgId = getAndIncRequestId(); + + public long getMsgId() { + return msgId; + } + + public long getMsRequestTimeout() { + return msRequestTimeout; + } + + public void setMsRequestTimeout(long msRequestTimeout) { + this.msRequestTimeout = msRequestTimeout; + } + + public long getFinishWriteToBuffer() { + return finishWriteToBuffer; + } + + public void finishWriteToBuffer() { + this.finishWriteToBuffer = System.nanoTime(); + } + + public abstract TcpOutputStream createStream(); +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPoolFactory.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPoolFactory.java new file mode 100644 index 00000000000..3d1b298cd0d --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPoolFactory.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.foundation.vertx.client.tcp; + +import org.apache.servicecomb.foundation.vertx.client.ClientPoolFactory; + +import io.vertx.core.Context; +import io.vertx.core.Vertx; + +public abstract class AbstractTcpClientPoolFactory implements ClientPoolFactory { + protected TcpClientConfig normalClientConfig; + + protected TcpClientConfig sslClientConfig; + + public AbstractTcpClientPoolFactory(TcpClientConfig normalClientConfig, TcpClientConfig sslClientConfig) { + this.normalClientConfig = normalClientConfig; + this.sslClientConfig = sslClientConfig; + } + + @Override + public CLIENT_POOL createClientPool(Context context) { + Vertx vertx = context.owner(); + + NetClientWrapper netClientWrapper = new NetClientWrapper(vertx, normalClientConfig, sslClientConfig); + return doCreateClientPool(context, netClientWrapper); + } + + protected abstract CLIENT_POOL doCreateClientPool(Context context, NetClientWrapper netClientWrapper); +} 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 new file mode 100644 index 00000000000..ce47126ee47 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/NetClientWrapper.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.foundation.vertx.client.tcp; + +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import io.vertx.core.net.NetClient; +import io.vertx.core.net.NetSocket; + +// netClient do not like httpClient +// can not support normal and ssl by the same instance +// so we do this wrap +public class NetClientWrapper { + private final TcpClientConfig normalClientConfig; + + private final NetClient normalNetClient; + + private final TcpClientConfig sslClientConfig; + + private final NetClient sslNetClient; + + public NetClientWrapper(Vertx vertx, TcpClientConfig normalClientConfig, TcpClientConfig sslClientConfig) { + this.normalClientConfig = normalClientConfig; + this.normalNetClient = vertx.createNetClient(normalClientConfig); + + this.sslClientConfig = sslClientConfig; + this.sslNetClient = vertx.createNetClient(sslClientConfig); + } + + public TcpClientConfig getClientConfig(boolean ssl) { + if (ssl) { + return sslClientConfig; + } + + return normalClientConfig; + } + + public Future connect(boolean ssl, int port, String host) { + if (ssl) { + return sslNetClient.connect(port, host); + } + + return normalNetClient.connect(port, host); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConfig.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConfig.java new file mode 100644 index 00000000000..dcdca0e4e90 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConfig.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.foundation.vertx.client.tcp; + +import io.vertx.core.net.NetClientOptions; + +public class TcpClientConfig extends NetClientOptions { + public static final int DEFAULT_LOGIN_TIMEOUT = 30000; + + private long msLoginTimeout; + + public TcpClientConfig() { + msLoginTimeout = DEFAULT_LOGIN_TIMEOUT; + } + + public long getMsLoginTimeout() { + return msLoginTimeout; + } + + public void setMsLoginTimeout(long msLoginTimeout) { + this.msLoginTimeout = msLoginTimeout; + } +} 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 new file mode 100644 index 00000000000..db071e3c34f --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java @@ -0,0 +1,331 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.IOException; +import java.net.InetSocketAddress; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Queue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeoutException; + +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.foundation.vertx.server.TcpParser; +import org.apache.servicecomb.foundation.vertx.tcp.TcpConnection; +import org.apache.servicecomb.foundation.vertx.tcp.TcpConst; +import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; + +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; + +public class TcpClientConnection extends TcpConnection { + private static final Logger LOGGER = LoggerFactory.getLogger(TcpClientConnection.class); + + enum Status { + CONNECTING, + DISCONNECTED, + TRY_LOGIN, + WORKING + } + + private final NetClientWrapper netClientWrapper; + + private final TcpClientConfig clientConfig; + + private final URIEndpointObject endpoint; + + private final InetSocketAddress socketAddress; + + private volatile boolean localSupportLogin = false; + + private final boolean remoteSupportLogin; + + private volatile Status status = Status.DISCONNECTED; + + // save msg before login success. + // before login, we can not know parameters, like: zip/codec compatible, and so on + // so can only save package, can not save byteBuf + private final Queue packageQueue = new ConcurrentLinkedQueue<>(); + + private volatile Map requestMap = new ConcurrentHashMap<>(); + + public TcpClientConnection(Context context, NetClientWrapper netClientWrapper, String strEndpoint) { + this.setContext(context); + + this.netClientWrapper = netClientWrapper; + endpoint = new URIEndpointObject(strEndpoint); + this.socketAddress = endpoint.getSocketAddress(); + this.remoteSupportLogin = Boolean.parseBoolean(endpoint.getFirst(TcpConst.LOGIN)); + this.clientConfig = netClientWrapper.getClientConfig(endpoint.isSslEnabled()); + } + + public boolean isLocalSupportLogin() { + return localSupportLogin; + } + + public TcpClientConfig getClientConfig() { + return clientConfig; + } + + public void setLocalSupportLogin(boolean localSupportLogin) { + this.localSupportLogin = localSupportLogin; + } + + protected TcpOutputStream createLogin() { + return null; + } + + protected boolean onLoginResponse(Buffer bodyBuffer) { + return true; + } + + public CompletableFuture send(AbstractTcpClientPackage tcpClientPackage) { + CompletableFuture future = new CompletableFuture<>(); + send(tcpClientPackage, ar -> { + if (ar.failed()) { + future.completeExceptionally(ar.cause()); + return; + } + future.complete(ar.result()); + }); + return future; + } + + public void send(AbstractTcpClientPackage tcpClientPackage, TcpResponseCallback callback) { + requestMap.put(tcpClientPackage.getMsgId(), new TcpRequest(tcpClientPackage.getMsRequestTimeout(), callback)); + + if (writeToBufferQueue(tcpClientPackage)) { + return; + } + + // before login success, no optimize, just make sure do not lost data + context.runOnContext(v -> { + if (!writeToBufferQueue(tcpClientPackage)) { + packageQueue.add(tcpClientPackage); + } + + // connect must call in eventloop thread + // otherwise vertx will create a new eventloop thread for it if count + // of eventloop thread is not up to the limit. + if (Status.DISCONNECTED.equals(status)) { + connect(); + } + }); + } + + private boolean writeToBufferQueue(AbstractTcpClientPackage tcpClientPackage) { + // read status maybe out of eventloop thread, it's not exact + // just optimize for main scenes + if (Status.WORKING.equals(status)) { + // encode in sender thread + try (TcpOutputStream os = tcpClientPackage.createStream()) { + write(os.getBuffer()); + tcpClientPackage.finishWriteToBuffer(); + } + return true; + } + + return false; + } + + @Override + protected void writeInContext() { + writePackageInContext(); + + super.writeInContext(); + } + + private void writePackageInContext() { + for (; ; ) { + AbstractTcpClientPackage pkg = packageQueue.poll(); + if (pkg == null) { + break; + } + + try (TcpOutputStream os = pkg.createStream()) { + Buffer buf = os.getBuffer(); + netSocket.write(buf); + pkg.finishWriteToBuffer(); + } + } + } + + @VisibleForTesting + protected void connect() { + this.status = Status.CONNECTING; + LOGGER.info("connecting to address {}", socketAddress.toString()); + + Future result = netClientWrapper.connect(endpoint.isSslEnabled(), + socketAddress.getPort(), + socketAddress.getHostString()); + result.onComplete((socket, fail) -> { + if (fail == null) { + onConnectSuccess(socket); + return; + } + + onConnectFailed(fail); + }); + } + + private void onConnectSuccess(NetSocket socket) { + LOGGER.info("connected to address {} success in thread {}.", + socketAddress.toString(), + Thread.currentThread().getName()); + // currently, socket always be NetSocketImpl + this.initNetSocket((NetSocketImpl) socket); + socket.handler(new TcpParser(this::onReply)); + + socket.exceptionHandler(this::onException); + socket.closeHandler(this::onClosed); + + // 开始登录 + tryLogin(); + } + + @VisibleForTesting + void onClosed(Void v) { + onDisconnected(new IOException("socket closed")); + } + + // 异常断连时,先触发onException,再触发onClosed + // 正常断连时,只触发onClosed + private void onException(Throwable e) { + LOGGER.error("{} disconnected from {}, in thread {}, cause {}", + netSocket.localAddress().toString(), + socketAddress.toString(), + Thread.currentThread().getName(), + e.getMessage()); + } + + private void onDisconnected(Throwable e) { + this.status = Status.DISCONNECTED; + LOGGER.error("{} disconnected from {}, in thread {}, cause {}", + netSocket.localAddress().toString(), + socketAddress.toString(), + Thread.currentThread().getName(), + e.getMessage()); + + clearCachedRequest(e); + } + + protected void tryLogin() { + if (!localSupportLogin || !remoteSupportLogin) { + LOGGER.error( + "local or remote not support login, address={}, localSupportLogin={}, remoteSupportLogin={}.", + socketAddress.toString(), + localSupportLogin, + remoteSupportLogin); + onLoginSuccess(); + return; + } + + this.status = Status.TRY_LOGIN; + LOGGER.info("try login to address {}", socketAddress.toString()); + + try (TcpOutputStream os = createLogin()) { + requestMap.put(os.getMsgId(), + new TcpRequest(clientConfig.getMsLoginTimeout(), this::onLoginResponse)); + netSocket.write(os.getBuffer()); + } + } + + private void onLoginResponse(AsyncResult asyncResult) { + if (asyncResult.failed()) { + LOGGER.error("login failed, address {}", socketAddress.toString(), asyncResult.cause()); + // 在相应回调中设置状态 + netSocket.close(); + return; + } + + if (!onLoginResponse(asyncResult.result().getBodyBuffer())) { + LOGGER.error("login failed, address {}", socketAddress.toString()); + // 在相应回调中设置状态 + netSocket.close(); + return; + } + + LOGGER.info("login success, address {}", socketAddress.toString()); + onLoginSuccess(); + } + + private void onLoginSuccess() { + this.status = Status.WORKING; + writeInContext(); + } + + private void onConnectFailed(Throwable cause) { + // 连接失败 + this.status = Status.DISCONNECTED; + String msg = String.format("connect to address %s failed.", + socketAddress.toString()); + LOGGER.error(msg, cause); + + clearCachedRequest(cause); + } + + protected void clearCachedRequest(Throwable cause) { + // 在onSendError,用户可能发起一次新的调用,需要避免作多余的清理 + Map oldMap = requestMap; + requestMap = new ConcurrentHashMap<>(); + + for (TcpRequest request : oldMap.values()) { + request.onSendError(cause); + } + oldMap.clear(); + } + + protected void onReply(long msgId, Buffer headerBuffer, Buffer bodyBuffer) { + TcpRequest request = requestMap.remove(msgId); + if (request == null) { + LOGGER.error("Unknown reply msgId {}, waiting count {}", msgId, requestMap.size()); + return; + } + + request.onReply(headerBuffer, bodyBuffer); + } + + public void checkTimeout() { + for (Entry entry : requestMap.entrySet()) { + TcpRequest request = entry.getValue(); + if (request.isTimeout()) { + // 可能正好收到reply,且被处理了,所以这里的remove不一定有效 + // 是否有效,根据remove的结果来决定 + request = requestMap.remove(entry.getKey()); + if (request != null) { + String msg = + String.format("request timeout, msgId=%d, address=%s", entry.getKey(), socketAddress); + LOGGER.error(msg); + + request.onTimeout(new TimeoutException(msg)); + } + } + } + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnectionPool.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnectionPool.java new file mode 100644 index 00000000000..4f1ed9a2c79 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnectionPool.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.foundation.vertx.client.tcp; + +import io.vertx.core.Context; + +public class TcpClientConnectionPool extends AbstractTcpClientConnectionPool { + public TcpClientConnectionPool(Context context, NetClientWrapper netClientWrapper) { + super(context, netClientWrapper); + } + + @Override + protected TcpClientConnection create(String endpoint) { + return new TcpClientConnection(context, netClientWrapper, endpoint); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientPackage.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientPackage.java new file mode 100644 index 00000000000..ceee9357f9e --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientPackage.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.foundation.vertx.client.tcp; + +import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; + +public class TcpClientPackage extends AbstractTcpClientPackage { + private final TcpOutputStream os; + + public TcpClientPackage(TcpOutputStream os) { + this.os = os; + } + + @Override + public TcpOutputStream createStream() { + return os; + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientPoolFactory.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientPoolFactory.java new file mode 100644 index 00000000000..d2ae7023d7b --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientPoolFactory.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.foundation.vertx.client.tcp; + +import io.vertx.core.Context; + +public class TcpClientPoolFactory extends AbstractTcpClientPoolFactory { + public TcpClientPoolFactory(TcpClientConfig normalClientConfig, TcpClientConfig sslClientConfig) { + super(normalClientConfig, sslClientConfig); + } + + @Override + protected TcpClientConnectionPool doCreateClientPool(Context context, NetClientWrapper netClientWrapper) { + return new TcpClientConnectionPool(context, netClientWrapper); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpData.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpData.java new file mode 100644 index 00000000000..c94b75987a0 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpData.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.foundation.vertx.client.tcp; + +import io.vertx.core.buffer.Buffer; + +public class TcpData { + private Buffer headerBuffer; + + private Buffer bodyBuffer; + + public TcpData(Buffer headerBuffer, Buffer bodyBuffer) { + this.headerBuffer = headerBuffer; + this.bodyBuffer = bodyBuffer; + } + + public Buffer getHeaderBuffer() { + return headerBuffer; + } + + public void setHeaderBuffer(Buffer headerBuffer) { + this.headerBuffer = headerBuffer; + } + + public Buffer getBodyBuffer() { + return bodyBuffer; + } + + public void setBodyBuffer(Buffer bodyBuffer) { + this.bodyBuffer = bodyBuffer; + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpRequest.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpRequest.java new file mode 100644 index 00000000000..6ca90adbcf1 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpRequest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.vertx.client.tcp; + +import java.util.concurrent.TimeoutException; + +import io.vertx.core.Context; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; + +public class TcpRequest { + private final long begin; + + private final long msTimeout; + + private final Context callContext; + + private final long threadId; + + private final TcpResponseCallback responseCallback; + + public TcpRequest(long msTimeout, TcpResponseCallback responseCallback) { + callContext = Vertx.currentContext(); + threadId = Thread.currentThread().getId(); + this.begin = System.currentTimeMillis(); + this.msTimeout = msTimeout; + this.responseCallback = responseCallback; + } + + public void onReply(Buffer headerBuffer, Buffer bodyBuffer) { + TcpData tcpData = new TcpData(headerBuffer, bodyBuffer); + + if (callContext == null || threadId == Thread.currentThread().getId()) { + responseCallback.success(tcpData); + return; + } + + callContext.runOnContext(Void -> responseCallback.success(tcpData)); + } + + public void onSendError(Throwable e) { + responseCallback.fail(e); + } + + public boolean isTimeout() { + return System.currentTimeMillis() - begin >= msTimeout; + } + + public void onTimeout(TimeoutException e) { + responseCallback.fail(e); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpResponseCallback.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpResponseCallback.java new file mode 100644 index 00000000000..c2df7e13435 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpResponseCallback.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.foundation.vertx.client.tcp; + +import org.apache.servicecomb.foundation.vertx.AsyncResultCallback; + +public interface TcpResponseCallback extends AsyncResultCallback { + +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/SinglePoolBlockingExecutor.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/SinglePoolBlockingExecutor.java new file mode 100644 index 00000000000..176e66aa50f --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/SinglePoolBlockingExecutor.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.foundation.vertx.executor; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SinglePoolBlockingExecutor implements Executor { + private static final Logger LOGGER = LoggerFactory.getLogger(SinglePoolBlockingExecutor.class); + + private static final Executor SINGLE_POOL = Executors.newSingleThreadExecutor((r) -> { + Thread thread = new Thread(r); + thread.setName("single-pool-blocking-executor"); + return thread; + }); + + public static SinglePoolBlockingExecutor create() { + return new SinglePoolBlockingExecutor(); + } + + private SinglePoolBlockingExecutor() { + + } + + @Override + public void execute(Runnable command) { + SINGLE_POOL.execute(() -> { + try { + command.run(); + } catch (Throwable e) { + LOGGER.error("Logic should not throw exception, please fix it", e); + } + }); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxContextExecutor.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxContextExecutor.java new file mode 100644 index 00000000000..49febb1b3d4 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxContextExecutor.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.foundation.vertx.executor; + +import java.util.concurrent.Executor; + +import io.vertx.core.Context; +import io.vertx.core.Vertx; + +public class VertxContextExecutor implements Executor { + public static VertxContextExecutor create(Vertx vertx) { + return new VertxContextExecutor(vertx.getOrCreateContext()); + } + + public static VertxContextExecutor create(Context context) { + return new VertxContextExecutor(context); + } + + private final Context context; + + private VertxContextExecutor(Context context) { + this.context = context; + } + + @Override + public void execute(Runnable command) { + if (context == Vertx.currentContext()) { + command.run(); + return; + } + + context.runOnContext(v -> command.run()); + } +} 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 new file mode 100644 index 00000000000..e3334a79f1b --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxWorkerExecutor.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.foundation.vertx.executor; + +import java.util.concurrent.Executor; + +import io.vertx.core.Vertx; + +public class VertxWorkerExecutor implements Executor { + + @Override + public void execute(Runnable command) { + 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 new file mode 100644 index 00000000000..058a9390464 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletRequest.java @@ -0,0 +1,399 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +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<>(); + + @Override + public Object getAttribute(String name) { + return attributeMap.get(name); + } + + @Override + public Enumeration getAttributeNames() { + return Collections.enumeration(attributeMap.keySet()); + } + + @Override + public String getCharacterEncoding() { + throw new Error("not supported method"); + } + + @Override + 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"); + } + + @Override + public long getContentLengthLong() { + throw new Error("not supported method"); + } + + @Override + public String getContentType() { + throw new Error("not supported method"); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + throw new Error("not supported method"); + } + + @Override + public String getParameter(String name) { + throw new Error("not supported method"); + } + + @Override + public Enumeration getParameterNames() { + throw new Error("not supported method"); + } + + @Override + public String[] getParameterValues(String name) { + throw new Error("not supported method"); + } + + @Override + public Map getParameterMap() { + throw new Error("not supported method"); + } + + @Override + public String getProtocol() { + throw new Error("not supported method"); + } + + @Override + public String getScheme() { + throw new Error("not supported method"); + } + + @Override + public String getServerName() { + throw new Error("not supported method"); + } + + @Override + public int getServerPort() { + throw new Error("not supported method"); + } + + @Override + public BufferedReader getReader() throws IOException { + throw new Error("not supported method"); + } + + @Override + public String getRemoteAddr() { + throw new Error("not supported method"); + } + + @Override + public String getRemoteHost() { + throw new Error("not supported method"); + } + + @Override + public void setAttribute(String name, Object o) { + attributeMap.put(name, o); + } + + @Override + public void removeAttribute(String name) { + attributeMap.remove(name); + } + + @Override + public Locale getLocale() { + throw new Error("not supported method"); + } + + @Override + public Enumeration getLocales() { + throw new Error("not supported method"); + } + + @Override + public boolean isSecure() { + throw new Error("not supported method"); + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + throw new Error("not supported method"); + } + + @Override + public int getRemotePort() { + throw new Error("not supported method"); + } + + @Override + public String getLocalName() { + throw new Error("not supported method"); + } + + @Override + public String getLocalAddr() { + throw new Error("not supported method"); + } + + @Override + public int getLocalPort() { + throw new Error("not supported method"); + } + + @Override + public ServletContext getServletContext() { + throw new Error("not supported method"); + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + throw new Error("not supported method"); + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) + throws IllegalStateException { + throw new Error("not supported method"); + } + + @Override + public boolean isAsyncStarted() { + throw new Error("not supported method"); + } + + @Override + public boolean isAsyncSupported() { + throw new Error("not supported method"); + } + + @Override + public AsyncContext getAsyncContext() { + throw new Error("not supported method"); + } + + @Override + public DispatcherType getDispatcherType() { + throw new Error("not supported method"); + } + + @Override + public String getAuthType() { + throw new Error("not supported method"); + } + + @Override + public Cookie[] getCookies() { + throw new Error("not supported method"); + } + + @Override + public long getDateHeader(String name) { + throw new Error("not supported method"); + } + + @Override + public String getHeader(String name) { + throw new Error("not supported method"); + } + + @Override + public Enumeration getHeaders(String name) { + throw new Error("not supported method"); + } + + @Override + public Enumeration getHeaderNames() { + throw new Error("not supported method"); + } + + @Override + public int getIntHeader(String name) { + throw new Error("not supported method"); + } + + @Override + public String getMethod() { + throw new Error("not supported method"); + } + + @Override + public String getPathInfo() { + throw new Error("not supported method"); + } + + @Override + public String getPathTranslated() { + throw new Error("not supported method"); + } + + @Override + public String getContextPath() { + throw new Error("not supported method"); + } + + @Override + public String getQueryString() { + throw new Error("not supported method"); + } + + @Override + public String getRemoteUser() { + throw new Error("not supported method"); + } + + @Override + public boolean isUserInRole(String role) { + throw new Error("not supported method"); + } + + @Override + public Principal getUserPrincipal() { + throw new Error("not supported method"); + } + + @Override + public String getRequestedSessionId() { + throw new Error("not supported method"); + } + + @Override + public String getRequestURI() { + throw new Error("not supported method"); + } + + @Override + public StringBuffer getRequestURL() { + throw new Error("not supported method"); + } + + @Override + public String getServletPath() { + throw new Error("not supported method"); + } + + @Override + public HttpSession getSession(boolean create) { + throw new Error("not supported method"); + } + + @Override + public HttpSession getSession() { + throw new Error("not supported method"); + } + + @Override + public String changeSessionId() { + throw new Error("not supported method"); + } + + @Override + public boolean isRequestedSessionIdValid() { + throw new Error("not supported method"); + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + throw new Error("not supported method"); + } + + @Override + public boolean isRequestedSessionIdFromURL() { + throw new Error("not supported method"); + } + + @Override + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { + throw new Error("not supported method"); + } + + @Override + public void login(String username, String password) throws ServletException { + throw new Error("not supported method"); + } + + @Override + public void logout() throws ServletException { + throw new Error("not supported method"); + } + + @Override + public Collection getParts() throws IOException, ServletException { + throw new Error("not supported method"); + } + + @Override + public Part getPart(String name) throws IOException, ServletException { + throw new Error("not supported method"); + } + + @Override + public T upgrade(Class handlerClass) 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 new file mode 100644 index 00000000000..bd7899b471c --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.IOException; +import java.io.PrintWriter; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +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<>(); + + @Override + public String getCharacterEncoding() { + throw new Error("not supported method"); + } + + @Override + public String getContentType() { + throw new Error("not supported method"); + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + throw new Error("not supported method"); + } + + @Override + public PrintWriter getWriter() throws IOException { + throw new Error("not supported method"); + } + + @Override + public void setCharacterEncoding(String charset) { + throw new Error("not supported method"); + } + + @Override + public void setContentLength(int len) { + throw new Error("not supported method"); + } + + @Override + public void setContentLengthLong(long len) { + throw new Error("not supported method"); + } + + @Override + public void setContentType(String type) { + throw new Error("not supported method"); + } + + @Override + public void setBufferSize(int size) { + throw new Error("not supported method"); + } + + @Override + public int getBufferSize() { + throw new Error("not supported method"); + } + + @Override + public void flushBuffer() throws IOException { + // for vert.x do noting + } + + @Override + public void endResponse() throws IOException { + throw new Error("not supported method"); + } + + @Override + public void resetBuffer() { + throw new Error("not supported method"); + } + + @Override + public boolean isCommitted() { + throw new Error("not supported method"); + } + + @Override + public void reset() { + throw new Error("not supported method"); + } + + @Override + public void setLocale(Locale loc) { + throw new Error("not supported method"); + } + + @Override + public Locale getLocale() { + throw new Error("not supported method"); + } + + @Override + public void addCookie(Cookie cookie) { + throw new Error("not supported method"); + } + + @Override + public boolean containsHeader(String name) { + throw new Error("not supported method"); + } + + @Override + public String encodeURL(String url) { + throw new Error("not supported method"); + } + + @Override + 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"); + } + + @Override + public void sendError(int sc) throws IOException { + throw new Error("not supported method"); + } + + @Override + public void sendRedirect(String location) throws IOException { + throw new Error("not supported method"); + } + + @Override + public void setDateHeader(String name, long date) { + throw new Error("not supported method"); + } + + @Override + public void addDateHeader(String name, long date) { + throw new Error("not supported method"); + } + + @Override + public void setHeader(String name, String value) { + throw new Error("not supported method"); + } + + @Override + public void addHeader(String name, String value) { + throw new Error("not supported method"); + } + + @Override + public void setIntHeader(String name, int value) { + throw new Error("not supported method"); + } + + @Override + public void addIntHeader(String name, int value) { + throw new Error("not supported method"); + } + + @Override + public void setStatus(int sc) { + throw new Error("not supported method"); + } + + @Override + public int getStatus() { + throw new Error("not supported method"); + } + + @Override + public String getHeader(String name) { + throw new Error("not supported method"); + } + + @Override + public Collection getHeaders(String name) { + throw new Error("not supported method"); + } + + @Override + public Collection getHeaderNames() { + throw new Error("not supported method"); + } + + @Override + public StatusType getStatusType() { + throw new Error("not supported method"); + } + + @Override + public void setAttribute(String key, Object value) { + this.attributes.put(key, value); + } + + @Override + public Object getAttribute(String key) { + return this.attributes.get(key); + } + + @Override + 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/BodyBufferSupport.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/BodyBufferSupport.java new file mode 100644 index 00000000000..b4b7eb4cb8f --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/BodyBufferSupport.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.vertx.http; + +import io.vertx.core.buffer.Buffer; + +public interface BodyBufferSupport { + void setBodyBuffer(Buffer bodyBuffer); + + Buffer getBodyBuffer(); + + // notice: byte[] maybe null, and length maybe bigger than body length + byte[] getBodyBytes(); + + int getBodyBytesLength(); +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/BodyBufferSupportImpl.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/BodyBufferSupportImpl.java new file mode 100644 index 00000000000..eceedf64cc9 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/BodyBufferSupportImpl.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.foundation.vertx.http; + +import com.google.common.annotations.VisibleForTesting; +import org.apache.servicecomb.foundation.vertx.VertxUtils; + +import io.vertx.core.buffer.Buffer; + +public class BodyBufferSupportImpl implements BodyBufferSupport { + protected Buffer bodyBuffer; + + private byte[] bodyBytes; + + private int bodyLength; + + @Override + public void setBodyBuffer(Buffer bodyBuffer) { + this.bodyBuffer = bodyBuffer; + this.bodyBytes = null; + this.bodyLength = 0; + } + + private void prepare() { + if (bodyBytes == null && bodyBuffer != null) { + bodyLength = bodyBuffer.length(); + bodyBytes = VertxUtils.getBytesFast(bodyBuffer); + } + } + + + @Override + public Buffer getBodyBuffer() { + return bodyBuffer; + } + + @VisibleForTesting + void setBodyBytes(byte[] bodyBytes) { + this.bodyBytes = bodyBytes; + } + + @Override + public byte[] getBodyBytes() { + prepare(); + return bodyBytes; + } + + @VisibleForTesting + void setBodyLength(int bodyLength) { + this.bodyLength = bodyLength; + } + + @Override + public int getBodyBytesLength() { + prepare(); + return bodyLength; + } +} 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 new file mode 100644 index 00000000000..7c9c0fffbfc --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/DownloadUtils.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.foundation.vertx.http; + +import java.io.IOException; + +import jakarta.servlet.http.Part; + +import org.apache.servicecomb.foundation.common.http.HttpUtils; +import org.apache.servicecomb.foundation.common.part.FilePartForSend; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.http.HttpHeaders; + +/** + * internal api + */ +public final class DownloadUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(DownloadUtils.class); + + private DownloadUtils() { + } + + public static void prepareDownloadHeader(HttpServletResponseEx responseEx, Part part) { + if (responseEx.getHeader(HttpHeaders.CONTENT_LENGTH.toString()) == null) { + responseEx.setChunked(true); + } + if (part == null) { + return; + } + if (responseEx.getHeader(HttpHeaders.CONTENT_TYPE.toString()) == null) { + if (responseEx.getContentType() != null) { + responseEx.setHeader(HttpHeaders.CONTENT_TYPE.toString(), responseEx.getContentType()); + } else { + responseEx.setHeader(HttpHeaders.CONTENT_TYPE.toString(), part.getContentType()); + } + } + + 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(jakarta.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION, + "attachment;filename=" + encodedFileName + ";filename*=utf-8''" + encodedFileName); + } + } + + public static void clearPartResource(Part part) { + if (part == null) { + return; + } + if (part instanceof FilePartForSend && ((FilePartForSend) part).isDeleteAfterFinished()) { + try { + part.delete(); + } catch (IOException e) { + LOGGER.error("Failed to delete temp file: {}.", ((FilePartForSend) part).getAbsolutePath(), e); + } + } + } +} 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 new file mode 100644 index 00000000000..142435ead3e --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/EmptyAsyncContext.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.vertx.http; + +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 + public ServletRequest getRequest() { + return null; + } + + @Override + public ServletResponse getResponse() { + return null; + } + + @Override + public boolean hasOriginalRequestAndResponse() { + return false; + } + + @Override + public void dispatch() { + + } + + @Override + public void dispatch(String path) { + + } + + @Override + public void dispatch(ServletContext context, String path) { + + } + + @Override + public void complete() { + + } + + @Override + public void start(Runnable run) { + + } + + @Override + public void addListener(AsyncListener listener) { + + } + + @Override + public void addListener(AsyncListener listener, ServletRequest servletRequest, ServletResponse servletResponse) { + + } + + @Override + public T createListener(Class clazz) throws ServletException { + return null; + } + + @Override + public void setTimeout(long timeout) { + + } + + @Override + public long getTimeout() { + return 0; + } +} 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 new file mode 100644 index 00000000000..9e72d64d289 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/FileUploadPart.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.foundation.vertx.http; + +import java.io.File; +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; + +import io.vertx.ext.web.FileUpload; + +public class FileUploadPart extends AbstractPart { + private final FileUpload fileUpload; + + public FileUploadPart(FileUpload fileUpload) { + this.fileUpload = fileUpload; + } + + @Override + public InputStream getInputStream() throws IOException { + return Files.newInputStream(new File(fileUpload.uploadedFileName()).toPath()); + } + + @Override + public String getContentType() { + return fileUpload.contentType(); + } + + @Override + public String getName() { + return fileUpload.name(); + } + + @Override + public String getSubmittedFileName() { + return fileUpload.fileName(); + } + + @Override + public long getSize() { + return fileUpload.size(); + } + + @Override + public void write(String fileName) throws IOException { + FileUtils.copyFile(new File(fileUpload.uploadedFileName()), new File(fileName)); + } +} 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 new file mode 100644 index 00000000000..701b117ab6c --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletRequestEx.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.foundation.vertx.http; + +import jakarta.servlet.http.HttpServletRequest; + +public interface HttpServletRequestEx extends HttpServletRequest, BodyBufferSupport { + default void setHeader(String name, String value) { + + } + + default void addHeader(String name, String value) { + + } + + default void setParameter(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 new file mode 100644 index 00000000000..a565ed7791e --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.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.foundation.vertx.http; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.concurrent.CompletableFuture; + +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(); + + void setAttribute(String key, Object value); + + Object getAttribute(String key); + + 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 new file mode 100644 index 00000000000..acd2fc1978a --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/ReadStreamPart.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.concurrent.CompletableFuture; +import java.util.function.Function; + +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.Context; +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.file.AsyncFile; +import io.vertx.core.file.OpenOptions; +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:
+ * 1.getContentType
+ * 2.getSubmittedFileName
+ * extend from AbstractPart just because want to make it be Part type, + * so that can be sent by + * {@link org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse#sendPart(Part) VertxServerResponseToHttpServletResponse.sendPart} + */ +public class ReadStreamPart extends AbstractPart { + private static final Logger LOGGER = LoggerFactory.getLogger(ReadStreamPart.class); + + private final Context context; + + private final ReadStream readStream; + + public ReadStreamPart(Context context, HttpClientResponse httpClientResponse) { + this(context, (ReadStream) httpClientResponse); + + setSubmittedFileName( + HttpUtils.parseFileNameFromHeaderValue(httpClientResponse.getHeader(HttpHeaders.CONTENT_DISPOSITION))); + + String contentType = httpClientResponse.getHeader(HttpHeaders.CONTENT_TYPE); + if (StringUtils.isNotEmpty(contentType)) { + this.contentType(contentType); + } + } + + public ReadStreamPart(Context context, ReadStream readStream) { + this.context = context; + this.readStream = readStream; + + readStream.pause(); + } + + public Context getContext() { + return context; + } + + public ReadStream getReadStream() { + return readStream; + } + + /** + * @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(readStream, writeStream, null); + } + + public CompletableFuture saveAsBytes() { + return saveAs(Buffer::getBytes); + } + + public CompletableFuture saveAsString() { + return saveAs(Buffer::toString); + } + + public CompletableFuture saveAs(Function converter) { + CompletableFuture future = new CompletableFuture<>(); + Buffer buffer = Buffer.buffer(); + + // if readStream.resume() not run on correct eventloop, will: + // 1.create a context task to save last chunk data to buffer + // 2.activate connection to read new data + // but maybe 2 will run before 1, that will cause lost data or get incorrect data + context.runOnContext(V -> { + readStream.exceptionHandler(future::completeExceptionally); + readStream.handler(buffer::appendBuffer); + readStream.endHandler(v -> future.complete(converter.apply(buffer))); + readStream.resume(); + }); + + return future; + } + + /** + * @return future of save to file, future complete means write to file finished + */ + public CompletableFuture saveToFile(String fileName) { + File file = new File(fileName); + file.getParentFile().mkdirs(); + OpenOptions openOptions = new OpenOptions().setCreateNew(true); + return saveToFile(file, openOptions); + } + + /** + * @return future of save to file, future complete means write to file finished + */ + public CompletableFuture saveToFile(File file, OpenOptions openOptions) { + CompletableFuture future = new CompletableFuture<>(); + + context.runOnContext((v) -> { + Vertx vertx = context.owner(); + Future openFuture = vertx.fileSystem().open(file.getAbsolutePath(), openOptions); + openFuture.onComplete((s, f) -> onFileOpened(file, s, f, future)); + }); + + return future; + } + + protected void onFileOpened(File file, AsyncFile asyncFile, Throwable failure, CompletableFuture future) { + if (failure != null) { + future.completeExceptionally(failure); + return; + } + + CompletableFuture saveFuture = saveToWriteStream(asyncFile); + 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 new file mode 100644 index 00000000000..82b46b47597 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletRequestEx.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.io.IOUtils; +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 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(); + + private boolean cacheRequest; + + private ServletInputStream inputStream; + + // by servlet specification + // only parse application/x-www-form-urlencoded of post request automatically + // we will parse this even not post method + private Map parameterMap; + + public StandardHttpServletRequestEx(HttpServletRequest request) { + super(request); + } + + public void setCacheRequest(boolean cacheRequest) { + this.cacheRequest = cacheRequest; + } + + @VisibleForTesting + public boolean isCacheRequest() { + return cacheRequest; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + if (this.inputStream == null) { + if (cacheRequest) { + byte[] inputBytes = IOUtils.toByteArray(getRequest().getInputStream()); + Buffer byteBuf = Buffer.buffer(inputBytes); + this.inputStream = new BufferInputStream(byteBuf); + setBodyBuffer(byteBuf); + } else { + this.inputStream = getRequest().getInputStream(); + } + } + return this.inputStream; + } + + @Override + public void setBodyBuffer(Buffer bodyBuffer) { + this.bodyBuffer.setBodyBuffer(bodyBuffer); + } + + @Override + public Buffer getBodyBuffer() { + return bodyBuffer.getBodyBuffer(); + } + + @Override + public byte[] getBodyBytes() { + return bodyBuffer.getBodyBytes(); + } + + @Override + public int getBodyBytesLength() { + return bodyBuffer.getBodyBytesLength(); + } + + private Map parseParameterMap() { + // 1.post method already parsed by servlet + // 2.not APPLICATION_FORM_URLENCODED, no need to enhance + if (getMethod().equalsIgnoreCase(HttpMethod.POST) + || !StringUtils.startsWithIgnoreCase(getContentType(), MediaType.APPLICATION_FORM_URLENCODED)) { + return super.getParameterMap(); + } + + Map> listMap = parseUrlEncodedBody(); + mergeParameterMaptoListMap(listMap); + return convertListMapToArrayMap(listMap); + } + + private Map convertListMapToArrayMap(Map> listMap) { + Map arrayMap = new HashMap<>(); + for (Entry> entry : listMap.entrySet()) { + arrayMap.put(entry.getKey(), entry.getValue().toArray(new String[0])); + } + return arrayMap; + } + + private void mergeParameterMaptoListMap(Map> listMap) { + for (Entry entry : super.getParameterMap().entrySet()) { + List values = listMap.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()); + // follow servlet behavior, inherited value first, and then body value + values.addAll(0, Arrays.asList(entry.getValue())); + } + } + + private Map> parseUrlEncodedBody() { + try (InputStream inputStream = getInputStream()) { + Map> listMap = new HashMap<>(); + String body = IOUtils.toString(inputStream, StandardCharsets.UTF_8); + List pairs = URLEncodedUtils + .parse(body, getCharacterEncoding() == null ? null : Charset.forName(getCharacterEncoding())); + for (NameValuePair pair : pairs) { + List values = listMap.computeIfAbsent(pair.getName(), k -> new ArrayList<>()); + values.add(pair.getValue()); + } + return listMap; + } catch (IOException e) { + throw new IllegalStateException("", e); + } + } + + @Override + public String[] getParameterValues(String name) { + return getParameterMap().get(name); + } + + @Override + public String getParameter(String name) { + String[] values = getParameterMap().get(name); + return values == null ? null : values[0]; + } + + @Override + public Enumeration getParameterNames() { + return Collections.enumeration(getParameterMap().keySet()); + } + + @Override + public Map getParameterMap() { + if (parameterMap == null) { + parameterMap = parseParameterMap(); + } + + return parameterMap; + } + + @Override + public void setParameter(String name, String value) { + getParameterMap().put(name, new String[] {value}); + } +} 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 new file mode 100644 index 00000000000..38db248efdb --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.vertx.http; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +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(); + + private final Map attributes = new HashMap<>(); + + private StatusType statusType; + + public StandardHttpServletResponseEx(HttpServletResponse response) { + super(response); + } + + @Override + public void setBodyBuffer(Buffer bodyBuffer) { + this.bodyBuffer.setBodyBuffer(bodyBuffer); + } + + @Override + public Buffer getBodyBuffer() { + return bodyBuffer.getBodyBuffer(); + } + + @Override + public byte[] getBodyBytes() { + return bodyBuffer.getBodyBytes(); + } + + @Override + public int getBodyBytesLength() { + return bodyBuffer.getBodyBytesLength(); + } + + @Override + public void setStatus(int sc) { + super.setStatus(sc); + statusType = Status.fromStatusCode(sc); + } + + @Override + public int getStatus() { + return statusType.getStatusCode(); + } + + @Override + public StatusType getStatusType() { + return statusType; + } + + @Override + public void endResponse() throws IOException { + super.flushBuffer(); + } + + @Override + public void setAttribute(String key, Object value) { + this.attributes.put(key, value); + } + + @Override + public Object getAttribute(String key) { + return this.attributes.get(key); + } + + @Override + public CompletableFuture sendPart(Part part) { + if (part == null) { + return CompletableFuture.completedFuture(null); + } + + DownloadUtils.prepareDownloadHeader(this, part); + + OutputStream outputStream; + try { + outputStream = getOutputStream(); + } catch (IOException e) { + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(e); + return future; + } + + // if context is null, then will switch to sync logic + 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 new file mode 100644 index 00000000000..8a54b8d835f --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxClientRequestToHttpServletRequest.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.foundation.vertx.http; + +import java.util.Collections; +import java.util.Enumeration; + +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; + + private String characterEncoding; + + public VertxClientRequestToHttpServletRequest(HttpClientRequest clientRequest, Buffer bodyBuffer) { + this.clientRequest = clientRequest; + setBodyBuffer(bodyBuffer); + } + + @Override + public String getRequestURI() { + return clientRequest.path(); + } + + @Override + public StringBuffer getRequestURL() { + return new StringBuffer(clientRequest.path()); + } + + @Override + public String getQueryString() { + return clientRequest.query(); + } + + @Override + public String getHeader(String name) { + return clientRequest.headers().get(name); + } + + @Override + public Enumeration getHeaders(String name) { + return Collections.enumeration(clientRequest.headers().getAll(name)); + } + + @Override + public Enumeration getHeaderNames() { + return Collections.enumeration(clientRequest.headers().names()); + } + + @Override + public void setHeader(String name, String value) { + clientRequest.headers().set(name, value); + } + + @Override + public void addHeader(String name, String value) { + clientRequest.headers().add(name, value); + } + + @Override + public String getContextPath() { + return ""; + } + + @Override + public String getMethod() { + return clientRequest.getMethod().name(); + } + + @Override + public String getContentType() { + return clientRequest.headers().get(HttpHeaders.CONTENT_TYPE); + } + + @Override + public String getCharacterEncoding() { + if (characterEncoding == null) { + characterEncoding = HttpUtils.getCharsetFromContentType(getContentType()); + } + + return characterEncoding; + } +} 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 new file mode 100644 index 00000000000..da7e532174b --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxClientResponseToHttpServletResponse.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.vertx.http; + +import java.util.Collection; + +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response.StatusType; + +import org.apache.servicecomb.foundation.common.http.HttpStatus; + +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClientResponse; + +public class VertxClientResponseToHttpServletResponse extends AbstractHttpServletResponse { + private final HttpClientResponse clientResponse; + + private StatusType statusType; + + public VertxClientResponseToHttpServletResponse(HttpClientResponse clientResponse, Buffer bodyBuffer) { + this.clientResponse = clientResponse; + setBodyBuffer(bodyBuffer); + } + + @Override + public int getStatus() { + return clientResponse.statusCode(); + } + + @Override + public StatusType getStatusType() { + if (statusType == null) { + statusType = new HttpStatus(clientResponse.statusCode(), clientResponse.statusMessage()); + } + return statusType; + } + + @Override + public String getContentType() { + return clientResponse.getHeader(HttpHeaders.CONTENT_TYPE); + } + + @Override + public String getHeader(String name) { + return clientResponse.getHeader(name); + } + + @Override + public Collection getHeaders(String name) { + return clientResponse.headers().getAll(name); + } + + @Override + public Collection getHeaderNames() { + return clientResponse.headers().names(); + } +} 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 new file mode 100644 index 00000000000..da55f5ff613 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java @@ -0,0 +1,312 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.servicecomb.foundation.common.http.HttpUtils; +import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpServerRequest; +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 { + private static final Logger LOGGER = LoggerFactory.getLogger(VertxServerRequestToHttpServletRequest.class); + + private static final EmptyAsyncContext EMPTY_ASYNC_CONTEXT = new EmptyAsyncContext(); + + private final RoutingContext context; + + private final HttpServerRequest vertxRequest; + + private Cookie[] cookies; + + private ServletInputStream inputStream; + + private String path; + + private final SocketAddress socketAddress; + + // cache from convert vertx parameters to servlet parameters + private Map parameterMap; + + private String characterEncoding; + + public VertxServerRequestToHttpServletRequest(RoutingContext context, String path) { + this(context); + this.path = path; + } + + public VertxServerRequestToHttpServletRequest(RoutingContext context) { + this.context = context; + this.vertxRequest = context.request(); + this.socketAddress = this.vertxRequest.remoteAddress(); + super.setBodyBuffer(context.body().buffer()); + } + + @Override + public void setBodyBuffer(Buffer bodyBuffer) { + super.setBodyBuffer(bodyBuffer); + if (context instanceof RoutingContextInternal) { + RoutingContextInternal contextInternal = (RoutingContextInternal) context; + contextInternal.setBody(bodyBuffer); + } + this.inputStream = null; + } + + @Override + public String getContentType() { + return this.vertxRequest.getHeader(HttpHeaders.CONTENT_TYPE); + } + + @Override + public Cookie[] getCookies() { + if (cookies == null) { + Set cookieSet = context.request().cookies(); + Cookie[] tmpCookies = new Cookie[cookieSet.size()]; + int idx = 0; + for (io.vertx.core.http.Cookie vertxCookie : cookieSet) { + Cookie cookie = new Cookie(vertxCookie.getName(), vertxCookie.getValue()); + tmpCookies[idx] = cookie; + idx++; + } + cookies = tmpCookies; + } + return cookies; + } + + @Override + public String getParameter(String name) { + if (parameterMap != null) { + String[] values = parameterMap.get(name); + return values == null ? null : values[0]; + } + + return this.vertxRequest.getParam(name); + } + + @Override + public Enumeration getParameterNames() { + if (parameterMap != null) { + return Collections.enumeration(parameterMap.keySet()); + } + + return Collections.enumeration(this.vertxRequest.params().names()); + } + + @Override + public String[] getParameterValues(String name) { + if (parameterMap != null) { + return parameterMap.get(name); + } + + List paramList = this.vertxRequest.params().getAll(name); + return paramList.toArray(new String[0]); + } + + @Override + public Map getParameterMap() { + if (parameterMap == null) { + Map paramMap = new HashMap<>(); + MultiMap map = this.vertxRequest.params(); + for (String name : map.names()) { + paramMap.put(name, map.getAll(name).toArray(new String[0])); + } + parameterMap = paramMap; + } + + return parameterMap; + } + + @Override + public void setParameter(String name, String value) { + if (parameterMap != null) { + parameterMap.put(name, new String[] {value}); + return; + } + + vertxRequest.params().set(name, value); + } + + @Override + public String getScheme() { + return this.vertxRequest.scheme(); + } + + @Override + public String getRemoteAddr() { + return this.socketAddress.host(); + } + + @Override + public String getRemoteHost() { + return this.socketAddress.host(); + } + + @Override + public int getRemotePort() { + return this.socketAddress.port(); + } + + @Override + public String getLocalAddr() { + return this.vertxRequest.localAddress().host(); + } + + @Override + public int getLocalPort() { + return this.vertxRequest.localAddress().port(); + } + + @Override + public String getHeader(String name) { + return this.vertxRequest.getHeader(name); + } + + @Override + public Enumeration getHeaders(String name) { + return Collections.enumeration(this.vertxRequest.headers().getAll(name)); + } + + @Override + public Enumeration getHeaderNames() { + return Collections.enumeration(vertxRequest.headers().names()); + } + + @Override + public int getIntHeader(String name) { + String header = this.vertxRequest.getHeader(name); + if (header == null) { + return -1; + } + + return Integer.parseInt(header); + } + + @Override + public String getMethod() { + return this.vertxRequest.method().name(); + } + + @Override + public String getPathInfo() { + return this.vertxRequest.path(); + } + + @Override + public String getQueryString() { + return this.vertxRequest.query(); + } + + @Override + public String getRequestURI() { + if (this.path == null) { + this.path = vertxRequest.path(); + } + 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(); + } + + @Override + public String getContextPath() { + return ""; + } + + @Override + public ServletInputStream getInputStream() { + if (inputStream != null) { + return inputStream; + } + if (context.body().buffer() == null) { + return null; + } + inputStream = new BufferInputStream(context.body().buffer()); + return inputStream; + } + + @Override + public AsyncContext getAsyncContext() { + return EMPTY_ASYNC_CONTEXT; + } + + @Override + public Part getPart(String name) { + Optional upload = context.fileUploads() + .stream() + .filter(fileUpload -> fileUpload.name().equals(name)) + .findFirst(); + if (!upload.isPresent()) { + LOGGER.debug("No such file with name: {}.", name); + return null; + } + + final FileUpload fileUpload = upload.get(); + return new FileUploadPart(fileUpload); + } + + @Override + public Collection getParts() { + return context.fileUploads().stream().map(FileUploadPart::new).collect(Collectors.toList()); + } + + public RoutingContext getContext() { + return context; + } + + @Override + public String getCharacterEncoding() { + if (characterEncoding == null) { + characterEncoding = HttpUtils.getCharsetFromContentType(getContentType()); + } + + return characterEncoding; + } +} 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 new file mode 100644 index 00000000000..81785e9698c --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.IOException; +import java.util.Collection; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +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; + + private final HttpServerResponse serverResponse; + + private StatusType statusType; + + public VertxServerResponseToHttpServletResponse(HttpServerResponse serverResponse) { + this.context = Vertx.currentContext(); + this.serverResponse = serverResponse; + + Objects.requireNonNull(context, "must run in vertx context."); + } + + @Override + public void setContentType(String type) { + serverResponse.headers().set(HttpHeaders.CONTENT_TYPE, type); + } + + @Override + public void setContentLength(int len) { + serverResponse.headers().set(HttpHeaders.CONTENT_LENGTH, String.valueOf(len)); + } + + + @Override + public void setStatus(int sc) { + serverResponse.setStatusCode(sc); + } + + @Override + public StatusType getStatusType() { + if (statusType == null) { + statusType = new HttpStatus(serverResponse.getStatusCode(), serverResponse.getStatusMessage()); + } + return statusType; + } + + @Override + public void addHeader(String name, String value) { + serverResponse.headers().add(name, value); + } + + @Override + public void setHeader(String name, String value) { + serverResponse.headers().set(name, value); + } + + @Override + public int getStatus() { + return serverResponse.getStatusCode(); + } + + @Override + public String getContentType() { + return serverResponse.headers().get(HttpHeaders.CONTENT_TYPE); + } + + @Override + public String getHeader(String name) { + return serverResponse.headers().get(name); + } + + @Override + public Collection getHeaders(String name) { + return serverResponse.headers().getAll(name); + } + + @Override + public Collection getHeaderNames() { + return serverResponse.headers().names(); + } + + @Override + public void endResponse() { + if (context == Vertx.currentContext()) { + internalFlushBuffer(); + return; + } + + context.runOnContext(V -> internalFlushBuffer()); + } + + public void internalFlushBuffer() { + if (serverResponse.closed()) { + return; + } + serverResponse.end(); + } + + @Override + public CompletableFuture sendPart(Part part) { + DownloadUtils.prepareDownloadHeader(this, part); + if (part == null) { + return CompletableFuture.completedFuture(null); + } + 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 new file mode 100644 index 00000000000..2bc9288857b --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultClientMetrics.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.foundation.vertx.metrics; + +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 { + private final DefaultClientEndpointMetric clientEndpointMetric; + + public DefaultClientMetrics(DefaultClientEndpointMetric clientEndpointMetric) { + this.clientEndpointMetric = clientEndpointMetric; + } + + public DefaultClientEndpointMetric getClientEndpointMetric() { + return this.clientEndpointMetric; + } + + @Override + public DefaultRequestMetric requestBegin(String uri, HttpRequest request) { + DefaultRequestMetric requestMetric = new DefaultRequestMetric(this.clientEndpointMetric); + requestMetric.requestBegin(); + return requestMetric; + } + + @Override + public void requestEnd(DefaultRequestMetric requestMetric, long bytesWritten) { + requestMetric.requestEnd(); + } + + @Override + public void responseBegin(DefaultRequestMetric requestMetric, HttpResponse response) { + requestMetric.responseBegin(); + } + + @Override + public void responseEnd(DefaultRequestMetric requestMetric, long bytesRead) { + requestMetric.responseEnd(); + } +} 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 new file mode 100644 index 00000000000..1279a4f8566 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpClientMetrics.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.foundation.vertx.metrics; + +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.DefaultRequestMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; + +import io.vertx.core.net.SocketAddress; +import io.vertx.core.spi.metrics.HttpClientMetrics; + +public class DefaultHttpClientMetrics implements + HttpClientMetrics { + private static final String PROTOCOL = "http://"; + + private final DefaultClientEndpointMetricManager clientEndpointMetricManager; + + public DefaultHttpClientMetrics(DefaultClientEndpointMetricManager clientEndpointMetricManager) { + this.clientEndpointMetricManager = clientEndpointMetricManager; + } + + @Override + public DefaultClientMetrics createEndpointMetrics( + SocketAddress remoteAddress, int maxPoolSize) { + return new DefaultClientMetrics( + getOrCreateEndpointMetric(remoteAddress)); + } + + private DefaultClientEndpointMetric getOrCreateEndpointMetric(SocketAddress remoteAddress) { + return this.clientEndpointMetricManager + .getOrCreateEndpointMetric(PROTOCOL + remoteAddress.host() + ":" + remoteAddress.port()); + } + + @Override + public DefaultTcpSocketMetric connected(SocketAddress remoteAddress, String remoteName) { + DefaultTcpSocketMetric socketMetric = new DefaultTcpSocketMetric( + getOrCreateEndpointMetric(remoteAddress)); + socketMetric.onConnect(); + return socketMetric; + } + + @Override + public void disconnected(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress) { + socketMetric.onDisconnect(); + } + + @Override + public void bytesRead(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { + socketMetric.getEndpointMetric().addBytesRead(numberOfBytes); + } + + @Override + public void bytesWritten(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { + socketMetric.getEndpointMetric().addBytesWritten(numberOfBytes); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpServerMetrics.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpServerMetrics.java new file mode 100644 index 00000000000..011deb980a3 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpServerMetrics.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.foundation.vertx.metrics; + +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultRequestMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; + +import io.vertx.core.net.SocketAddress; +import io.vertx.core.spi.metrics.HttpServerMetrics; +import io.vertx.core.spi.observability.HttpRequest; +import io.vertx.core.spi.observability.HttpResponse; + +/** + * important: not singleton, every HttpServer instance relate to an HttpServerMetrics instance + */ +public class DefaultHttpServerMetrics implements + HttpServerMetrics { + private final DefaultServerEndpointMetric endpointMetric; + + public DefaultHttpServerMetrics(DefaultServerEndpointMetric endpointMetric) { + this.endpointMetric = endpointMetric; + } + + public DefaultServerEndpointMetric getEndpointMetric() { + return endpointMetric; + } + + @Override + public DefaultRequestMetric requestBegin(DefaultTcpSocketMetric socketMetric, HttpRequest request) { + DefaultRequestMetric requestMetric = new DefaultRequestMetric(socketMetric.getEndpointMetric()); + requestMetric.requestBegin(); + return requestMetric; + } + + @Override + public void requestEnd(DefaultRequestMetric requestMetric, HttpRequest request, long bytesRead) { + requestMetric.requestEnd(); + } + + @Override + public void responseBegin(DefaultRequestMetric requestMetric, HttpResponse response) { + requestMetric.responseBegin(); + } + + @Override + public void responseEnd(DefaultRequestMetric requestMetric, HttpResponse response, long bytesWritten) { + requestMetric.responseEnd(); + } + + @Override + public DefaultTcpSocketMetric connected(SocketAddress remoteAddress, String remoteName) { + DefaultTcpSocketMetric socketMetric = new DefaultTcpSocketMetric(endpointMetric); + socketMetric.onConnect(); + return socketMetric; + } + + @Override + public void disconnected(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress) { + socketMetric.onDisconnect(); + } + + @Override + public void bytesRead(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { + socketMetric.getEndpointMetric().addBytesRead(numberOfBytes); + } + + @Override + public void bytesWritten(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { + socketMetric.getEndpointMetric().addBytesWritten(numberOfBytes); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultTcpClientMetrics.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultTcpClientMetrics.java new file mode 100644 index 00000000000..1bc6f706cb6 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultTcpClientMetrics.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.vertx.metrics; + +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.DefaultTcpSocketMetric; + +import io.vertx.core.net.SocketAddress; +import io.vertx.core.spi.metrics.TCPMetrics; + +/** + * important: not singleton, every NetClient instance relate to a TCPMetrics instance + */ +public class DefaultTcpClientMetrics implements TCPMetrics { + private static final String PROTOCOL = "tcp://"; + + private final DefaultClientEndpointMetricManager clientEndpointMetricManager; + + public DefaultTcpClientMetrics(DefaultClientEndpointMetricManager clientEndpointMetricManager) { + this.clientEndpointMetricManager = clientEndpointMetricManager; + } + + @Override + public DefaultTcpSocketMetric connected(SocketAddress remoteAddress, String remoteName) { + DefaultClientEndpointMetric endpointMetric = this.clientEndpointMetricManager + .getOrCreateEndpointMetric(PROTOCOL + remoteAddress.toString()); + DefaultTcpSocketMetric socketMetric = new DefaultTcpSocketMetric(endpointMetric); + socketMetric.onConnect(); + return socketMetric; + } + + @Override + public void disconnected(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress) { + socketMetric.onDisconnect(); + } + + @Override + public void bytesRead(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { + socketMetric.getEndpointMetric().addBytesRead(numberOfBytes); + } + + @Override + public void bytesWritten(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { + socketMetric.getEndpointMetric().addBytesWritten(numberOfBytes); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultTcpServerMetrics.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultTcpServerMetrics.java new file mode 100644 index 00000000000..633d6a817d2 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultTcpServerMetrics.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.foundation.vertx.metrics; + +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; + +import io.vertx.core.net.SocketAddress; +import io.vertx.core.spi.metrics.TCPMetrics; + +/** + * important: not singleton, every NetServer instance relate to a TcpServerMetrics instance + */ +public class DefaultTcpServerMetrics implements TCPMetrics { + private final DefaultServerEndpointMetric endpointMetric; + + public DefaultTcpServerMetrics(DefaultServerEndpointMetric endpointMetric) { + this.endpointMetric = endpointMetric; + } + + public DefaultServerEndpointMetric getEndpointMetric() { + return endpointMetric; + } + + @Override + public DefaultTcpSocketMetric connected(SocketAddress remoteAddress, String remoteName) { + DefaultTcpSocketMetric socketMetric = new DefaultTcpSocketMetric(endpointMetric); + socketMetric.onConnect(); + return socketMetric; + } + + @Override + public void disconnected(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress) { + socketMetric.onDisconnect(); + } + + @Override + public void bytesRead(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { + socketMetric.getEndpointMetric().addBytesRead(numberOfBytes); + } + + @Override + public void bytesWritten(DefaultTcpSocketMetric socketMetric, SocketAddress remoteAddress, long numberOfBytes) { + socketMetric.getEndpointMetric().addBytesWritten(numberOfBytes); + } +} 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 new file mode 100644 index 00000000000..c0b89a42eea --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetrics.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.metrics; + +import java.util.Map; + +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.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 implements VertxMetrics { + // to support listen multiple addresses, must use a map to manage the metric + private final Map serverEndpointMetricMap = new ConcurrentHashMapEx<>(); + + private final DefaultClientEndpointMetricManager clientEndpointMetricManager; + + public DefaultVertxMetrics(VertxOptions vertxOptions) { + this.clientEndpointMetricManager = new DefaultClientEndpointMetricManager( + (MetricsOptionsEx) vertxOptions.getMetricsOptions()); + } + + public DefaultClientEndpointMetricManager getClientEndpointMetricManager() { + return clientEndpointMetricManager; + } + + public Map getServerEndpointMetricMap() { + return serverEndpointMetricMap; + } + + @Override + public HttpServerMetrics createHttpServerMetrics(HttpServerOptions options, SocketAddress localAddress) { + DefaultServerEndpointMetric endpointMetric = serverEndpointMetricMap + .computeIfAbsent(localAddress.toString(), DefaultServerEndpointMetric::new); + return new DefaultHttpServerMetrics(endpointMetric); + } + + @Override + public HttpClientMetrics createHttpClientMetrics(HttpClientOptions options) { + return new DefaultHttpClientMetrics(clientEndpointMetricManager); + } + + @Override + public TCPMetrics createNetServerMetrics(NetServerOptions options, SocketAddress localAddress) { + DefaultServerEndpointMetric endpointMetric = serverEndpointMetricMap + .computeIfAbsent(localAddress.toString(), DefaultServerEndpointMetric::new); + return new DefaultTcpServerMetrics(endpointMetric); + } + + @Override + public TCPMetrics createNetClientMetrics(NetClientOptions options) { + return new DefaultTcpClientMetrics(clientEndpointMetricManager); + } + + @Override + public boolean isMetricsEnabled() { + return true; + } + + public void setVertx(Vertx vertx) { + clientEndpointMetricManager.setVertx(vertx); + } +} 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 new file mode 100644 index 00000000000..540a644b1e1 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetricsFactory.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.foundation.vertx.metrics; + +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.json.JsonObject; +import io.vertx.core.metrics.MetricsOptions; +import io.vertx.core.spi.VertxMetricsFactory; +import io.vertx.core.spi.metrics.VertxMetrics; + +/** + *

+ * only for one vertx instance
+ * DO NOT inject to vertx by SPI
+ * typical usage:
+ *     DefaultVertxMetricsFactory factory = new DefaultVertxMetricsFactory();
+ *     MetricsOptionsEx metricsOptionsEx = (MetricsOptionsEx) factory.newOptions();
+ *
+ *     VertxOptions vertxOptions = new VertxOptions();
+ *     vertxOptions.setMetricsOptions(metricsOptionsEx);
+ *
+ *     Vertx vertx = Vertx.vertx(vertxOptions);
+ * 
+ */ +public class DefaultVertxMetricsFactory implements VertxMetricsFactory { + private DefaultVertxMetrics vertxMetrics; + + public DefaultVertxMetrics getVertxMetrics() { + return vertxMetrics; + } + + @Override + public synchronized VertxMetrics metrics(VertxOptions options) { + if (vertxMetrics == null) { + vertxMetrics = new DefaultVertxMetrics(options); + } + return vertxMetrics; + } + + @Override + public MetricsOptions newOptions() { + MetricsOptionsEx metricsOptions = new MetricsOptionsEx(); + metricsOptions.setEnabled(true); + return metricsOptions; + } + + @Override + public MetricsOptions newOptions(JsonObject jsonObject) { + return new MetricsOptionsEx(jsonObject); + } + + @Override + public MetricsOptions newOptions(MetricsOptions options) { + return newOptions(options.toJson()); + } + + public void setVertx(Vertx vertx, VertxOptions options) { + if (vertxMetrics == null) { + vertxMetrics = new DefaultVertxMetrics(options); + } + vertxMetrics.setVertx(vertx); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/MetricsOptionsEx.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/MetricsOptionsEx.java new file mode 100644 index 00000000000..0ae1de13cd4 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/MetricsOptionsEx.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.foundation.vertx.metrics; + +import java.util.concurrent.TimeUnit; + +import io.vertx.core.json.JsonObject; +import io.vertx.core.metrics.MetricsOptions; + +public class MetricsOptionsEx extends MetricsOptions { + private long checkClientEndpointMetricIntervalInMilliseconds = TimeUnit.MINUTES.toMillis(1); + + private long checkClientEndpointMetricExpiredInNano = TimeUnit.MINUTES.toNanos(15); + + public MetricsOptionsEx() { + super(); + } + + public MetricsOptionsEx(JsonObject json) { + super(json); + } + + public long getCheckClientEndpointMetricIntervalInMilliseconds() { + return checkClientEndpointMetricIntervalInMilliseconds; + } + + public void setCheckClientEndpointMetricIntervalInMinute(long minute) { + this.checkClientEndpointMetricIntervalInMilliseconds = TimeUnit.MINUTES.toMillis(minute); + } + + public long getCheckClientEndpointMetricExpiredInNano() { + return checkClientEndpointMetricExpiredInNano; + } + + public void setCheckClientEndpointMetricExpiredInNano(long nanoTime) { + this.checkClientEndpointMetricExpiredInNano = nanoTime; + } + + public void setCheckClientEndpointMetricExpiredInMinute(long minute) { + this.checkClientEndpointMetricExpiredInNano = TimeUnit.MINUTES.toNanos(minute); + } +} 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 new file mode 100644 index 00000000000..ce618078957 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetric.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.foundation.vertx.metrics.metric; + +import java.util.concurrent.atomic.LongAdder; + +import com.google.common.annotations.VisibleForTesting; + +/** + * for one listen address, include multiple httpClient or httpServer + */ +public class DefaultClientEndpointMetric extends DefaultEndpointMetric { + private final LongAdder queue = new LongAdder(); + + // 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 volatile long lastNanoTime = System.nanoTime(); + + public DefaultClientEndpointMetric(String address) { + super(address); + } + + @VisibleForTesting + public long getLastNanoTime() { + return lastNanoTime; + } + + @Override + public void onDisconnect() { + super.onDisconnect(); + lastNanoTime = System.nanoTime(); + } + + public long getQueueCount() { + return queue.longValue(); + } + + public void enqueueRequest() { + queue.increment(); + } + + public void dequeueRequest() { + queue.decrement(); + } + + public boolean isExpired(long nsTimeout) { + return getCurrentConnectionCount() == 0 + && (System.nanoTime() - lastNanoTime) > nsTimeout; + } +} 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 new file mode 100644 index 00000000000..17977a468ca --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.vertx.metrics.metric; + +import java.util.Map; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.vertx.metrics.MetricsOptionsEx; + +import com.google.common.annotations.VisibleForTesting; + +import io.vertx.core.Vertx; + +public class DefaultClientEndpointMetricManager { + private final MetricsOptionsEx metricsOptionsEx; + + // to avoid save too many endpoint that not exist any more + // must check expired periodically + private final Map clientEndpointMetricMap = new ConcurrentHashMapEx<>(); + + // clientEndpointMetricMap is thread safe + // but get/isExpired/remove is not safe + // 1.isExpired + // 2.get + // 3.remove + // will get a removed instance + // so must lock the logic + private final ReadWriteLock rwlock = new ReentrantReadWriteLock(); + + public DefaultClientEndpointMetricManager(MetricsOptionsEx metricsOptionsEx) { + this.metricsOptionsEx = metricsOptionsEx; + } + + public DefaultClientEndpointMetric getOrCreateEndpointMetric(String address) { + rwlock.readLock().lock(); + try { + if (clientEndpointMetricMap.get(address) == null) { + clientEndpointMetricMap.put(address, new DefaultClientEndpointMetric(address)); + } + return clientEndpointMetricMap.get(address); + } finally { + rwlock.readLock().unlock(); + } + } + + @VisibleForTesting + public DefaultClientEndpointMetric getClientEndpointMetric(String serverAddress) { + return clientEndpointMetricMap.get(serverAddress); + } + + public Map getClientEndpointMetricMap() { + return clientEndpointMetricMap; + } + + @VisibleForTesting + public void onCheckClientEndpointMetricExpired(long periodic) { + for (DefaultClientEndpointMetric metric : clientEndpointMetricMap.values()) { + if (metric.isExpired(metricsOptionsEx.getCheckClientEndpointMetricExpiredInNano())) { + rwlock.writeLock().lock(); + try { + if (metric.isExpired(metricsOptionsEx.getCheckClientEndpointMetricExpiredInNano())) { + clientEndpointMetricMap.remove(metric.getAddress()); + } + } finally { + rwlock.writeLock().unlock(); + } + } + } + } + + public void setVertx(Vertx vertx) { + vertx.setPeriodic(metricsOptionsEx.getCheckClientEndpointMetricIntervalInMilliseconds(), + this::onCheckClientEndpointMetricExpired); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientTaskMetric.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientTaskMetric.java new file mode 100644 index 00000000000..0b032c5e672 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientTaskMetric.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.foundation.vertx.metrics.metric; + +public class DefaultClientTaskMetric { + private final DefaultClientEndpointMetric endpointMetric; + + public DefaultClientTaskMetric(DefaultClientEndpointMetric endpointMetric) { + this.endpointMetric = endpointMetric; + } + + public void enqueueRequest() { + endpointMetric.enqueueRequest(); + } + + public void dequeueRequest() { + endpointMetric.dequeueRequest(); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultEndpointMetric.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultEndpointMetric.java new file mode 100644 index 00000000000..f26f33b58de --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultEndpointMetric.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.foundation.vertx.metrics.metric; + +import java.util.concurrent.atomic.LongAdder; + +/** + * for one listen address, include multiple httpClient or httpServer + */ +public class DefaultEndpointMetric { + private final String address; + + // summary of connect times from boot + // by this, we can know how many new connections connected recently + private final LongAdder connectCount = new LongAdder(); + + // summary of disconnect times from boot + // by this, we can know how many connections disconnected recently + private final LongAdder disconnectCount = new LongAdder(); + + private final LongAdder bytesRead = new LongAdder(); + + private final LongAdder bytesWritten = new LongAdder(); + + private final LongAdder requests = new LongAdder(); + + private final LongAdder latency = new LongAdder(); + + public DefaultEndpointMetric(String address) { + this.address = address; + } + + public String getAddress() { + return address; + } + + public long getConnectCount() { + return connectCount.longValue(); + } + + public long getDisconnectCount() { + return disconnectCount.longValue(); + } + + public long getCurrentConnectionCount() { + return connectCount.longValue() - disconnectCount.longValue(); + } + + public void onConnect() { + connectCount.increment(); + } + + public void onDisconnect() { + disconnectCount.increment(); + } + + public long getBytesRead() { + return bytesRead.longValue(); + } + + public void addBytesRead(long bytes) { + bytesRead.add(bytes); + } + + public long getBytesWritten() { + return bytesWritten.longValue(); + } + + public void addBytesWritten(long bytes) { + bytesWritten.add(bytes); + } + + public void incrementRequests() { + requests.increment(); + } + + public long getRequests() { + return requests.longValue(); + } + + public void addLatency(long delta) { + latency.add(delta); + } + + public long getLatency() { + return latency.longValue(); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultRequestMetric.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultRequestMetric.java new file mode 100644 index 00000000000..224d416c2e5 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultRequestMetric.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.foundation.vertx.metrics.metric; + +public class DefaultRequestMetric { + private long requestBeginTime; + + private long requestEndTime; + + private long responseBeginTime; + + private long responseEndTime; + + private final DefaultEndpointMetric endpointMetric; + + public DefaultRequestMetric(DefaultEndpointMetric endpointMetric) { + this.endpointMetric = endpointMetric; + } + + public long getRequestBeginTime() { + return requestBeginTime != 0 ? requestBeginTime : System.nanoTime(); + } + + public long getRequestEndTime() { + return requestEndTime != 0 ? requestEndTime : System.nanoTime(); + } + + public long getResponseBeginTime() { + return responseBeginTime != 0 ? responseBeginTime : System.nanoTime(); + } + + public long getResponseEndTime() { + return responseEndTime != 0 ? responseEndTime : System.nanoTime(); + } + + public void requestBegin() { + this.requestBeginTime = System.nanoTime(); + } + + public void requestEnd() { + this.requestEndTime = System.nanoTime(); + } + + public void responseBegin() { + this.responseBeginTime = System.nanoTime(); + } + + public void responseEnd() { + this.responseEndTime = System.nanoTime(); + this.endpointMetric.incrementRequests(); + this.endpointMetric.addLatency(System.nanoTime() - this.requestBeginTime); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultServerEndpointMetric.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultServerEndpointMetric.java new file mode 100644 index 00000000000..c01af512ee7 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultServerEndpointMetric.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.foundation.vertx.metrics.metric; + +import java.util.concurrent.atomic.LongAdder; + +public class DefaultServerEndpointMetric extends DefaultEndpointMetric { + private final LongAdder rejectByConnectionLimitCount = new LongAdder(); + + public DefaultServerEndpointMetric(String address) { + super(address); + } + + public long getRejectByConnectionLimitCount() { + return rejectByConnectionLimitCount.longValue(); + } + + public void onRejectByConnectionLimit() { + rejectByConnectionLimitCount.increment(); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultTcpSocketMetric.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultTcpSocketMetric.java new file mode 100644 index 00000000000..32651d93f64 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultTcpSocketMetric.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.foundation.vertx.metrics.metric; + +import com.google.common.annotations.VisibleForTesting; + +public class DefaultTcpSocketMetric { + protected DefaultEndpointMetric endpointMetric; + + protected boolean connected = true; + + protected long connectedTime = System.nanoTime(); + + public DefaultTcpSocketMetric(DefaultEndpointMetric endpointMetric) { + this.endpointMetric = endpointMetric; + } + + public DefaultEndpointMetric getEndpointMetric() { + return endpointMetric; + } + + @VisibleForTesting + public boolean isConnected() { + return connected; + } + + public void onConnect() { + endpointMetric.onConnect(); + this.connected = true; + } + + public void onDisconnect() { + endpointMetric.onDisconnect(); + this.connected = false; + } + + @VisibleForTesting + public long getConnectedTime() { + return connectedTime; + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpBufferHandler.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpBufferHandler.java new file mode 100644 index 00000000000..f7171117448 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpBufferHandler.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.foundation.vertx.server; + +import io.vertx.core.buffer.Buffer; + +/** + * TcpBufferHandler + * + * + */ +public interface TcpBufferHandler { + void handle(long msgId, Buffer headerBuffer, Buffer bodyBuffer); +} 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 new file mode 100644 index 00000000000..4e79f69fca3 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpParser.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.foundation.vertx.server; + +import java.nio.charset.StandardCharsets; + +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.parsetools.RecordParser; + +public class TcpParser implements Handler { + public static final byte[] TCP_MAGIC; + + public static final int TCP_HEADER_LENGTH = 23; + + static { + TCP_MAGIC = "CSE.TCP".getBytes(StandardCharsets.UTF_8); + } + + enum ParseStatus { + TCP_HEADER, + TCP_PAYLOAD + } + + private final TcpBufferHandler outputHandler; + + private RecordParser parser; + + private ParseStatus status; + + private long msgId; + + // 仅仅是header + body,不包括headerLen本身 + private int totalLen; + + private int headerLen; + + public TcpParser(TcpBufferHandler output) { + this.outputHandler = output; + + reset(); + } + + /** + * 在解析出错时,通过重新创建parser对象,将整个缓冲区重置 + */ + protected void reset() { + parser = RecordParser.newFixed(TCP_HEADER_LENGTH, this::onParse); + status = ParseStatus.TCP_HEADER; + + parser.handle(Buffer.buffer(0)); + } + + public boolean firstNEqual(byte[] a, byte[] b, int n) { + assert a.length >= n && b.length >= n; + + for (int i = 0; i < n; i++) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } + + protected void onParse(Buffer buffer) { + switch (status) { + case TCP_HEADER: + if (!firstNEqual(TCP_MAGIC, buffer.getBytes(), TCP_MAGIC.length)) { + reset(); + return; + } + + 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."); + } + + if (totalLen == 0) { + onReadOnePackage(null, null); + return; + } + + parser.fixedSizeMode(totalLen); + status = ParseStatus.TCP_PAYLOAD; + break; + + case TCP_PAYLOAD: + Buffer headerBuffer = buffer.slice(0, headerLen); + Buffer bodyBuffer = buffer.slice(headerLen, buffer.length()); + onReadOnePackage(headerBuffer, bodyBuffer); + break; + + default: + break; + } + } + + private void onReadOnePackage(Buffer headerBuffer, Buffer bodyBuffer) { + outputHandler.handle(msgId, headerBuffer, bodyBuffer); + + parser.fixedSizeMode(TCP_HEADER_LENGTH); + status = ParseStatus.TCP_HEADER; + } + + public void handle(Buffer buf) { + parser.handle(buf); + } +} 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 new file mode 100644 index 00000000000..8a960175ad8 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServer.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.foundation.vertx.server; + +import java.net.InetSocketAddress; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +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.vertx.AsyncResultCallback; +import org.apache.servicecomb.foundation.vertx.VertxTLSBuilder; +import org.apache.servicecomb.foundation.vertx.metrics.DefaultTcpServerMetrics; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; +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; +import io.vertx.core.net.impl.NetSocketImpl; + +public class TcpServer { + private static final Logger LOGGER = LoggerFactory.getLogger(TcpServer.class); + + private final URIEndpointObject endpointObject; + + public TcpServer(URIEndpointObject endpointObject) { + this.endpointObject = endpointObject; + } + + public void init(Vertx vertx, String sslKey, AsyncResultCallback callback) { + NetServer netServer; + if (endpointObject.isSslEnabled()) { + 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()); + NetServerOptions serverOptions = new NetServerOptions(); + VertxTLSBuilder.buildNetServerOptions(sslOption, sslCustom, serverOptions); + netServer = vertx.createNetServer(serverOptions); + } else { + netServer = vertx.createNetServer(); + } + + netServer.connectHandler(netSocket -> { + DefaultTcpServerMetrics serverMetrics = (DefaultTcpServerMetrics) ((NetSocketImpl) netSocket).metrics(); + DefaultServerEndpointMetric endpointMetric = serverMetrics.getEndpointMetric(); + long connectedCount = endpointMetric.getCurrentConnectionCount(); + int connectionLimit = getConnectionLimit(); + if (connectedCount > connectionLimit) { + netSocket.close(); + endpointMetric.onRejectByConnectionLimit(); + return; + } + + TcpServerConnection connection = createTcpServerConnection(); + connection.init(netSocket); + }); + netServer.exceptionHandler( + e -> LOGGER.error("Unexpected error in server.", e)); + InetSocketAddress socketAddress = endpointObject.getSocketAddress(); + 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, f)); + }); + } + + protected int getConnectionLimit() { + return Integer.MAX_VALUE; + } + + protected TcpServerConnection createTcpServerConnection() { + return new TcpServerConnection(); + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServerConnection.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServerConnection.java new file mode 100644 index 00000000000..dae3d422161 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServerConnection.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.foundation.vertx.server; + +import org.apache.servicecomb.foundation.vertx.tcp.TcpConnection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.net.NetSocket; +import io.vertx.core.net.impl.NetSocketImpl; + +public class TcpServerConnection extends TcpConnection { + private static final Logger LOGGER = LoggerFactory.getLogger(TcpServerConnection.class); + + protected TcpParser splitter; + + public void init(NetSocket netSocket) { + // currently, socket always be NetSocketImpl + this.initNetSocket((NetSocketImpl) netSocket); + + String remoteAddress = netSocket.remoteAddress().toString(); + LOGGER.info("connect from {}, in thread {}", + remoteAddress, + Thread.currentThread().getName()); + netSocket.exceptionHandler(e -> LOGGER.error("disconnected from {}, in thread {}, cause {}", + remoteAddress, + Thread.currentThread().getName(), + e.getMessage())); + netSocket.closeHandler(Void -> LOGGER.info("disconnected from {}, in thread {}", + remoteAddress, + Thread.currentThread().getName())); + + netSocket.handler(splitter); + } +} 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 new file mode 100644 index 00000000000..af0d4acf185 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferInputStream.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.IOException; +import java.nio.charset.StandardCharsets; + +import io.vertx.core.buffer.Buffer; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; + +public class BufferInputStream extends ServletInputStream { + private int readIndex = 0; + + private final Buffer byteBuf; + + public BufferInputStream(Buffer buffer) { + this.byteBuf = buffer; + } + + @Override + public long skip(long len) { + int skipLen = Math.min((int) len, available()); + this.readIndex += skipLen; + return skipLen; + } + + public byte readByte() { + int index = readIndex; + readIndex = index + 1; + return byteBuf.getByte(index); + } + + @Override + public int read() { + return this.readByte() & 255; + } + + public boolean readBoolean() { + return this.readByte() != 0; + } + + public short readShort() { + int index = readIndex; + readIndex = index + 2; + return byteBuf.getShort(index); + } + + public int readInt() { + int index = readIndex; + readIndex = index + 4; + return byteBuf.getInt(index); + } + + public long readLong() { + int index = readIndex; + readIndex = index + 8; + return byteBuf.getLong(index); + } + + public int getIndex() { + return readIndex; + } + + public String readString() { + int length = readInt(); + byte[] bytes = new byte[length]; + int index = readIndex; + readIndex = index + length; + byteBuf.getBytes(index, readIndex, bytes); + return new String(bytes, StandardCharsets.UTF_8); + } + + @Override + public int read(byte[] b) { + return read(b, 0, b.length); + } + + @Override + public int read(byte[] b, int off, int len) { + int avail = available(); + if (avail <= 0) { + return -1; + } + + if (len == 0) { + return 0; + } + + if (len > avail) { + len = avail; + } + + int index = readIndex; + readIndex = index + len; + + byteBuf.getBytes(index, readIndex, b); + return len; + } + + @Override + public int available() { + return byteBuf.length() - readIndex; + } + + @Override + public void close() { + // nothing to do + } + + @Override + public void reset() throws IOException { + readIndex = 0; + } + + public Buffer getByteBuf() { + return byteBuf; + } + + @Override + public boolean isFinished() { + return byteBuf.length() > readIndex; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setReadListener(ReadListener readListener) { + } +} 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 new file mode 100644 index 00000000000..42c9b31261d --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferOutputStream.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.vertx.stream; + +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +import io.vertx.core.buffer.Buffer; + +/** + * BufferOutputStream. + * + * Notice: will not release the underlining ByteBuffer, the user is responsible to release it. + */ +public class BufferOutputStream extends OutputStream { + private static final int DIRECT_BUFFER_SIZE = 1024; + + protected Buffer byteBuf; + + public BufferOutputStream() { + this(Buffer.buffer(DIRECT_BUFFER_SIZE)); + } + + public BufferOutputStream(Buffer buffer) { + this.byteBuf = buffer; + } + + public Buffer getBuffer() { + return byteBuf; + } + + public int length() { + return byteBuf.length(); + } + + public void writeByte(byte value) { + byteBuf.appendByte(value); + } + + // 实际是写byte + @Override + public void write(int byteValue) { + byteBuf.appendByte((byte) byteValue); + } + + public void write(boolean value) { + byteBuf.appendByte(value ? (byte) 1 : (byte) 0); + } + + public void writeInt(int pos, int value) { + byteBuf.setInt(pos, value); + } + + public void writeShort(short value) { + byteBuf.appendShort(value); + } + + public void writeInt(int value) { + byteBuf.appendInt(value); + } + + public void writeLong(long value) { + byteBuf.appendLong(value); + } + + public void writeString(String value) { + writeInt(value.length()); + byteBuf.appendString(value, StandardCharsets.UTF_8.toString()); + } + + @Override + public void write(byte[] b) { + write(b, 0, b.length); + } + + @Override + public void write(byte[] bytes, int offset, int len) { + byteBuf.appendBytes(bytes, offset, len); + } + + @Override + public void close() { + // Do no release byteBuf, the target BufferedInputStream will release it. + } + + public int 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 new file mode 100644 index 00000000000..580d460e5c0 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/InputStreamToReadStream.java @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.IOException; +import java.io.InputStream; +import java.util.Arrays; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.Context; +import io.vertx.core.Handler; +import io.vertx.core.Promise; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.streams.ReadStream; + +public class InputStreamToReadStream implements ReadStream { + private static final Logger LOGGER = LoggerFactory.getLogger(InputStreamToReadStream.class); + + public static final int DEFAULT_READ_BUFFER_SIZE = 1024 * 1024; + + private final Context context; + + private final InputStream inputStream; + + private volatile boolean closed; + + private boolean paused; + + private boolean readInProgress; + + private int readBufferSize = DEFAULT_READ_BUFFER_SIZE; + + private Handler exceptionHandler = this::unhandledException; + + private Handler dataHandler; + + private Handler endHandler; + + private final boolean autoCloseInputStream; + + public InputStreamToReadStream(Context context, InputStream inputStream, + boolean autoCloseInputStream) { + this.context = context; + this.inputStream = inputStream; + this.autoCloseInputStream = autoCloseInputStream; + } + + + public synchronized InputStreamToReadStream readBufferSize(int readBufferSize) { + this.readBufferSize = readBufferSize; + return this; + } + + private void check() { + if (closed) { + throw new IllegalStateException("inputStream is closed"); + } + } + + private void unhandledException(Throwable t) { + LOGGER.error("Unhandled exception", t); + } + + @Override + public synchronized InputStreamToReadStream exceptionHandler(Handler handler) { + check(); + this.exceptionHandler = handler; + return this; + } + + @Override + public synchronized InputStreamToReadStream handler(Handler handler) { + check(); + this.dataHandler = handler; + if (dataHandler != null && !paused && !closed) { + doRead(); + } + return this; + } + + class ReadResult { + int read; + + byte[] bytes = new byte[readBufferSize]; + + void doRead() throws IOException { + read = inputStream.read(bytes); + } + + Buffer toBuffer() { + return Buffer.buffer(Arrays.copyOf(bytes, read)); + } + } + + private synchronized void doRead() { + if (!readInProgress) { + readInProgress = true; + Promise future = Promise.promise(); + context.executeBlocking(() -> { + readInWorker(future); + return null; + }, + true); + future.future().onComplete(this::afterReadInEventloop); + } + } + + private synchronized void readInWorker(Promise future) { + try { + ReadResult readResult = new ReadResult(); + readResult.doRead(); + future.complete(readResult); + } catch (Throwable e) { + future.fail(e); + } + } + + public void handleException(Throwable e) { + closeInputStream(); + exceptionHandler.handle(e); + } + + private synchronized void afterReadInEventloop(ReadResult readResult, Throwable failure) { + if (failure != null) { + handleException(failure); + return; + } + + readInProgress = false; + if (readResult.read < 0) { + handleEnd(); + return; + } + + handleData(readResult.toBuffer()); + if (!paused && dataHandler != null) { + doRead(); + } + } + + @Override + public synchronized InputStreamToReadStream pause() { + check(); + paused = true; + return this; + } + + @Override + public synchronized InputStreamToReadStream resume() { + check(); + if (paused && !closed) { + paused = false; + if (dataHandler != null) { + doRead(); + } + } + return this; + } + + private synchronized void handleData(Buffer buffer) { + if (dataHandler != null) { + dataHandler.handle(buffer); + } + } + + private synchronized void handleEnd() { + dataHandler = null; + closeInputStream(); + if (endHandler != null) { + endHandler.handle(null); + } + } + + private void closeInputStream() { + if (closed) { + return; + } + + closed = true; + if (!autoCloseInputStream) { + return; + } + + try { + inputStream.close(); + } catch (IOException e) { + LOGGER.error("failed to close inputSteam.", e); + } + } + + @Override + public ReadStream endHandler(Handler handler) { + check(); + this.endHandler = handler; + return this; + } + + @Override + public ReadStream fetch(long amount) { + return this; + } +} 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 new file mode 100644 index 00000000000..35681a723a8 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/OutputStreamToWriteStream.java @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.IOException; +import java.io.OutputStream; +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.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.streams.WriteStream; + +/** + * for pump from a readStream + */ +public class OutputStreamToWriteStream implements WriteStream, AsyncCloseable { + private static final int DEFAULT_MAX_BUFFERS = 4; + + private static final int SMALLEST_MAX_BUFFERS = 2; + + private final OutputStream outputStream; + + private final Context context; + + private final boolean autoCloseOutputStream; + + private Handler exceptionHandler; + + // resume readStream + private Handler drainHandler; + + // when invoke close, but outputStream not write all data, must put close logic to closedDeferred + private Runnable closedDeferred; + + private boolean closed; + + // buffers.size() need to loop all node, and maybe result is not correct in concurrent condition + // we just need to flow control by pump, so use another size + private final Queue buffers = new ConcurrentLinkedQueue<>(); + + private final AtomicInteger currentBufferCount = new AtomicInteger(); + + // just indicate if buffers is full, not control add logic + // must >= SMALLEST_MAX_BUFFERS + // if < SMALLEST_MAX_BUFFERS, then maxBuffers will be SMALLEST_MAX_BUFFERS + private int maxBuffers = DEFAULT_MAX_BUFFERS; + + // if currentBufferCount <= drainMark, will invoke drainHandler to resume readStream + private int drainMark = maxBuffers / 2; + + public OutputStreamToWriteStream(Context context, OutputStream outputStream, + boolean autoCloseOutputStream) { + this.context = context; + this.outputStream = outputStream; + this.autoCloseOutputStream = autoCloseOutputStream; + } + + @Override + public WriteStream exceptionHandler(Handler handler) { + this.exceptionHandler = handler; + return this; + } + + private void handleException(Throwable t) { + if (exceptionHandler != null) { + exceptionHandler.handle(t); + } + } + + @Override + public synchronized Future write(Buffer data) { + Promise result = Promise.promise(); + write(data, result); + return result.future(); + } + + private void write(Buffer data, Promise future) { + currentBufferCount.incrementAndGet(); + buffers.add(data); + context.executeBlocking( + () -> { + writeInWorker(future); + return null; + }, + true + ); + } + + protected void writeInWorker(Promise future) { + while (true) { + Buffer buffer = buffers.poll(); + if (buffer == null) { + future.complete(); + return; + } + + try { + outputStream.write(buffer.getBytes()); + + synchronized (OutputStreamToWriteStream.this) { + currentBufferCount.decrementAndGet(); + Runnable action = (currentBufferCount.get() == 0 && closedDeferred != null) + ? closedDeferred : this::checkDrained; + action.run(); + } + } catch (IOException e) { + currentBufferCount.decrementAndGet(); + future.fail(e); + return; + } + } + } + + @Override + public Future end() { + return Future.fromCompletionStage(close()); + } + + @Override + public WriteStream setWriteQueueMaxSize(int maxSize) { + this.maxBuffers = Math.max(maxSize, SMALLEST_MAX_BUFFERS); + this.drainMark = maxBuffers / 2; + return this; + } + + @Override + public synchronized boolean writeQueueFull() { + return currentBufferCount.get() >= maxBuffers; + } + + @Override + public synchronized WriteStream drainHandler(Handler handler) { + this.drainHandler = handler; + return this; + } + + private synchronized void checkDrained() { + if (drainHandler != null && currentBufferCount.get() <= drainMark) { + Handler handler = drainHandler; + drainHandler = null; + handler.handle(null); + } + } + + @Override + public CompletableFuture close() { + return closeInternal(); + } + + private void check() { + checkClosed(); + } + + private void checkClosed() { + if (closed) { + throw new IllegalStateException(this.getClass().getName() + " is closed"); + } + } + + private synchronized CompletableFuture closeInternal() { + check(); + + closed = true; + + CompletableFuture future = new CompletableFuture<>(); + if (currentBufferCount.get() == 0) { + doClose(future); + } else { + closedDeferred = () -> doClose(future); + } + return future; + } + + private void doClose(CompletableFuture future) { + if (autoCloseOutputStream) { + try { + outputStream.close(); + } catch (IOException e) { + future.completeExceptionally(new IllegalStateException("failed to close outputStream.", e)); + return; + } + } + + future.complete(null); + } +} 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 new file mode 100644 index 00000000000..ae4b5bac727 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpCommon.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.foundation.common.io.AsyncCloseable; + +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.ReadStream; +import io.vertx.core.streams.WriteStream; +import io.vertx.core.streams.impl.PipeImpl; + +public class PumpCommon { + /** + * @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(ReadStream readStream, WriteStream writeStream, + Handler throwableHandler) { + CompletableFuture readFuture = new CompletableFuture<>(); + + writeStream.exceptionHandler(e -> { + // consumer -> producer + // 3rd consumer -> edge -> producer + // when download not finished, consumer stop download + // producer should stop download logic at once + if (readStream instanceof InputStreamToReadStream) { + ((InputStreamToReadStream) readStream).handleException(e); + } else if (readStream instanceof HttpClientResponse) { + // can not find a way to cancel/terminate request + // so can only close the connection. + ((HttpClientResponse) readStream).request().connection().close(); + } + if (throwableHandler != null) { + throwableHandler.handle(e); + } + readFuture.completeExceptionally(e); + }); + readStream.exceptionHandler(readFuture::completeExceptionally); + + Future pipeResult = new PipeImpl<>(readStream).endOnComplete(false).to(writeStream); + + pipeResult.onComplete((s) -> readFuture.complete(null), + (f) -> { + if (throwableHandler != null) { + throwableHandler.handle(f); + } + readFuture.completeExceptionally(f); + }); + + if (!(writeStream instanceof AsyncCloseable)) { + return readFuture; + } + + return closeWriteStream((AsyncCloseable) writeStream, readFuture); + } + + protected CompletableFuture closeWriteStream(AsyncCloseable writeStream, + CompletableFuture readFuture) { + CompletableFuture writeFuture = new CompletableFuture<>(); + readFuture.whenComplete((v, e) -> + writeStream.close().whenComplete((wv, we) -> { + if (we != null) { + writeFuture.completeExceptionally(we); + return; + } + + writeFuture.complete(null); + }) + ); + + return CompletableFuture.allOf(readFuture, writeFuture); + } +} 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 new file mode 100644 index 00000000000..2c5284e12dc --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.CompletableFuture; + +import jakarta.servlet.http.Part; + +import org.apache.commons.io.IOUtils; +import org.apache.servicecomb.foundation.vertx.http.DownloadUtils; +import org.apache.servicecomb.foundation.vertx.http.ReadStreamPart; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.Context; +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.streams.ReadStream; +import io.vertx.core.streams.WriteStream; + +public class PumpFromPart { + private static final Logger LOGGER = LoggerFactory.getLogger(PumpFromPart.class); + + private final Context context; + + private final Part part; + + public PumpFromPart(Context context, Part part) { + this.context = context; + this.part = part; + } + + private CompletableFuture> prepareReadStream() { + CompletableFuture> future = new CompletableFuture<>(); + + if (ReadStreamPart.class.isInstance(part)) { + future.complete(((ReadStreamPart) part).getReadStream()); + return future; + } + + try { + InputStream inputStream = part.getInputStream(); + InputStreamToReadStream inputStreamToReadStream = new InputStreamToReadStream(context, inputStream, true); + inputStreamToReadStream.pause(); + future.complete(inputStreamToReadStream); + } catch (IOException e) { + future.completeExceptionally(e); + } + + return future; + } + + public CompletableFuture toWriteStream(WriteStream writeStream, Handler throwableHandler) { + return prepareReadStream() + .thenCompose(readStream -> new PumpCommon().pump(readStream, writeStream, throwableHandler)) + .whenComplete((v, e) -> { + if (e != null) { + LOGGER.error("to write stream failed.", e); + } + DownloadUtils.clearPartResource(part); + // PumpImpl will add drainHandler to writeStream, + // in order to support write multiple files to same writeStream, + // need reset after one stream is successful. + writeStream.drainHandler(null); + }); + } + + public CompletableFuture toOutputStream(OutputStream outputStream, boolean autoCloseOutputStream) { + if (context == null) { + return toOutputStreamSync(outputStream, autoCloseOutputStream); + } + + return toOutputStreamAsync(outputStream, autoCloseOutputStream); + } + + private CompletableFuture toOutputStreamAsync(OutputStream outputStream, boolean autoCloseOutputStream) { + OutputStreamToWriteStream outputStreamToWriteStream = new OutputStreamToWriteStream(context, outputStream, + autoCloseOutputStream); + return toWriteStream(outputStreamToWriteStream, null); + } + + // DO NOT use a mocked sync context to unify the pump logic + // otherwise when pump big stream, will cause stack overflow + private CompletableFuture toOutputStreamSync(OutputStream outputStream, boolean autoCloseOutputStream) { + CompletableFuture future = new CompletableFuture<>(); + future.whenComplete((v, e) -> DownloadUtils.clearPartResource(part)); + + try (InputStream inputStream = part.getInputStream()) { + IOUtils.copyLarge(inputStream, outputStream); + } catch (Throwable e) { + future.completeExceptionally(e); + } + + if (autoCloseOutputStream) { + try { + outputStream.close(); + } catch (Throwable e) { + future.completeExceptionally(e); + } + } + + future.complete(null); + return future; + } +} 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 new file mode 100644 index 00000000000..fcc22bfd7c9 --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConnection.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.tcp; + +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicLong; + +import io.vertx.core.Context; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.net.NetSocket; +import io.vertx.core.net.impl.NetSocketImpl; + +public class TcpConnection { + protected String protocol; + + // 压缩算法名字 + protected String zipName; + + protected NetSocket netSocket; + + // context of netSocket + protected Context context; + + // vertx's wrap for netty is ugly: write always lock first + // so we save buf in a CAS queue, and notify eventloop thread to do the real write + // + // netty: + // if write caller is not in network IO thread + // always wrap the write as a task, and put it to IO thread queue(MPSC queue) + // + // so this optimization: + // 1.avoid vertx's lock + // 2.reduce netty's task schedule + private final Queue writeQueue = new ConcurrentLinkedQueue<>(); + + private final AtomicLong writeQueueSize = new AtomicLong(); + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getZipName() { + return zipName; + } + + public void setZipName(String zipName) { + this.zipName = zipName; + } + + public void setContext(Context context) { + this.context = context; + } + + public NetSocket getNetSocket() { + return netSocket; + } + + public void initNetSocket(NetSocketImpl netSocket) { + this.netSocket = netSocket; + + this.context = netSocket.context(); + } + + public void write(Buffer buf) { + writeQueue.add(buf); + long oldSize = writeQueueSize.getAndIncrement(); + if (oldSize == 0) { + scheduleWrite(); + } + } + + // notify context thread to write + protected void scheduleWrite() { + context.runOnContext(v -> writeInContext()); + } + + protected void writeInContext() { + for (; ; ) { + Buffer buf = writeQueue.poll(); + if (buf == null) { + break; + } + + writeQueueSize.decrementAndGet(); + + netSocket.write(buf); + } + } +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConst.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConst.java new file mode 100644 index 00000000000..fe91773a49b --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConst.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.foundation.vertx.tcp; + +public final class TcpConst { + private TcpConst() { + } + + public static final String LOGIN = "login"; +} diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpOutputStream.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpOutputStream.java new file mode 100644 index 00000000000..8efcef3b6ce --- /dev/null +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpOutputStream.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.foundation.vertx.tcp; + +import org.apache.servicecomb.foundation.vertx.server.TcpParser; +import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; + +/** + * TcpOutputStream + * + * + */ +public class TcpOutputStream extends BufferOutputStream { + private final long msgId; + + public TcpOutputStream(long msgId) { + super(); + + this.msgId = msgId; + write(TcpParser.TCP_MAGIC); + writeLong(msgId); + } + + public long getMsgId() { + return msgId; + } + + public void writeLength(int totalLen, int headerLen) { + writeInt(totalLen); + writeInt(headerLen); + } +} 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 51c391cda88..00000000000 --- a/foundations/foundation-vertx/src/main/resources/META-INF/spring/cse.bean.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestClient.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestClient.java deleted file mode 100644 index 7b1b3f0e6f0..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestClient.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.foundation.vertx.client.http.HttpClientVerticle; -import io.servicecomb.foundation.vertx.client.http.HttpClientWithContext; -import io.vertx.core.Vertx; - -public class TestClient { - - @Test - public void testHttpClientVerticle() throws Exception { - Vertx vertx = VertxUtils.init(null); - - HttpClientVerticle oVerticle = new HttpClientVerticle(); - oVerticle.init(vertx, null); - Assert.assertEquals("clientMgr", HttpClientVerticle.CLIENT_MGR); - Assert.assertEquals("poolCount", HttpClientVerticle.POOL_COUNT); - Assert.assertEquals("clientOptions", HttpClientVerticle.CLIENT_OPTIONS); - HttpClientWithContext oContextClient = new HttpClientWithContext(null, null); - Assert.assertEquals(null, oContextClient.getHttpClient()); - - vertx.close(); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestSimpleBodyHandler.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestSimpleBodyHandler.java deleted file mode 100644 index 968cbbd307c..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestSimpleBodyHandler.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx; - -import javax.ws.rs.core.Response.Status; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.vertx.core.http.HttpServerRequest; -import io.vertx.core.http.HttpServerResponse; -import io.vertx.ext.web.RoutingContext; - -public class TestSimpleBodyHandler { - - private SimpleBodyHandler instance; - - private RoutingContext context; - - @Before - public void setUp() throws Exception { - context = Mockito.mock(RoutingContext.class); - HttpServerRequest request = Mockito.mock(HttpServerRequest.class); - Mockito.when(context.request()).thenReturn(request); - HttpServerResponse response = Mockito.mock(HttpServerResponse.class); - Mockito.when(response.setStatusCode(Status.UNSUPPORTED_MEDIA_TYPE.getStatusCode())).thenReturn(response); - Mockito.when(context.response()).thenReturn(response); - } - - @After - public void tearDown() throws Exception { - instance = null; - context = null; - } - - @Test - public void testValidContentType() { - instance = new SimpleBodyHandler() { - @Override - protected boolean contentTypeSupported(String contentType) { - return true; - } - }; - instance.handle(context); - Assert.assertTrue(instance.checkContentType(context)); - } - - @Test - public void testInvalidContentType() { - instance = new SimpleBodyHandler() { - @Override - protected boolean contentTypeSupported(String contentType) { - return false; - } - }; - instance.handle(context); - Assert.assertFalse(instance.checkContentType(context)); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestSimpleJsonObject.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestSimpleJsonObject.java deleted file mode 100644 index 2c5ad9aed38..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestSimpleJsonObject.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.foundation.vertx; - -import org.junit.Assert; -import org.junit.Test; - -public class TestSimpleJsonObject { - @Test - public void test() { - Object value = new Object(); - - SimpleJsonObject json = new SimpleJsonObject(); - json.put("k", value); - Assert.assertEquals(value, json.getValue("k")); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestStream.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestStream.java deleted file mode 100644 index d51c3a45071..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestStream.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx; - -import org.junit.Assert; -import org.junit.Test; - -import io.netty.buffer.ByteBuf; -import io.servicecomb.foundation.vertx.stream.BufferInputStream; -import io.servicecomb.foundation.vertx.stream.BufferOutputStream; -import io.vertx.core.buffer.Buffer; - -public class TestStream { - - private static final int DIRECT_BUFFER_SIZE = 1024; - - @Test - public void testBufferInputStream() { - ByteBuf obuf = Buffer.buffer(DIRECT_BUFFER_SIZE).getByteBuf(); - obuf.writeBytes(("testss").getBytes()); - @SuppressWarnings("resource") - BufferInputStream oBufferInputStream = new BufferInputStream(obuf); - Assert.assertNotEquals(1234, oBufferInputStream.skip(0)); - Assert.assertNotEquals(obuf.readByte(), oBufferInputStream.readByte()); - Assert.assertEquals(obuf.readByte() + 1, oBufferInputStream.read()); - Assert.assertEquals(obuf.readBoolean(), oBufferInputStream.readBoolean()); - Assert.assertEquals(obuf.readerIndex(), oBufferInputStream.getIndex()); - Assert.assertEquals(obuf.readableBytes(), oBufferInputStream.available()); - Assert.assertNotEquals(null, oBufferInputStream.read(("test").getBytes())); - } - - @Test - public void testBufferOutputStream() { - @SuppressWarnings({"resource"}) - BufferOutputStream oBufferOutputStream = new BufferOutputStream(); - oBufferOutputStream.writeString("test"); - Assert.assertNotEquals(null, oBufferOutputStream.writerIndex()); - oBufferOutputStream.write(1); - oBufferOutputStream.write(true); - Assert.assertEquals(true, (1 < oBufferOutputStream.length())); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestTcp.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestTcp.java deleted file mode 100644 index 09d78c150a8..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestTcp.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; -import io.servicecomb.foundation.vertx.client.tcp.TcpClientConnection; -import io.servicecomb.foundation.vertx.client.tcp.TcpClientConnectionPool; -import io.servicecomb.foundation.vertx.client.tcp.TcpClientPackage; -import io.servicecomb.foundation.vertx.client.tcp.TcpClientVerticle; -import io.servicecomb.foundation.vertx.client.tcp.TcpData; -import io.servicecomb.foundation.vertx.client.tcp.TcpRequest; -import io.servicecomb.foundation.vertx.client.tcp.TcpResonseCallback; -import io.servicecomb.foundation.vertx.server.TcpParser; -import io.servicecomb.foundation.vertx.server.TcpServer; -import io.servicecomb.foundation.vertx.tcp.TcpOutputStream; -import io.vertx.core.AsyncResult; -import io.vertx.core.Context; -import io.vertx.core.Handler; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.net.NetClient; -import io.vertx.core.net.NetSocket; -import io.vertx.core.net.impl.NetSocketImpl; -import io.vertx.core.net.impl.SocketAddressImpl; -import mockit.Deencapsulation; -import mockit.Mock; -import mockit.MockUp; - -public class TestTcp { - - enum ParseStatus { - MSG_ID_AND_LEN, - // len: total len/header len - HEADER, - BODY - } - - @Test - public void testTcpClient() throws Exception { - NetClient oNetClient = new NetClient() { - - @Override - public boolean isMetricsEnabled() { - return true; - } - - @Override - public NetClient connect(int port, String host, Handler> connectHandler) { - return Mockito.mock(NetClient.class); - } - - @Override - public void close() { - } - }; - TcpClientConnection oTcpClient = - new TcpClientConnection(Mockito.mock(Context.class), oNetClient, "highway://127.2.0.1:8080", - new TcpClientConfig()); - oTcpClient.checkTimeout(); - oTcpClient.send(new TcpClientPackage(null), 123, Mockito.mock(TcpResonseCallback.class)); - oTcpClient.send(new TcpClientPackage(null), 123, Mockito.mock(TcpResonseCallback.class)); - - new MockUp() { - @Mock - protected void startCheckTimeout(TcpClientConfig clientConfig, Context context) { - } - }; - Vertx vertx = VertxUtils.init(null); - TcpClientConfig config = new TcpClientConfig(); - TcpClientConnectionPool oClientPool = - new TcpClientConnectionPool(config, vertx.getOrCreateContext(), oNetClient); - oClientPool.send(oTcpClient, new TcpClientPackage(null), Mockito.mock(TcpResonseCallback.class)); - oClientPool.send(oTcpClient, new TcpClientPackage(null), Mockito.mock(TcpResonseCallback.class)); - Assert.assertNotNull(oClientPool); - - TcpRequest oTcpRequest = new TcpRequest(1234, Mockito.mock(TcpResonseCallback.class)); - oTcpRequest.isTimeout(); - oTcpRequest.onReply(Buffer.buffer(), Buffer.buffer(("test").getBytes())); - oTcpRequest.onSendError(new Throwable("test Errorsss")); - Assert.assertNotNull(oTcpRequest); - - TcpClientVerticle oTcpClientVerticle = new TcpClientVerticle(); - oTcpClientVerticle.init(vertx, vertx.getOrCreateContext()); - oTcpClientVerticle.createClientPool(); - oTcpClientVerticle.createClientPool(); - Assert.assertNotNull(oTcpClientVerticle.getVertx()); - - NetSocket socket = Mockito.mock(NetSocketImpl.class); - Throwable e = Mockito.mock(Throwable.class); - Buffer hBuffer = Mockito.mock(Buffer.class); - Buffer bBuffer = Mockito.mock(Buffer.class); - - Deencapsulation.invoke(oTcpClient, "connect"); - Deencapsulation.invoke(oTcpClient, "onConnectSuccess", socket); - Mockito.when(socket.localAddress()).thenReturn(new SocketAddressImpl(0, "127.0.0.1")); - Deencapsulation.setField(oTcpClient, "netSocket", socket); - Deencapsulation.invoke(oTcpClient, "onDisconnected", e); - Deencapsulation.invoke(oTcpClient, "tryLogin"); - Deencapsulation.invoke(oTcpClient, "onLoginSuccess"); - Deencapsulation.invoke(oTcpClient, "onConnectFailed", e); - long l = 10; - Deencapsulation.invoke(oTcpClient, "onReply", l, hBuffer, bBuffer); - oTcpClient.checkTimeout(); - Assert.assertNotNull(oTcpClient); - - vertx.close(); - } - - @Test - public void testTcpOutputStream() { - TcpOutputStream oStream = new TcpOutputStream(0); - oStream.close(); - Buffer buffer = oStream.getBuffer(); - Assert.assertArrayEquals(TcpParser.TCP_MAGIC, buffer.getBytes(0, TcpParser.TCP_MAGIC.length)); - Assert.assertEquals(oStream.getMsgId(), buffer.getLong(TcpParser.TCP_MAGIC.length)); - } - - @Test - public void testTcpServerStarter() { - Vertx vertx = VertxUtils.init(null); - - URIEndpointObject endpiont = new URIEndpointObject("highway://127.0.0.1:9900"); - TcpServer oStarter = new TcpServer(endpiont); - oStarter.init(vertx, "", null); - Assert.assertNotNull(oStarter); - //TODO Need to find a way to Assert TcpServerStarter as this obbject does not return any values. - - vertx.close(); - } - - @Test - public void testTcpClientConfig() { - TcpClientConfig tcpClientConfig = new TcpClientConfig(); - tcpClientConfig.getRequestTimeoutMillis(); - tcpClientConfig.setRequestTimeoutMillis(1); - Assert.assertNotNull(tcpClientConfig); - } - - @Test - public void testTcpData() { - Buffer hBuffer = Mockito.mock(Buffer.class); - Buffer bBuffer = Mockito.mock(Buffer.class); - TcpData tcpData = new TcpData(hBuffer, bBuffer); - tcpData.getBodyBuffer(); - tcpData.setBodyBuffer(bBuffer); - tcpData.getHeaderBuffer(); - tcpData.setBodyBuffer(hBuffer); - Assert.assertNotNull(tcpData.getBodyBuffer()); - Assert.assertNotNull(tcpData.getHeaderBuffer()); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestVertex.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestVertex.java deleted file mode 100644 index d14eeab930b..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestVertex.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx; - -import java.util.concurrent.CountDownLatch; - -import javax.xml.ws.Holder; - -import org.junit.Assert; -import org.junit.Test; - -import io.vertx.core.Vertx; -import io.vertx.core.VertxOptions; -import io.vertx.core.json.JsonObject; - -public class TestVertex { - @Test - public void testGetOrCreateVertx() throws InterruptedException { - Vertx vertx = VertxUtils.getOrCreateVertxByName("ut", null); - - Holder name = new Holder<>(); - CountDownLatch latch = new CountDownLatch(1); - vertx.runOnContext(v -> { - name.value = Thread.currentThread().getName(); - latch.countDown(); - }); - latch.await(); - - Assert.assertEquals(name.value, "ut-vert.x-eventloop-thread-0"); - vertx.close(); - } - - @Test - public void testVertxUtilsInitNullOptions() { - Vertx vertx = VertxUtils.init(null); - Assert.assertNotEquals(null, vertx); - vertx.close(); - } - - @Test - public void testVertxUtilsInitWithOptions() { - VertxOptions oOptions = new VertxOptions(); - oOptions.setClustered(false); - - Vertx vertx = VertxUtils.init(oOptions); - Assert.assertNotEquals(null, vertx); - vertx.close(); - } - - @Test - public void testSimpleJsonObject() { - SimpleJsonObject oObject = new SimpleJsonObject(); - JsonObject oJsonObject = oObject.put("testKey", oObject); - Assert.assertEquals(true, oJsonObject.containsKey("testKey")); - JsonObject oCopyObject = oObject.copy(); - Assert.assertEquals(true, oCopyObject.containsKey("testKey")); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestVertxTLSBuilder.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestVertxTLSBuilder.java deleted file mode 100644 index f20fc19be27..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/TestVertxTLSBuilder.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.foundation.ssl.SSLCustom; -import io.servicecomb.foundation.ssl.SSLOption; -import io.vertx.core.http.ClientAuth; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.http.HttpServerOptions; -import mockit.Mock; -import mockit.MockUp; - -public class TestVertxTLSBuilder { - - @Test - public void testbuildHttpServerOptions() { - SSLOption option = SSLOption.buildFromYaml("rest.provider"); - SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); - HttpServerOptions serverOptions = new HttpServerOptions(); - VertxTLSBuilder.buildNetServerOptions(option, custom, serverOptions); - Assert.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); - Assert.assertEquals(serverOptions.getClientAuth(), ClientAuth.REQUEST); - } - - @Test - public void testbuildHttpClientOptions() { - SSLOption option = SSLOption.buildFromYaml("rest.consumer"); - SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); - HttpClientOptions serverOptions = new HttpClientOptions(); - VertxTLSBuilder.buildHttpClientOptions(option, custom, serverOptions); - Assert.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); - Assert.assertEquals(serverOptions.isTrustAll(), true); - } - - @Test - public void testbuildClientOptionsBase() { - SSLOption option = SSLOption.buildFromYaml("rest.consumer"); - SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); - HttpClientOptions serverOptions = new HttpClientOptions(); - VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); - Assert.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); - Assert.assertEquals(serverOptions.isTrustAll(), true); - } - - @Test - public void testbuildClientOptionsBaseFileNull() { - SSLOption option = SSLOption.buildFromYaml("rest.consumer"); - option.setKeyStore(null); - option.setTrustStore(null); - option.setCrl(null); - SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); - HttpClientOptions serverOptions = new HttpClientOptions(); - VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); - Assert.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); - Assert.assertEquals(serverOptions.isTrustAll(), true); - } - - @Test - public void testbuildClientOptionsBaseAuthPeerFalse() { - SSLOption option = SSLOption.buildFromYaml("rest.consumer"); - SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); - HttpClientOptions serverOptions = new HttpClientOptions(); - new MockUp() { - - @Mock - public boolean isAuthPeer() { - return false; - } - }; - VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); - Assert.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); - Assert.assertEquals(serverOptions.isTrustAll(), true); - } - - @Test - public void testbuildClientOptionsBaseSTORE_JKS() { - SSLOption option = SSLOption.buildFromYaml("rest.consumer"); - SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); - HttpClientOptions serverOptions = new HttpClientOptions(); - new MockUp() { - - @Mock - public String getKeyStoreType() { - return "JKS"; - } - }; - VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); - Assert.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); - Assert.assertEquals(serverOptions.isTrustAll(), true); - } - - @Test - public void testbuildClientOptionsBaseSTORE_PKCS12() { - SSLOption option = SSLOption.buildFromYaml("rest.consumer"); - SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); - HttpClientOptions serverOptions = new HttpClientOptions(); - new MockUp() { - - @Mock - public String getTrustStoreType() { - return "PKCS12"; - } - }; - VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); - Assert.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); - Assert.assertEquals(serverOptions.isTrustAll(), true); - } - - @Test - public void testbuildHttpServerOptionsRequest() { - SSLOption option = SSLOption.buildFromYaml("rest.provider"); - SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); - HttpServerOptions serverOptions = new HttpServerOptions(); - - new MockUp() { - - @Mock - public boolean isAuthPeer() { - return false; - } - }; - VertxTLSBuilder.buildNetServerOptions(option, custom, serverOptions); - Assert.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); - Assert.assertEquals(serverOptions.getClientAuth(), ClientAuth.REQUEST); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientTest.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientTest.java deleted file mode 100644 index 3f8368fb3d9..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TcpClientTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.tcp; - -import java.net.InetSocketAddress; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mockito; - -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.foundation.common.net.NetUtils; -import io.vertx.core.Context; -import io.vertx.core.net.NetClient; -import mockit.Mock; -import mockit.MockUp; - -public class TcpClientTest { - - private TcpClientConnection instance = null; - - @InjectMocks - private NetUtils netUtils; - - private void mockTestCases() { - - new MockUp() { - @Mock - public IpPort parseIpPort(String address) { - return Mockito.mock(IpPort.class); - } - }; - } - - @Before - public void setUp() throws Exception { - Context context = Mockito.mock(Context.class); - NetClient netClient = Mockito.mock(NetClient.class); - InetSocketAddress socketAddress = Mockito.mock(InetSocketAddress.class); - mockTestCases(); - Mockito.when(NetUtils.parseIpPort("sss").getSocketAddress()).thenReturn(socketAddress); - instance = new TcpClientConnection(context, netClient, "highway://127.0.0.1:80", new TcpClientConfig()); - } - - @After - public void tearDown() throws Exception { - instance = null; - } - - @Test - public void testCallBack() { - instance.getClass(); - TcpResonseCallback callback = Mockito.mock(TcpResonseCallback.class); - try { - instance.send(new TcpClientPackage(null), 1, callback); - Assert.assertNotNull(callback); - } catch (Exception e) { - Assert.assertEquals("java.lang.NullPointerException", e.getClass().getName()); - } - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TestClientPoolManager.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TestClientPoolManager.java deleted file mode 100644 index 75db8c914bf..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TestClientPoolManager.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.tcp; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.foundation.vertx.client.ClientPoolManager; -import io.servicecomb.foundation.vertx.client.NetThreadData; -import io.servicecomb.foundation.vertx.client.http.HttpClientWithContext; - -public class TestClientPoolManager { - private ClientPoolManager instance; - - @Before - public void setUp() throws Exception { - instance = new ClientPoolManager<>(); - } - - @Test - public void testAddNetThread() { - @SuppressWarnings("unchecked") - NetThreadData netThread = Mockito.mock(NetThreadData.class); - instance.addNetThread(netThread); - HttpClientWithContext context = Mockito.mock(HttpClientWithContext.class); - Mockito.when(netThread.selectClientPool()).thenReturn(context); - HttpClientWithContext netThreadValue = instance.findThreadBindClientPool(); - Assert.assertNotNull(netThreadValue); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TestNetThreadData.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TestNetThreadData.java deleted file mode 100644 index 0c045b3ac28..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TestNetThreadData.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.tcp; - -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.foundation.vertx.client.ClientPoolFactory; -import io.servicecomb.foundation.vertx.client.NetThreadData; -import io.servicecomb.foundation.vertx.client.http.HttpClientWithContext; - -/** - * - * - */ -public class TestNetThreadData { - - private NetThreadData instance; - - @Before - public void setUp() throws Exception { - @SuppressWarnings("unchecked") - ClientPoolFactory factory = Mockito.mock(ClientPoolFactory.class); - instance = new NetThreadData<>(factory, 1); - Assert.assertNotNull(instance); - } - - @After - public void tearDown() throws Exception { - instance = null; - } - - @Test - public void testGetFactory() { - ClientPoolFactory factory = instance.getFactory(); - Assert.assertNotNull(factory); - } - - @Test - public void testGetPools() { - instance.getPools(); - Assert.assertNotNull(instance.getPools()); - } - - @Test - public void testGetBindIndex() { - AtomicInteger count = instance.getBindIndex(); - Assert.assertNotNull(count); - } - - @Test - public void testSelectClientPool() { - Assert.assertNull(instance.selectClientPool()); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TestTcpClientConfig.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TestTcpClientConfig.java deleted file mode 100644 index bf816f5544f..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TestTcpClientConfig.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.tcp; - -import org.junit.Assert; -import org.junit.Test; - -public class TestTcpClientConfig { - @Test - public void testTcpClientConfig() { - TcpClientConfig config = new TcpClientConfig(); - Assert.assertEquals(config.getRequestTimeoutMillis(), 30000); - Assert.assertEquals(config.isSsl(), false); - config.setRequestTimeoutMillis(500); - Assert.assertEquals(config.getRequestTimeoutMillis(), 500); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TestTcpClientVerticle.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TestTcpClientVerticle.java deleted file mode 100644 index c369f3116a7..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/client/tcp/TestTcpClientVerticle.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.client.tcp; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.foundation.vertx.client.ClientPoolManager; -import io.vertx.core.Context; -import io.vertx.core.impl.VertxInternal; -import io.vertx.core.json.JsonObject; -import mockit.Expectations; -import mockit.Mocked; - -public class TestTcpClientVerticle { - private ClientPoolManager clientMgr = new ClientPoolManager<>(); - - @Test - public void testTcpClientVerticle(@Mocked TcpClientConfig config, @Mocked VertxInternal vertx, - @Mocked Context context, - @Mocked JsonObject json) throws Exception { - - new Expectations() { - { - context.config(); - result = json; - json.getValue("clientOptions"); - result = config; - json.getValue("clientMgr"); - result = clientMgr; - } - }; - - TcpClientVerticle client = new TcpClientVerticle(); - client.init(vertx, context); - client.start(); - TcpClientConnectionPool pool = client.createClientPool(); - Assert.assertNotNull(pool); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/server/TestTcpParser.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/server/TestTcpParser.java deleted file mode 100644 index 4cfc37c0878..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/server/TestTcpParser.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.foundation.vertx.server; - -import java.io.UnsupportedEncodingException; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.foundation.vertx.tcp.TcpOutputStream; -import io.vertx.core.buffer.Buffer; - -public class TestTcpParser { - long msgId; - - Buffer headerBuffer; - - Buffer bodyBuffer; - - @Test - public void test() throws UnsupportedEncodingException { - TcpBufferHandler output = new TcpBufferHandler() { - @Override - public void handle(long _msgId, Buffer _headerBuffer, Buffer _bodyBuffer) { - msgId = _msgId; - headerBuffer = _headerBuffer; - bodyBuffer = _bodyBuffer; - } - }; - - byte[] header = new byte[] {1, 2, 3}; - byte[] body = new byte[] {1, 2, 3, 4}; - TcpOutputStream os = new TcpOutputStream(1); - os.writeInt(header.length + body.length); - os.writeInt(header.length); - os.write(header); - os.write(body); - - TcpParser parser = new TcpParser(output); - parser.handle(os.getBuffer()); - os.close(); - - Assert.assertEquals(1, msgId); - Assert.assertArrayEquals(header, headerBuffer.getBytes()); - Assert.assertArrayEquals(body, bodyBuffer.getBytes()); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/server/TestTcpServer.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/server/TestTcpServer.java deleted file mode 100644 index a1038061a07..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/server/TestTcpServer.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.server; - -import java.net.InetSocketAddress; - -import org.junit.Test; - -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.foundation.vertx.AsyncResultCallback; -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 mockit.Expectations; -import mockit.Mocked; - -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); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/server/TestTcpServerConnection.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/server/TestTcpServerConnection.java deleted file mode 100644 index 2ef7e0fadbe..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/server/TestTcpServerConnection.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.foundation.vertx.server; - -import org.junit.Assert; -import org.junit.Test; - -import io.vertx.core.net.impl.NetSocketImpl; -import mockit.Mocked; - -public class TestTcpServerConnection { - @Test - public void test(@Mocked NetSocketImpl netSocket) { - TcpServerConnection connection = new TcpServerConnection(); - connection.setProtocol("p"); - connection.setZipName("z"); - - connection.init(netSocket); - - Assert.assertEquals(netSocket, connection.getNetSocket()); - } -} diff --git a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/stream/TestBufferInputStream.java b/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/stream/TestBufferInputStream.java deleted file mode 100644 index 72916c84429..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/servicecomb/foundation/vertx/stream/TestBufferInputStream.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.foundation.vertx.stream; - -import java.io.UnsupportedEncodingException; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.netty.buffer.ByteBuf; - -public class TestBufferInputStream { - - private BufferInputStream instance; - - @Before - public void setUp() throws Exception { - ByteBuf buffer = Mockito.mock(ByteBuf.class); - instance = new BufferInputStream(buffer); - } - - @After - public void tearDown() throws Exception { - instance = null; - } - - @Test - public void testRead() { - Assert.assertEquals(0, instance.read()); - } - - @Test - public void testReadByteArray() throws UnsupportedEncodingException { - byte[] b = "csr".getBytes("UTF-8"); - Assert.assertEquals(-1, instance.read(b)); - } - - @Test - public void testReadByteArrayIntInt() throws UnsupportedEncodingException { - byte[] b = "csr".getBytes("UTF-8"); - Assert.assertEquals(-1, instance.read(b, 1, 0)); - } - - @Test - public void testSkip() { - Assert.assertEquals(0, instance.skip(1)); - } - - @Test - public void testAvailable() { - Assert.assertEquals(0, instance.available()); - } - - @Test - public void testClose() { - try { - instance.close(); - } catch (Exception e) { - Assert.assertTrue(false); // This assertion is made to fail the test case in case the close() throws exception - } - } - - @Test - public void testBufferInputStream() { - Assert.assertNotNull(instance); - } - - @Test - public void testReadBoolean() { - Assert.assertEquals(false, instance.readBoolean()); - } - - @Test - public void testReadShort() { - Assert.assertEquals(0, instance.readShort()); - } - - @Test - public void testReadInt() { - Assert.assertEquals(0, instance.readInt()); - } - - @Test - public void testReadLong() { - Assert.assertEquals(0, instance.readLong()); - } - - @Test - public void testGetIndex() { - Assert.assertEquals(0, instance.getIndex()); - } - - @Test - public void testReadString() { - Assert.assertNotNull(instance.readString()); - } -} 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 new file mode 100644 index 00000000000..8e77a256251 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSharedVertxFactory.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.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(environment)); + Assertions.assertSame(SharedVertxFactory.getSharedVertx(environment), + SharedVertxFactory.getSharedVertx(environment)); + + 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 new file mode 100644 index 00000000000..8aec460f9c0 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSimpleBodyHandler.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.vertx; + +import jakarta.ws.rs.core.Response.Status; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; + +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.impl.RoutingContextInternal; + +public class TestSimpleBodyHandler { + + private SimpleBodyHandler instance; + + private RoutingContextInternal context; + + @Before + public void setUp() throws Exception { + context = Mockito.mock(RoutingContextInternal.class); + HttpServerRequest request = Mockito.mock(HttpServerRequest.class); + Mockito.when(context.request()).thenReturn(request); + MultiMap multiMap = Mockito.mock(MultiMap.class); + Mockito.when(request.headers()).thenReturn(multiMap); + HttpServerResponse response = Mockito.mock(HttpServerResponse.class); + Mockito.when(response.setStatusCode(Status.UNSUPPORTED_MEDIA_TYPE.getStatusCode())).thenReturn(response); + Mockito.when(context.response()).thenReturn(response); + } + + @After + public void tearDown() throws Exception { + instance = null; + context = null; + } + + @Test + public void testValidContentType() { + instance = new SimpleBodyHandler() { + @Override + protected boolean contentTypeSupported(String contentType) { + return true; + } + }; + instance.handle(context); + Assertions.assertTrue(instance.checkContentType(context)); + } + + @Test + public void testInvalidContentType() { + instance = new SimpleBodyHandler() { + @Override + protected boolean contentTypeSupported(String contentType) { + return false; + } + }; + instance.handle(context); + Assertions.assertFalse(instance.checkContentType(context)); + } +} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSimpleJsonObject.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSimpleJsonObject.java new file mode 100644 index 00000000000..d0a51708073 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSimpleJsonObject.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.foundation.vertx; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestSimpleJsonObject { + @Test + public void testPutObject() { + Object value = new Object(); + + SimpleJsonObject json = new SimpleJsonObject(); + json.put("k", value); + Assertions.assertSame(value, json.getValue("k")); + } + + @Test + public void testCopy() { + SimpleJsonObject json = new SimpleJsonObject(); + Assertions.assertSame(json, json.copy()); + } +} 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 new file mode 100644 index 00000000000..af3e2078c90 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestStream.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.foundation.vertx; + +import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; +import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.vertx.core.buffer.Buffer; + +public class TestStream { + + private static final int DIRECT_BUFFER_SIZE = 1024; + + @Test + public void testBufferInputStream() { + Buffer obuf = Buffer.buffer(DIRECT_BUFFER_SIZE); + obuf.appendBytes(("tests1").getBytes()); + @SuppressWarnings("resource") + BufferInputStream oBufferInputStream = new BufferInputStream(obuf); + + 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 + public void testBufferOutputStream() { + @SuppressWarnings({"resource"}) + BufferOutputStream oBufferOutputStream = new BufferOutputStream(); + oBufferOutputStream.writeString("test"); + oBufferOutputStream.write(1); + oBufferOutputStream.write(true); + Assertions.assertTrue((1 < oBufferOutputStream.length())); + + @SuppressWarnings("resource") + BufferInputStream oBufferInputStream = new BufferInputStream(oBufferOutputStream.getBuffer()); + Assertions.assertEquals("test", oBufferInputStream.readString()); + Assertions.assertEquals(1, oBufferInputStream.readByte()); + 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 new file mode 100644 index 00000000000..f99abd698df --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxTLSBuilder.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.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.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; + +public class TestVertxTLSBuilder { + Environment environment = Mockito.mock(Environment.class); + + @Before + public void setUp() { + LegacyPropertyFactory.setEnvironment(environment); + } + + @Test + public void testBuildHttpServerOptions() { + SSLOption option = SSLOption.build("rest.provider", environment); + SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); + HttpServerOptions serverOptions = new HttpServerOptions(); + VertxTLSBuilder.buildNetServerOptions(option, custom, serverOptions); + Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); + Assertions.assertEquals(serverOptions.getClientAuth(), ClientAuth.REQUEST); + } + + @Test + public void testBuildHttpClientOptions_sslKey_noFactory() { + HttpClientOptions clientOptions = new HttpClientOptions(); + VertxTLSBuilder.buildHttpClientOptions("notExist", clientOptions); + Assertions.assertTrue(clientOptions.isSsl()); + } + + public static class SSLOptionFactoryForTest implements SSLOptionFactory { + static SSLOption sslOption = new SSLOption(); + + static { + sslOption.setEngine("openssl"); + sslOption.setProtocols(""); + sslOption.setCiphers(SSLOption.DEFAULT_CIPHERS); + sslOption.setCheckCNHost(true); + } + + @Override + public SSLOption createSSLOption() { + return sslOption; + } + } + + @Test + public void testBuildHttpClientOptions_ssl_withFactory() { + Mockito.when(environment.getProperty("ssl.exist.sslOptionFactory")) + .thenReturn(SSLOptionFactoryForTest.class.getName()); + HttpClientOptions clientOptions = new HttpClientOptions(); + VertxTLSBuilder.buildHttpClientOptions("exist", clientOptions); + Assertions.assertTrue(clientOptions.isSsl()); + Assertions.assertTrue(clientOptions.isVerifyHost()); + } + + @Test + public void testBuildHttpClientOptions() { + SSLOption option = SSLOption.build("rest.consumer", environment); + SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); + HttpClientOptions serverOptions = new HttpClientOptions(); + VertxTLSBuilder.buildHttpClientOptions(option, custom, serverOptions); + Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); + Assertions.assertTrue(serverOptions.isTrustAll()); + } + + @Test + public void testBuildClientOptionsBase() { + SSLOption option = SSLOption.build("rest.consumer", environment); + SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); + HttpClientOptions serverOptions = new HttpClientOptions(); + VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); + Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); + Assertions.assertTrue(serverOptions.isTrustAll()); + } + + @Test + public void testBuildClientOptionsBaseFileNull() { + SSLOption option = SSLOption.build("rest.consumer", environment); + option.setKeyStore(null); + option.setTrustStore(null); + option.setCrl(null); + SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); + HttpClientOptions serverOptions = new HttpClientOptions(); + VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); + Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); + Assertions.assertTrue(serverOptions.isTrustAll()); + } + + @Test + public void testBuildClientOptionsBaseAuthPeerFalse() { + SSLOption option = SSLOption.build("rest.consumer", environment); + SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); + HttpClientOptions serverOptions = new HttpClientOptions(); + new MockUp() { + + @Mock + public boolean isAuthPeer() { + return false; + } + }; + VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); + Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); + Assertions.assertTrue(serverOptions.isTrustAll()); + } + + @Test + public void testBuildClientOptionsBaseSTORE_JKS() { + SSLOption option = SSLOption.build("rest.consumer", environment); + SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); + HttpClientOptions serverOptions = new HttpClientOptions(); + new MockUp() { + + @Mock + public String getKeyStoreType() { + return "JKS"; + } + }; + VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); + Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); + Assertions.assertTrue(serverOptions.isTrustAll()); + } + + @Test + public void testBuildClientOptionsBaseSTORE_PKCS12() { + SSLOption option = SSLOption.build("rest.consumer", environment); + SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); + HttpClientOptions serverOptions = new HttpClientOptions(); + new MockUp() { + + @Mock + public String getTrustStoreType() { + return "PKCS12"; + } + }; + VertxTLSBuilder.buildClientOptionsBase(option, custom, serverOptions); + Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); + Assertions.assertTrue(serverOptions.isTrustAll()); + } + + @Test + public void testBuildHttpServerOptionsRequest() { + SSLOption option = SSLOption.build("rest.provider", environment); + SSLCustom custom = SSLCustom.createSSLCustom(option.getSslCustomClass()); + HttpServerOptions serverOptions = new HttpServerOptions(); + + new MockUp() { + + @Mock + public boolean isAuthPeer() { + return false; + } + }; + VertxTLSBuilder.buildNetServerOptions(option, custom, serverOptions); + Assertions.assertEquals(serverOptions.getEnabledSecureTransportProtocols().toArray().length, 1); + Assertions.assertEquals(serverOptions.getClientAuth(), ClientAuth.REQUEST); + } +} 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 new file mode 100644 index 00000000000..9f966cbcbde --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxUtils.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.CountDownLatch; + +import org.apache.commons.io.FileUtils; +import org.apache.servicecomb.foundation.common.Holder; +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 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, null); + + Holder name = new Holder<>(); + CountDownLatch latch = new CountDownLatch(1); + vertx.runOnContext(v -> { + name.value = Thread.currentThread().getName(); + latch.countDown(); + }); + latch.await(); + + Assertions.assertEquals(name.value, "ut-vert.x-eventloop-thread-0"); + VertxUtils.blockCloseVertxByName("ut"); + } + + @Test + public void testCreateVertxWithFileCPResolving() { + // create .vertx folder + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) + .thenReturn(false); + deleteCacheFile(); + VertxUtils.getOrCreateVertxByName("testCreateVertxWithFileCPResolvingFalse", null, null); + Assertions.assertTrue(isCacheFileExists()); + VertxUtils.blockCloseVertxByName("testCreateVertxWithFileCPResolvingFalse"); + + // don't create .vertx folder + deleteCacheFile(); + Assertions.assertFalse(isCacheFileExists()); + 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"); + } + + private void deleteCacheFile() { + String cacheDirBase = System.getProperty(SysProps.FILE_CACHE_DIR.name, + System.getProperty("java.io.tmpdir", ".")); + File folder = new File(cacheDirBase); + File[] files = folder.listFiles(); + for (File f : files) { + if (f.getName().startsWith("vertx-cache")) { + FileUtils.deleteQuietly(f); + } + } + } + + private boolean isCacheFileExists() { + String cacheDirBase = System.getProperty(SysProps.FILE_CACHE_DIR.name, + System.getProperty("java.io.tmpdir", ".")); + File folder = new File(cacheDirBase); + File[] files = folder.listFiles(); + for (File f : files) { + if (f.getName().startsWith("vertx-cache")) { + return true; + } + } + return false; + } + + @Test + public void testVertxUtilsInitNullOptions() { + Vertx vertx = VertxUtils.init(null, null, null); + Assertions.assertNotEquals(null, vertx); + VertxUtils.blockCloseVertx(vertx); + } + + @Test + public void testVertxUtilsInitWithOptions() { + VertxOptions oOptions = new VertxOptions(); + + Vertx vertx = VertxUtils.init(null, oOptions, null); + Assertions.assertNotEquals(null, vertx); + VertxUtils.blockCloseVertx(vertx); + } + + @Test + public void testgetBytesFastBufferInputStream() throws IOException { + byte[] bytes = new byte[] {1}; + Buffer byteBuf = Buffer.buffer(bytes); + + try (BufferInputStream inputStream = new BufferInputStream(byteBuf)) { + byte[] result = VertxUtils.getBytesFast(inputStream); + Assertions.assertEquals(bytes.length, result.length); + Assertions.assertEquals(bytes[0], result[0]); + } + } + + @Test + public void testgetBytesFastNormalInputStream() throws IOException { + byte[] bytes = new byte[] {1}; + + try (InputStream inputStream = new ByteArrayInputStream(bytes)) { + byte[] result = VertxUtils.getBytesFast(inputStream); + Assertions.assertEquals(1, result[0]); + } + } + + @Test + public void testgetBytesFastBuffer() { + Buffer buffer = Buffer.buffer(); + buffer.appendByte((byte) 1); + + byte[] result = VertxUtils.getBytesFast(buffer); + Assertions.assertEquals(1, result[0]); + } + + @Test + public void testgetBytesFastByteBufHasArray() { + byte[] bytes = new byte[] {1}; + ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes); + + byte[] result = VertxUtils.getBytesFast(byteBuf); + Assertions.assertSame(bytes, result); + } + + @Test + public void testgetBytesFastByteBufCopy() { + ByteBuf byteBuf = Unpooled.directBuffer(); + byteBuf.writeByte(1); + Assertions.assertFalse(byteBuf.hasArray()); + + byte[] result = VertxUtils.getBytesFast(byteBuf); + Assertions.assertEquals(1, result[0]); + + byteBuf.release(); + } +} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/TestClientPoolManager.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/TestClientPoolManager.java new file mode 100644 index 00000000000..c42c3f3360e --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/TestClientPoolManager.java @@ -0,0 +1,237 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext; +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.Context; +import io.vertx.core.Vertx; +import io.vertx.core.impl.VertxImpl; +import mockit.Deencapsulation; +import mockit.Expectations; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; + +public class TestClientPoolManager { + @Mocked + Vertx vertx; + + @Mocked + ClientPoolFactory factory; + + ClientPoolManager poolMgr; + + String id; + + List pools; + + Map contextMap = new HashMap<>(); + + @Mocked + Context context; + + @Before + public void setup() { + poolMgr = new ClientPoolManager<>(vertx, factory); + id = Deencapsulation.getField(poolMgr, "id"); + pools = Deencapsulation.getField(poolMgr, "pools"); + new MockUp(context) { + @Mock + void put(Object key, Object value) { + contextMap.put(key, value); + } + + @SuppressWarnings("unchecked") + @Mock + T get(Object key) { + return (T) contextMap.get(key); + } + + @Mock + Vertx owner() { + return vertx; + } + + @Mock + boolean isEventLoopContext() { + return true; + } + }; + } + + @Test + public void createClientPool(@Mocked HttpClientWithContext pool) { + new Expectations(VertxImpl.class) { + { + factory.createClientPool(context); + result = pool; + } + }; + + Assertions.assertSame(pool, poolMgr.createClientPool(context)); + Assertions.assertSame(pool, context.get(id)); + MatcherAssert.assertThat(pools, Matchers.contains(pool)); + } + + @Test + public void findClientPool_sync(@Mocked HttpClientWithContext pool1, @Mocked HttpClientWithContext pool2) { + new Expectations(poolMgr) { + { + poolMgr.findThreadBindClientPool(); + result = pool1; + poolMgr.findByContext(null); + result = pool2; + } + }; + + Assertions.assertSame(pool1, poolMgr.findClientPool(true)); + Assertions.assertSame(pool2, poolMgr.findClientPool(false)); + } + + @Test + public void findThreadBindClientPool(@Mocked HttpClientWithContext pool1, @Mocked HttpClientWithContext pool2) { + pools.add(pool1); + pools.add(pool2); + + new MockUp() { + @Mock + long getId() { + return 0; + } + }; + + Assertions.assertSame(pool1, poolMgr.findThreadBindClientPool()); + // find again, get the same result + Assertions.assertSame(pool1, poolMgr.findThreadBindClientPool()); + + new MockUp() { + @Mock + long getId() { + return 1; + } + }; + + Assertions.assertSame(pool2, poolMgr.findThreadBindClientPool()); + // find again, get the same result + Assertions.assertSame(pool2, poolMgr.findThreadBindClientPool()); + } + + @Test + public void findByContext_reactive() { + HttpClientWithContext notMatchPool1 = new HttpClientWithContext(null, null); + HttpClientWithContext notMatchPool2 = new HttpClientWithContext(null, null); + pools.add(notMatchPool1); + pools.add(notMatchPool2); + + new Expectations() { + { + Vertx.currentContext(); + result = context; + } + }; + context.put(id, notMatchPool2); + + Assertions.assertSame(notMatchPool2, poolMgr.findByContext()); + // find again, get the same result + Assertions.assertSame(notMatchPool2, poolMgr.findByContext()); + } + + @Test + public void findByContext_wrongContext_reverse() { + HttpClientWithContext pool1 = new HttpClientWithContext(null, null); + HttpClientWithContext pool2 = new HttpClientWithContext(null, null); + pools.add(pool1); + pools.add(pool2); + + new Expectations() { + { + Vertx.currentContext(); + result = null; + } + }; + + AtomicInteger reactiveNextIndex = Deencapsulation.getField(poolMgr, "reactiveNextIndex"); + reactiveNextIndex.set(Integer.MAX_VALUE); + // each time invoke find, reactiveNextIndex will inc 1 + Assertions.assertSame(pool2, poolMgr.findByContext()); + Assertions.assertSame(pool1, poolMgr.findByContext()); + Assertions.assertSame(pool2, poolMgr.findByContext()); + Assertions.assertSame(pool1, poolMgr.findByContext()); + } + + @Test + public void findByContext_normalThread() { + HttpClientWithContext pool = new HttpClientWithContext(null, null); + pools.add(pool); + + new Expectations() { + { + Vertx.currentContext(); + result = null; + } + }; + + Assertions.assertSame(pool, poolMgr.findByContext()); + } + + @Test + public void findByContext_otherVertx(@Mocked VertxImpl otherVertx, @Mocked Context otherContext) { + HttpClientWithContext pool = new HttpClientWithContext(null, null); + pools.add(pool); + + new Expectations() { + { + Vertx.currentContext(); + result = otherContext; + otherContext.owner(); + result = otherVertx; + } + }; + + Assertions.assertSame(pool, poolMgr.findByContext()); + } + + @Test + public void findByContext_worker(@Mocked Context workerContext) { + HttpClientWithContext pool = new HttpClientWithContext(null, null); + pools.add(pool); + + new Expectations() { + { + Vertx.currentContext(); + result = workerContext; + workerContext.owner(); + result = vertx; + workerContext.isEventLoopContext(); + result = false; + } + }; + + Assertions.assertSame(pool, poolMgr.findByContext()); + } +} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/TestClientVerticle.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/TestClientVerticle.java new file mode 100644 index 00000000000..197eb9f1f34 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/TestClientVerticle.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.vertx.client; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.foundation.vertx.SimpleJsonObject; +import org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext; +import org.junit.Test; + +import io.vertx.core.Context; +import io.vertx.core.json.JsonObject; +import mockit.Expectations; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; +import org.junit.jupiter.api.Assertions; + +public class TestClientVerticle { + ClientVerticle clientVerticle = new ClientVerticle<>(); + + @Test + public void start(@Mocked Context context) throws Exception { + AtomicInteger count = new AtomicInteger(); + ClientPoolManager clientMgr = new MockUp>() { + @Mock + HttpClientWithContext createClientPool(Context context) { + count.incrementAndGet(); + return null; + } + }.getMockInstance(); + clientVerticle.init(null, context); + + JsonObject config = new SimpleJsonObject(); + config.put(ClientVerticle.CLIENT_MGR, clientMgr); + new Expectations() { + { + context.config(); + result = config; + } + }; + + clientVerticle.start(); + + Assertions.assertEquals(1, count.get()); + } +} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestAbstractTcpClientPoolFactory.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestAbstractTcpClientPoolFactory.java new file mode 100644 index 00000000000..dd01fdab11f --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestAbstractTcpClientPoolFactory.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.foundation.vertx.client.tcp; + +import org.junit.jupiter.api.Assertions; + +import io.vertx.core.Context; +import io.vertx.core.Vertx; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestAbstractTcpClientPoolFactory { + private final TcpClientConfig normalClientConfig = new TcpClientConfig(); + + private final TcpClientConfig sslClientConfig = new TcpClientConfig(); + + TcpClientPoolFactory factory = new TcpClientPoolFactory(normalClientConfig, sslClientConfig); + + @Test + public void createClientPool() { + Vertx vertx = Mockito.mock(Vertx.class); + Context context = Mockito.mock(Context.class); + Mockito.when(context.owner()).thenReturn(vertx); + TcpClientConnectionPool pool = factory.createClientPool(context); + + Assertions.assertSame(normalClientConfig, pool.netClientWrapper.getClientConfig(false)); + Assertions.assertSame(sslClientConfig, pool.netClientWrapper.getClientConfig(true)); + } +} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConfig.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConfig.java new file mode 100644 index 00000000000..b7a2f50d048 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConfig.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.foundation.vertx.client.tcp; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestTcpClientConfig { + @Test + public void testTcpClientConfig() { + TcpClientConfig config = new TcpClientConfig(); + Assertions.assertEquals(config.getMsLoginTimeout(), 30000); + Assertions.assertFalse(config.isSsl()); + config.setMsLoginTimeout(500); + Assertions.assertEquals(config.getMsLoginTimeout(), 500); + } +} 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 new file mode 100644 index 00000000000..f072d962000 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnection.java @@ -0,0 +1,257 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Map; +import java.util.Queue; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; +import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConnection.Status; +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.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; +import mockit.Expectations; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; + +public class TestTcpClientConnection { + @Mocked + Context context; + + @Mocked + NetClientWrapper netClientWrapper; + + String strEndpoint = "rest://localhost:8080"; + + TcpClientConnection tcpClientConnection; + + Map requestMap; + + Queue writeQueue; + + Queue packageQueue; + + @Before + public void setup() { + tcpClientConnection = new TcpClientConnection(context, netClientWrapper, strEndpoint); + requestMap = Deencapsulation.getField(tcpClientConnection, "requestMap"); + packageQueue = Deencapsulation.getField(tcpClientConnection, "packageQueue"); + writeQueue = Deencapsulation.getField(tcpClientConnection, "writeQueue"); + } + + @Test + public void localSupportLogin() { + Assertions.assertFalse(tcpClientConnection.isLocalSupportLogin()); + + tcpClientConnection.setLocalSupportLogin(true); + Assertions.assertTrue(tcpClientConnection.isLocalSupportLogin()); + } + + @Test + public void createLogin() { + Assertions.assertNull(tcpClientConnection.createLogin()); + } + + @Test + public void onLoginResponse_buffer() { + Assertions.assertTrue(tcpClientConnection.onLoginResponse(null)); + } + + @Test + public void send_inWorkingStatus(@Mocked AbstractTcpClientPackage tcpClientPackage, + @Mocked TcpOutputStream tcpOutputStream) { + Deencapsulation.setField(tcpClientConnection, "status", Status.WORKING); + + long msgId = 1; + Buffer byteBuf = Buffer.buffer(); + new Expectations(tcpClientConnection) { + { + tcpClientPackage.getMsgId(); + result = msgId; + tcpClientPackage.createStream(); + result = tcpOutputStream; + tcpOutputStream.getBuffer(); + result = byteBuf; + } + }; + + new MockUp(context) { + @Mock + void runOnContext(Handler action) { + } + }; + tcpClientConnection.send(tcpClientPackage, ar -> { + }); + + Assertions.assertSame(byteBuf, writeQueue.poll()); + Assertions.assertNull(writeQueue.poll()); + Assertions.assertEquals(Status.WORKING, Deencapsulation.getField(tcpClientConnection, "status")); + } + + @Test + public void send_inDisconnectedStatus(@Mocked AbstractTcpClientPackage tcpClientPackage, + @Mocked TcpOutputStream tcpOutputStream) { + long msgId = 1; + new Expectations(tcpClientConnection) { + { + tcpClientPackage.getMsgId(); + result = msgId; + } + }; + new MockUp(context) { + @Mock + void runOnContext(Handler action) { + action.handle(null); + } + }; + tcpClientConnection.send(tcpClientPackage, ar -> { + }); + + Assertions.assertSame(tcpClientPackage, packageQueue.poll()); + Assertions.assertNull(packageQueue.poll()); + Assertions.assertEquals(Status.CONNECTING, Deencapsulation.getField(tcpClientConnection, "status")); + } + + @Test + public void send_disconnectedToTryLogin(@Mocked AbstractTcpClientPackage tcpClientPackage, + @Mocked TcpOutputStream tcpOutputStream) { + long msgId = 1; + new Expectations(tcpClientConnection) { + { + tcpClientPackage.getMsgId(); + result = msgId; + } + }; + new MockUp(context) { + @Mock + void runOnContext(Handler action) { + Deencapsulation.setField(tcpClientConnection, "status", Status.TRY_LOGIN); + action.handle(null); + } + }; + tcpClientConnection.send(tcpClientPackage, ar -> { + }); + + Assertions.assertSame(tcpClientPackage, packageQueue.poll()); + Assertions.assertNull(packageQueue.poll()); + Assertions.assertEquals(Status.TRY_LOGIN, Deencapsulation.getField(tcpClientConnection, "status")); + } + + @Test + public void send_disconnectedToWorking(@Mocked AbstractTcpClientPackage tcpClientPackage, + @Mocked TcpOutputStream tcpOutputStream) { + long msgId = 1; + new Expectations(tcpClientConnection) { + { + tcpClientPackage.getMsgId(); + result = msgId; + tcpClientConnection.write((Buffer) any); + } + }; + new MockUp(context) { + @Mock + void runOnContext(Handler action) { + Deencapsulation.setField(tcpClientConnection, "status", Status.WORKING); + action.handle(null); + } + }; + tcpClientConnection.send(tcpClientPackage, ar -> { + }); + + Assertions.assertNull(writeQueue.poll()); + Assertions.assertNull(packageQueue.poll()); + Assertions.assertEquals(Status.WORKING, Deencapsulation.getField(tcpClientConnection, "status")); + } + + @Test + public void connect_success(@Mocked NetSocketImpl netSocket) { + Promise promise = Promise.promise(); + new MockUp(netClientWrapper) { + @Mock + public Future connect(boolean ssl, int port, String host) { + promise.complete(netSocket); + return promise.future(); + } + }; + + tcpClientConnection.connect(); + + Assertions.assertSame(netSocket, tcpClientConnection.getNetSocket()); + Assertions.assertEquals(Status.WORKING, Deencapsulation.getField(tcpClientConnection, "status")); + } + + @Test + public void connect_failed() { + requestMap.put(10L, new TcpRequest(10, ar -> { + })); + + Promise promise = Promise.promise(); + RuntimeException error = new RuntimeExceptionWithoutStackTrace(); + new MockUp(netClientWrapper) { + @Mock + public Future connect(boolean ssl, int port, String host) { + promise.fail(error); + return promise.future(); + } + }; + + tcpClientConnection.connect(); + + Assertions.assertEquals(Status.DISCONNECTED, Deencapsulation.getField(tcpClientConnection, "status")); + Assertions.assertEquals(0, requestMap.size()); + } + + @Test + public void onClosed(@Mocked NetSocketImpl netSocket) { + requestMap.put(10L, new TcpRequest(10, ar -> { + })); + tcpClientConnection.initNetSocket(netSocket); + + tcpClientConnection.onClosed(null); + Assertions.assertEquals(Status.DISCONNECTED, Deencapsulation.getField(tcpClientConnection, "status")); + Assertions.assertEquals(0, requestMap.size()); + } + + @Test + public void onReply_notExist() { + // should not throw exception + tcpClientConnection.onReply(1, null, null); + } + + @Test + public void on_exist() { + long msgId = 1L; + AtomicInteger count = new AtomicInteger(); + requestMap.put(msgId, new TcpRequest(10, ar -> count.incrementAndGet())); + + tcpClientConnection.onReply(msgId, null, null); + Assertions.assertEquals(1, count.get()); + } +} 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 new file mode 100644 index 00000000000..de7c8dfa1db --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletRequest.java @@ -0,0 +1,429 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.Collections; + +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() { + }; + + @Test + public void testAttribute() { + String key = "a1"; + String value = "abc"; + request.setAttribute(key, value); + Assertions.assertSame(value, request.getAttribute(key)); + MatcherAssert.assertThat(Collections.list(request.getAttributeNames()), Matchers.contains(key)); + + request.setAttribute("a2", "v"); + MatcherAssert.assertThat(Collections.list(request.getAttributeNames()), Matchers.containsInAnyOrder(key, "a2")); + + request.removeAttribute(key); + Assertions.assertNull(request.getAttribute(key)); + } + + private void checkError(Error error) { + Assertions.assertEquals("not supported method", error.getMessage()); + } + + @Test + public void testGetCharacterEncoding() { + Error error = Assertions.assertThrows(Error.class, () -> request.getCharacterEncoding()); + checkError(error); + } + + @Test + public void testSetCharacterEncoding() { + Error error = Assertions.assertThrows(Error.class, () -> request.setCharacterEncoding("")); + checkError(error); + } + + @Test + public void testGetContentLength() { + Error error = Assertions.assertThrows(Error.class, () -> request.getContentLength()); + checkError(error); + } + + @Test + public void testGetContentLengthLong() { + Error error = Assertions.assertThrows(Error.class, () -> request.getContentLengthLong()); + checkError(error); + } + + @Test + public void testGetContentType() { + Error error = Assertions.assertThrows(Error.class, () -> request.getContentType()); + checkError(error); + } + + @Test + public void testGetInputStream() { + Error error = Assertions.assertThrows(Error.class, () -> request.getInputStream()); + checkError(error); + } + + @Test + public void testGetParameter() { + Error error = Assertions.assertThrows(Error.class, () -> request.getParameter("")); + checkError(error); + } + + @Test + public void testGetParameterNames() { + Error error = Assertions.assertThrows(Error.class, () -> request.getParameterNames()); + checkError(error); + } + + @Test + public void testGetParameterValues() { + Error error = Assertions.assertThrows(Error.class, () -> request.getParameterValues("")); + checkError(error); + } + + @Test + public void testGetParameterMap() { + Error error = Assertions.assertThrows(Error.class, () -> request.getParameterMap()); + checkError(error); + } + + @Test + public void testGetProtocol() { + Error error = Assertions.assertThrows(Error.class, () -> request.getProtocol()); + checkError(error); + } + + @Test + public void testGetScheme() { + Error error = Assertions.assertThrows(Error.class, () -> request.getScheme()); + checkError(error); + } + + @Test + public void testGetServerName() { + Error error = Assertions.assertThrows(Error.class, () -> request.getServerName()); + checkError(error); + } + + @Test + public void testGetServerPort() { + Error error = Assertions.assertThrows(Error.class, () -> request.getServerPort()); + checkError(error); + } + + @Test + public void testGetReader() { + Error error = Assertions.assertThrows(Error.class, () -> request.getReader()); + checkError(error); + } + + @Test + public void testGetRemoteAddr() { + Error error = Assertions.assertThrows(Error.class, () -> request.getRemoteAddr()); + checkError(error); + } + + @Test + public void testGetRemoteHost() { + Error error = Assertions.assertThrows(Error.class, () -> request.getRemoteHost()); + checkError(error); + } + + @Test + public void testGetLocale() { + Error error = Assertions.assertThrows(Error.class, () -> request.getLocale()); + checkError(error); + } + + @Test + public void testGetLocales() { + Error error = Assertions.assertThrows(Error.class, () -> request.getLocales()); + checkError(error); + } + + @Test + public void testIsSecure() { + Error error = Assertions.assertThrows(Error.class, () -> request.isSecure()); + checkError(error); + } + + @Test + public void testGetRequestDispatcher() { + Error error = Assertions.assertThrows(Error.class, () -> request.getRequestDispatcher("")); + checkError(error); + } + + @Test + public void testGetRemotePort() { + Error error = Assertions.assertThrows(Error.class, () -> request.getRemotePort()); + checkError(error); + } + + @Test + public void testGetLocalName() { + Error error = Assertions.assertThrows(Error.class, () -> request.getLocalName()); + checkError(error); + } + + @Test + public void testGetLocalAddr() { + Error error = Assertions.assertThrows(Error.class, () -> request.getLocalAddr()); + checkError(error); + } + + @Test + public void testGetLocalPort() { + Error error = Assertions.assertThrows(Error.class, () -> request.getLocalPort()); + checkError(error); + } + + @Test + public void testGetServletContext() { + Error error = Assertions.assertThrows(Error.class, () -> request.getServletContext()); + checkError(error); + } + + @Test + public void testStartAsync() { + Error error = Assertions.assertThrows(Error.class, () -> request.startAsync()); + checkError(error); + } + + @Test + public void testStartAsyncWithParam() { + Error error = Assertions.assertThrows(Error.class, () -> request.startAsync(null, null)); + checkError(error); + } + + @Test + public void testIsAsyncStarted() { + Error error = Assertions.assertThrows(Error.class, () -> request.isAsyncStarted()); + checkError(error); + } + + @Test + public void testIsAsyncSupported() { + Error error = Assertions.assertThrows(Error.class, () -> request.isAsyncSupported()); + checkError(error); + } + + @Test + public void testGetAsyncContext() { + Error error = Assertions.assertThrows(Error.class, () -> request.getAsyncContext()); + checkError(error); + } + + @Test + public void testGetDispatcherType() { + Error error = Assertions.assertThrows(Error.class, () -> request.getDispatcherType()); + checkError(error); + } + + @Test + public void testGetAuthType() { + Error error = Assertions.assertThrows(Error.class, () -> request.getAuthType()); + checkError(error); + } + + @Test + public void testGetCookies() { + Error error = Assertions.assertThrows(Error.class, () -> request.getCookies()); + checkError(error); + } + + @Test + public void testGetDateHeader() { + Error error = Assertions.assertThrows(Error.class, () -> request.getDateHeader("")); + checkError(error); + } + + @Test + public void testGetHeader() { + Error error = Assertions.assertThrows(Error.class, () -> request.getHeader("")); + checkError(error); + } + + @Test + public void testGetHeaders() { + Error error = Assertions.assertThrows(Error.class, () -> request.getHeaders("")); + checkError(error); + } + + @Test + public void testGetHeaderNames() { + Error error = Assertions.assertThrows(Error.class, () -> request.getHeaderNames()); + checkError(error); + } + + @Test + public void testGetIntHeader() { + Error error = Assertions.assertThrows(Error.class, () -> request.getIntHeader("")); + checkError(error); + } + + @Test + public void testGetMethod() { + Error error = Assertions.assertThrows(Error.class, () -> request.getMethod()); + checkError(error); + } + + @Test + public void testGetPathInfo() { + Error error = Assertions.assertThrows(Error.class, () -> request.getPathInfo()); + checkError(error); + } + + @Test + public void testGetPathTranslated() { + Error error = Assertions.assertThrows(Error.class, () -> request.getPathTranslated()); + checkError(error); + } + + @Test + public void testGetContextPath() { + Error error = Assertions.assertThrows(Error.class, () -> request.getContextPath()); + checkError(error); + } + + @Test + public void testGetQueryString() { + Error error = Assertions.assertThrows(Error.class, () -> request.getQueryString()); + checkError(error); + } + + @Test + public void testGetRemoteUser() { + Error error = Assertions.assertThrows(Error.class, () -> request.getRemoteUser()); + checkError(error); + } + + @Test + public void testIsUserInRole() { + Error error = Assertions.assertThrows(Error.class, () -> request.isUserInRole("")); + checkError(error); + } + + @Test + public void testGetUserPrincipal() { + Error error = Assertions.assertThrows(Error.class, () -> request.getUserPrincipal()); + checkError(error); + } + + @Test + public void testGetRequestedSessionId() { + Error error = Assertions.assertThrows(Error.class, () -> request.getRequestedSessionId()); + checkError(error); + } + + @Test + public void testGetRequestURI() { + Error error = Assertions.assertThrows(Error.class, () -> request.getRequestURI()); + checkError(error); + } + + @Test + public void testGetRequestURL() { + Error error = Assertions.assertThrows(Error.class, () -> request.getRequestURL()); + checkError(error); + } + + @Test + public void testGetServletPath() { + Error error = Assertions.assertThrows(Error.class, () -> request.getServletPath()); + checkError(error); + } + + @Test + public void testGetSessionWithParam() { + Error error = Assertions.assertThrows(Error.class, () -> request.getSession(true)); + checkError(error); + } + + @Test + public void testGetSession() { + Error error = Assertions.assertThrows(Error.class, () -> request.getSession()); + checkError(error); + } + + @Test + public void testChangeSessionId() { + Error error = Assertions.assertThrows(Error.class, () -> request.changeSessionId()); + checkError(error); + } + + @Test + public void testIsRequestedSessionIdValid() { + Error error = Assertions.assertThrows(Error.class, () -> request.isRequestedSessionIdValid()); + checkError(error); + } + + @Test + public void testIsRequestedSessionIdFromCookie() { + Error error = Assertions.assertThrows(Error.class, () -> request.isRequestedSessionIdFromCookie()); + checkError(error); + } + + @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)); + checkError(error); + } + + @Test + public void testLogin() { + Error error = Assertions.assertThrows(Error.class, () -> request.login(null, null)); + checkError(error); + } + + @Test + public void testLogout() { + Error error = Assertions.assertThrows(Error.class, () -> request.logout()); + checkError(error); + } + + @Test + public void testGetParts() { + Error error = Assertions.assertThrows(Error.class, () -> request.getParts()); + checkError(error); + } + + @Test + public void testGetPart() { + Error error = Assertions.assertThrows(Error.class, () -> request.getPart("")); + checkError(error); + } + + @Test + public void testUpgrade() { + Error error = Assertions.assertThrows(Error.class, () -> request.upgrade(null)); + checkError(error); + } +} 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 new file mode 100644 index 00000000000..053cc56e7de --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java @@ -0,0 +1,252 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestAbstractHttpServletResponse { + + HttpServletResponseEx response = new AbstractHttpServletResponse() { + }; + + private void checkError(Error error) { + Assertions.assertEquals("not supported method", error.getMessage()); + } + + @Test + public void testGetCharacterEncoding() { + Error error = Assertions.assertThrows(Error.class, () -> response.getCharacterEncoding()); + checkError(error); + } + + @Test + public void testGetContentType() { + Error error = Assertions.assertThrows(Error.class, () -> response.getContentType()); + checkError(error); + } + + @Test + public void testGetOutputStream() { + Error error = Assertions.assertThrows(Error.class, () -> response.getOutputStream()); + checkError(error); + } + + @Test + public void testGetWriter() { + Error error = Assertions.assertThrows(Error.class, () -> response.getWriter()); + checkError(error); + } + + @Test + public void testSetCharacterEncoding() { + Error error = Assertions.assertThrows(Error.class, () -> response.setCharacterEncoding("")); + checkError(error); + } + + @Test + public void testSetContentLength() { + Error error = Assertions.assertThrows(Error.class, () -> response.setContentLength(0)); + checkError(error); + } + + @Test + public void testSetContentLengthLong() { + Error error = Assertions.assertThrows(Error.class, () -> response.setContentLengthLong(0)); + checkError(error); + } + + @Test + public void testSetContentType() { + Error error = Assertions.assertThrows(Error.class, () -> response.setContentType("")); + checkError(error); + } + + @Test + public void testSetBufferSize() { + Error error = Assertions.assertThrows(Error.class, () -> response.setBufferSize(0)); + checkError(error); + } + + @Test + public void testGetBufferSize() { + Error error = Assertions.assertThrows(Error.class, () -> response.getBufferSize()); + checkError(error); + } + + @Test + public void testFlushBuffer() { + Assertions.assertDoesNotThrow(() -> response.flushBuffer()); + } + + @Test + public void testResetBuffer() { + Error error = Assertions.assertThrows(Error.class, () -> response.resetBuffer()); + checkError(error); + } + + @Test + public void testIsCommitted() { + Error error = Assertions.assertThrows(Error.class, () -> response.isCommitted()); + checkError(error); + } + + @Test + public void testReset() { + Error error = Assertions.assertThrows(Error.class, () -> response.reset()); + checkError(error); + } + + @Test + public void testSetLocale() { + Error error = Assertions.assertThrows(Error.class, () -> response.setLocale(null)); + checkError(error); + } + + @Test + public void testGetLocale() { + Error error = Assertions.assertThrows(Error.class, () -> response.getLocale()); + checkError(error); + } + + @Test + public void testAddCookie() { + Error error = Assertions.assertThrows(Error.class, () -> response.addCookie(null)); + checkError(error); + } + + @Test + public void testContainsHeader() { + Error error = Assertions.assertThrows(Error.class, () -> response.containsHeader(null)); + checkError(error); + } + + @Test + public void testEncodeURL() { + Error error = Assertions.assertThrows(Error.class, () -> response.encodeURL(null)); + checkError(error); + } + + @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)); + checkError(error); + } + + @Test + public void testSendErrorSc() { + Error error = Assertions.assertThrows(Error.class, () -> response.sendError(0)); + checkError(error); + } + + @Test + public void testSendRedirect() { + Error error = Assertions.assertThrows(Error.class, () -> response.sendRedirect(null)); + checkError(error); + } + + @Test + public void testSetDateHeader() { + Error error = Assertions.assertThrows(Error.class, () -> response.setDateHeader(null, 0)); + checkError(error); + } + + @Test + public void testAddDateHeader() { + Error error = Assertions.assertThrows(Error.class, () -> response.addDateHeader(null, 0)); + checkError(error); + } + + @Test + public void testSetHeader() { + Error error = Assertions.assertThrows(Error.class, () -> response.setHeader(null, null)); + checkError(error); + } + + @Test + public void testAddHeader() { + Error error = Assertions.assertThrows(Error.class, () -> response.addHeader(null, null)); + checkError(error); + } + + @Test + public void testSetIntHeader() { + Error error = Assertions.assertThrows(Error.class, () -> response.setIntHeader(null, 0)); + checkError(error); + } + + @Test + public void testAddIntHeader() { + Error error = Assertions.assertThrows(Error.class, () -> response.addIntHeader(null, 0)); + checkError(error); + } + + @Test + public void testSetStatusSc() { + Error error = Assertions.assertThrows(Error.class, () -> response.setStatus(0)); + checkError(error); + } + + @Test + public void testGetStatus() { + Error error = Assertions.assertThrows(Error.class, () -> response.getStatus()); + checkError(error); + } + + @Test + public void testGetHeader() { + Error error = Assertions.assertThrows(Error.class, () -> response.getHeader("")); + checkError(error); + } + + @Test + public void testGetHeaders() { + Error error = Assertions.assertThrows(Error.class, () -> response.getHeaders("")); + checkError(error); + } + + @Test + public void testGetHeaderNames() { + Error error = Assertions.assertThrows(Error.class, () -> response.getHeaderNames()); + checkError(error); + } + + @Test + public void testGetStatusType() { + Error error = Assertions.assertThrows(Error.class, () -> response.getStatusType()); + checkError(error); + } + + @Test + public void attribute() { + response.setAttribute("k", "v"); + Assertions.assertEquals("v", response.getAttribute("k")); + } + + @Test + public void sendPart() { + Error error = Assertions.assertThrows(Error.class, () -> response.sendPart(null)); + checkError(error); + } +} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestBodyBufferSupportImpl.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestBodyBufferSupportImpl.java new file mode 100644 index 00000000000..f12bec04915 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestBodyBufferSupportImpl.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.vertx.http; + +import io.vertx.core.buffer.Buffer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestBodyBufferSupportImpl { + BodyBufferSupportImpl impl = new BodyBufferSupportImpl(); + + @Test + public void testSetBodyBuffer() { + impl.setBodyBytes(new byte[] {}); + impl.setBodyLength(10); + + Assertions.assertNotNull(impl.getBodyBytes()); + Assertions.assertEquals(10, impl.getBodyBytesLength()); + + impl.setBodyBuffer(null); + + Assertions.assertNull(impl.getBodyBytes()); + Assertions.assertEquals(0, impl.getBodyBytesLength()); + } + + @Test + public void testGetBodyBuffer() { + Assertions.assertNull(impl.getBodyBuffer()); + + Buffer bodyBuffer = Buffer.buffer(); + impl.setBodyBuffer(bodyBuffer); + + Assertions.assertSame(bodyBuffer, impl.getBodyBuffer()); + } + + @Test + public void testGetBodyBytes() { + Assertions.assertNull(impl.getBodyBytes()); + + byte[] bytes = new byte[] {1, 2, 3}; + Buffer bodyBuffer = Buffer.buffer(bytes); + impl.setBodyBuffer(bodyBuffer); + + Assertions.assertArrayEquals(bytes, impl.getBodyBytes()); + } + + @Test + public void testGetBodyBytesLength() { + Assertions.assertEquals(0, impl.getBodyBytesLength()); + + byte[] bytes = new byte[] {1, 2, 3}; + Buffer bodyBuffer = Buffer.buffer(bytes); + impl.setBodyBuffer(bodyBuffer); + + Assertions.assertEquals(3, impl.getBodyBytesLength()); + } +} 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 new file mode 100644 index 00000000000..ed2e331da71 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestFileUploadPart.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.foundation.vertx.http; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.UUID; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import io.vertx.ext.web.FileUpload; +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; + +public class TestFileUploadPart { + + FileUpload fileUpload; + + FileUploadPart part; + + static File file; + + static String content = "fileContent"; + + @BeforeAll + public static void classSetup() throws IOException { + file = Files.createTempFile("upload", ".txt").toFile(); + file.deleteOnExit(); + FileUtils.writeStringToFile(file, content, StandardCharsets.UTF_8, false); + } + + @BeforeEach + public void setup() { + fileUpload = Mockito.mock(FileUpload.class); + part = new FileUploadPart(fileUpload); + } + + @AfterEach + public void after() { + Mockito.reset(fileUpload); + } + + @Test + public void getInputStream() throws IOException { + Mockito.when(fileUpload.uploadedFileName()).thenReturn(file.getAbsolutePath()); + try (InputStream is = part.getInputStream()) { + Assertions.assertEquals(content, IOUtils.toString(is, StandardCharsets.UTF_8)); + } + } + + @Test + public void getContentType() { + String contentType = "type"; + Mockito.when(fileUpload.contentType()).thenReturn(contentType); + + Assertions.assertEquals(contentType, part.getContentType()); + } + + @Test + public void getName() { + String name = "pName"; + Mockito.when(fileUpload.name()).thenReturn(name); + + Assertions.assertEquals(name, part.getName()); + } + + @Test + public void getSubmittedFileName() { + String clientName = "clientName"; + Mockito.when(fileUpload.fileName()).thenReturn(clientName); + + Assertions.assertEquals(clientName, part.getSubmittedFileName()); + } + + @Test + public void getSize() { + long fileSize = 10; + Mockito.when(fileUpload.size()).thenReturn(fileSize); + + Assertions.assertEquals(fileSize, part.getSize()); + } + + @Test + public void write() throws IOException { + Mockito.when(fileUpload.uploadedFileName()).thenReturn(file.getAbsolutePath()); + + File targetFile = new File(UUID.randomUUID().toString()); + targetFile.deleteOnExit(); + part.write(targetFile.getAbsolutePath()); + Assertions.assertEquals(content, FileUtils.readFileToString(targetFile, StandardCharsets.UTF_8)); + } +} 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 new file mode 100644 index 00000000000..15e52aa5f71 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletRequestEx.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +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; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; + +import io.vertx.core.buffer.Buffer; +import mockit.Deencapsulation; +import mockit.Expectations; +import mockit.Mocked; +import org.junit.jupiter.api.Assertions; + +public class TestStandardHttpServletRequestEx { + @Mocked + HttpServletRequest request; + + StandardHttpServletRequestEx requestEx; + + @Before + public void setup() { + requestEx = new StandardHttpServletRequestEx(request); + } + + @Test + public void setBodyBuffer() { + Buffer bodyBuffer = Buffer.buffer(); + bodyBuffer.appendString("abc"); + + requestEx.setBodyBuffer(bodyBuffer); + Assertions.assertSame(bodyBuffer, requestEx.getBodyBuffer()); + Assertions.assertArrayEquals("abc".getBytes(), Arrays.copyOf(requestEx.getBodyBytes(), requestEx.getBodyBytesLength())); + } + + @Test + public void getInputStreamNotCache() throws IOException { + ServletInputStream inputStream = request.getInputStream(); + + Assertions.assertSame(inputStream, requestEx.getInputStream()); + } + + @Test + public void getInputStreamCache() throws IOException { + requestEx.setCacheRequest(true); + + ServletInputStream inputStream = request.getInputStream(); + new Expectations(IOUtils.class) { + { + IOUtils.toByteArray(inputStream); + result = "abc".getBytes(); + } + }; + + ServletInputStream cachedInputStream = requestEx.getInputStream(); + Assertions.assertEquals("abc", IOUtils.toString(cachedInputStream, StandardCharsets.UTF_8)); + Assertions.assertEquals("abc", requestEx.getBodyBuffer().toString()); + // do not create another one + Assertions.assertSame(cachedInputStream, requestEx.getInputStream()); + } + + @Test + public void parameterMap_inherited() { + Map inherited = new HashMap<>(); + String[] v1 = new String[] {"v1-1", "v1-2"}; + inherited.put("p1", v1); + new Expectations() { + { + request.getParameterMap(); + result = inherited; + request.getMethod(); + result = HttpMethod.POST; + } + }; + + Assertions.assertSame(inherited, requestEx.getParameterMap()); + MatcherAssert.assertThat(Collections.list(requestEx.getParameterNames()), Matchers.contains("p1")); + Assertions.assertSame(v1, requestEx.getParameterValues("p1")); + Assertions.assertEquals("v1-1", requestEx.getParameter("p1")); + } + + @Test + public void parameterMap_merge() throws IOException { + Map inherited = new HashMap<>(); + String[] v1 = new String[] {"v1-1", "v1-2"}; + inherited.put("p1", v1); + + Buffer buffer = Buffer.buffer("p1=v1-3;p2=v2"); + BufferInputStream inputStream = new BufferInputStream(buffer); + new Expectations() { + { + request.getParameterMap(); + result = inherited; + request.getMethod(); + result = HttpMethod.PUT; + request.getContentType(); + result = MediaType.APPLICATION_FORM_URLENCODED.toUpperCase(Locale.US) + ";abc"; + request.getInputStream(); + result = inputStream; + } + }; + + MatcherAssert.assertThat(Collections.list(requestEx.getParameterNames()), Matchers.containsInAnyOrder("p1", "p2")); + MatcherAssert.assertThat(requestEx.getParameterValues("p1"), Matchers.arrayContaining("v1-1", "v1-2", "v1-3")); + Assertions.assertEquals("v1-1", requestEx.getParameter("p1")); + } + + @Test + public void setParameter() { + Map parameterMap = new HashMap<>(); + Deencapsulation.setField(requestEx, "parameterMap", parameterMap); + + requestEx.setParameter("k1", "v1"); + requestEx.setParameter("k2", "v2"); + + Assertions.assertEquals("v1", requestEx.getParameter("k1")); + Assertions.assertEquals("v2", requestEx.getParameter("k2")); + + Assertions.assertSame(parameterMap, requestEx.getParameterMap()); + + MatcherAssert.assertThat(Collections.list(requestEx.getParameterNames()), Matchers.containsInAnyOrder("k1", "k2")); + } +} 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 new file mode 100644 index 00000000000..289d5b15226 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.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.foundation.vertx.http; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +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; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +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 { + HttpServletResponse response; + + StandardHttpServletResponseEx responseEx; + + @Before + public void setup() { + response = Mockito.mock(HttpServletResponse.class); + responseEx = new StandardHttpServletResponseEx(response); + } + + @Test + public void setBodyBuffer() { + Assertions.assertNull(responseEx.getBodyBuffer()); + + Buffer bodyBuffer = Buffer.buffer(); + bodyBuffer.appendString("abc"); + responseEx.setBodyBuffer(bodyBuffer); + Assertions.assertEquals("abc", responseEx.getBodyBuffer().toString()); + } + + @Test + public void getBodyBytes() { + Buffer bodyBuffer = Buffer.buffer(); + bodyBuffer.appendString("abc"); + responseEx.setBodyBuffer(bodyBuffer); + Assertions.assertEquals("abc", new String(responseEx.getBodyBytes(), 0, responseEx.getBodyBytesLength())); + } + + @Test + public void getBodyBytesLength() { + Buffer bodyBuffer = Buffer.buffer(); + bodyBuffer.appendString("abc"); + responseEx.setBodyBuffer(bodyBuffer); + Assertions.assertEquals(3, responseEx.getBodyBytesLength()); + } + + @Test + public void flushBuffer() throws IOException { + Buffer buffer = Buffer.buffer(); + ServletOutputStream output = new ServletOutputStream() { + @Override + public boolean isReady() { + return true; + } + + @Override + public void setWriteListener(WriteListener writeListener) { + + } + + @Override + public void write(int b) { + buffer.appendByte((byte) b); + } + }; + + Mockito.when(response.getOutputStream()).thenReturn(output); + responseEx = new StandardHttpServletResponseEx(response); + + // no body + responseEx.flushBuffer(); + Assertions.assertEquals(0, buffer.length()); + + Buffer body = Buffer.buffer().appendString("body"); + responseEx.setBodyBuffer(body); + responseEx.flushBuffer(); + Assertions.assertEquals(0, buffer.length()); + } + + @Test + public void attribute() { + responseEx.setAttribute("k", "v"); + Assertions.assertEquals("v", responseEx.getAttribute("k")); + } + + @Test + public void sendPart_succ() throws Throwable { + String src = RandomStringUtils.random(100, true, true); + InputStream inputStream = new ByteArrayInputStream(src.getBytes()); + Part part = new InputStreamPart("name", inputStream); + Buffer buffer = Buffer.buffer(); + + ServletOutputStream outputStream = new ServletOutputStream() { + @Override + public boolean isReady() { + return false; + } + + @Override + public void setWriteListener(WriteListener writeListener) { + + } + + @Override + public void write(int b) { + buffer.appendByte((byte) b); + } + }; + Mockito.when(response.getOutputStream()).thenReturn(outputStream); + + responseEx.sendPart(part).get(); + + Assertions.assertEquals(src, buffer.toString()); + } + + @Test + public void sendPart_failed() throws Throwable { + Part part = Mockito.mock(Part.class); + RuntimeException error = new RuntimeExceptionWithoutStackTrace(); + Mockito.when(response.getOutputStream()).thenThrow(error); + + Assertions.assertThrows(RuntimeException.class, () -> responseEx.sendPart(part).get()); + } +} 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 new file mode 100644 index 00000000000..9cbe78713b3 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxClientRequestToHttpServletRequest.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.util.Collections; + +import jakarta.ws.rs.core.HttpHeaders; + +import org.apache.servicecomb.foundation.common.http.HttpUtils; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; + +import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpMethod; +import mockit.Expectations; +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 TestVertxClientRequestToHttpServletRequest { + HttpClientRequest clientRequest; + + Buffer bodyBuffer = Buffer.buffer(); + + VertxClientRequestToHttpServletRequest request; + + @BeforeEach + public void setup() { + clientRequest = Mockito.mock(HttpClientRequest.class); + request = new VertxClientRequestToHttpServletRequest(clientRequest, bodyBuffer); + } + + @AfterEach + public void after() { + Mockito.reset(clientRequest); + } + + @Test + public void testGetRequestURI() { + Mockito.when(clientRequest.path()).thenReturn("/path"); + + Assertions.assertEquals("/path", request.getRequestURI()); + } + + @Test + public void testGetQueryString() { + Mockito.when(clientRequest.query()).thenReturn("a=1&b=2"); + + Assertions.assertEquals("a=1&b=2", request.getQueryString()); + } + + @Test + public void testGetHeader() { + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + headers.add("name", "value"); + Mockito.when(clientRequest.headers()).thenReturn(headers); + + Assertions.assertEquals("value", request.getHeader("name")); + } + + @Test + public void testGetHeaders() { + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + headers.add("name", "value"); + Mockito.when(clientRequest.headers()).thenReturn(headers); + + MatcherAssert.assertThat(Collections.list(request.getHeaders("name")), Matchers.contains("value")); + } + + @Test + public void testGetHeaderNames() { + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + headers.add("name", "value"); + Mockito.when(clientRequest.headers()).thenReturn(headers); + + MatcherAssert.assertThat(Collections.list(request.getHeaderNames()), Matchers.contains("name")); + } + + @Test + public void testSetHeader() { + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + Mockito.when(clientRequest.headers()).thenReturn(headers); + + request.setHeader("name", "v1"); + request.setHeader("name", "v2"); + MatcherAssert.assertThat(headers.getAll("name"), Matchers.contains("v2")); + } + + @Test + public void testAddHeader() { + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + Mockito.when(clientRequest.headers()).thenReturn(headers); + + request.addHeader("name", "v1"); + request.addHeader("name", "v2"); + MatcherAssert.assertThat(headers.getAll("name"), Matchers.contains("v1", "v2")); + } + + @Test + public void testGetContextPath() { + Assertions.assertEquals("", request.getContextPath()); + } + + @Test + public void getMethod() { + Mockito.when(clientRequest.getMethod()).thenReturn(HttpMethod.GET); + + Assertions.assertEquals("GET", request.getMethod()); + } + + @Test + public void getContentType() { + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + Mockito.when(clientRequest.headers()).thenReturn(headers); + + request.addHeader(HttpHeaders.CONTENT_TYPE, "ct"); + + Assertions.assertEquals("ct", request.getContentType()); + } + + @Test + public void getCharacterEncoding() { + String contentType = "ct"; + String characterEncoding = "ce"; + + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + new Expectations(HttpUtils.class) { + { + HttpUtils.getCharsetFromContentType(contentType); + result = characterEncoding; + } + }; + Mockito.when(clientRequest.headers()).thenReturn(headers); + + request.addHeader(HttpHeaders.CONTENT_TYPE, contentType); + + Assertions.assertEquals("ce", request.getCharacterEncoding()); + } +} 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 new file mode 100644 index 00000000000..ca204c9b6fc --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxClientResponseToHttpServletResponse.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.foundation.vertx.http; + +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response.StatusType; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; + +import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClientResponse; +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 TestVertxClientResponseToHttpServletResponse { + HttpClientResponse clientResponse; + + Buffer bodyBuffer = Buffer.buffer(); + + VertxClientResponseToHttpServletResponse response; + + @BeforeEach + public void setup() { + clientResponse = Mockito.mock(HttpClientResponse.class); + response = new VertxClientResponseToHttpServletResponse(clientResponse, bodyBuffer); + } + + @AfterEach + public void after() { + Mockito.reset(clientResponse); + } + + @Test + public void getStatus() { + Mockito.when(clientResponse.statusCode()).thenReturn(123); + + Assertions.assertEquals(123, response.getStatus()); + } + + @Test + public void getStatusType() { + Mockito.when(clientResponse.statusCode()).thenReturn(123); + Mockito.when(clientResponse.statusMessage()).thenReturn("test"); + + StatusType type = response.getStatusType(); + Assertions.assertSame(type, response.getStatusType()); + Assertions.assertEquals(123, type.getStatusCode()); + Assertions.assertEquals("test", type.getReasonPhrase()); + } + + @Test + public void getContentType() { + Mockito.when(clientResponse.getHeader(HttpHeaders.CONTENT_TYPE)).thenReturn("json"); + + Assertions.assertEquals("json", response.getContentType()); + } + + @Test + public void getHeader() { + Mockito.when(clientResponse.getHeader("name")).thenReturn("value"); + + Assertions.assertEquals("value", response.getHeader("name")); + } + + @Test + public void getHeaders() { + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + headers.add("name", "v1"); + headers.add("name", "v2"); + Mockito.when(clientResponse.headers()).thenReturn(headers); + + MatcherAssert.assertThat(response.getHeaders("name"), Matchers.contains("v1", "v2")); + } + + @Test + public void getHeaderNames() { + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + headers.add("n1", "v1"); + headers.add("n2", "v2"); + Mockito.when(clientResponse.headers()).thenReturn(headers); + + MatcherAssert.assertThat(response.getHeaderNames(), Matchers.contains("n1", "n2")); + } +} 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 new file mode 100644 index 00000000000..9d53cd649da --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerRequestToHttpServletRequest.java @@ -0,0 +1,496 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +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; +import org.apache.servicecomb.foundation.common.Holder; +import org.apache.servicecomb.foundation.common.http.HttpUtils; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.net.SocketAddress; +import io.vertx.ext.web.RoutingContext; +import mockit.Deencapsulation; +import mockit.Expectations; +import mockit.Mocked; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; + +public class TestVertxServerRequestToHttpServletRequest { + @Mocked + RoutingContext context; + + @Mocked + HttpServerRequest vertxRequest; + + @Mocked + SocketAddress socketAddress; + + RequestBody mockRequestBody; + + RoutingContextInternal mockContext; + + HttpServerRequest mockHttpServerRequest; + + SocketAddress mockSocketAddress; + + VertxServerRequestToHttpServletRequest request; + + @Before + public void setup() { + new Expectations() { + { + context.request(); + result = vertxRequest; + vertxRequest.remoteAddress(); + result = socketAddress; + } + }; + + mockRequestBody = Mockito.mock(RequestBody.class); + mockContext = Mockito.mock(RoutingContextInternal.class); + mockHttpServerRequest = Mockito.mock(HttpServerRequest.class); + mockSocketAddress = Mockito.mock(SocketAddress.class); + // init mocks + Mockito.when(mockHttpServerRequest.remoteAddress()).thenReturn(mockSocketAddress); + Mockito.when(mockContext.body()).thenReturn(mockRequestBody); + Mockito.when(mockContext.request()).thenReturn(mockHttpServerRequest); + + request = new VertxServerRequestToHttpServletRequest(context); + } + + @After + public void clean() { + Mockito.reset(mockRequestBody); + Mockito.reset(mockContext); + } + + @Test + public void constructWithPath() { + request = new VertxServerRequestToHttpServletRequest(context, "/path"); + + Assertions.assertEquals("/path", request.getRequestURI()); + } + + @Test + public void setBodyBuffer() { + Holder bodyHolder = new Holder<>(); + Mockito.doAnswer(invocation -> { + bodyHolder.value = invocation.getArgument(0); + return null; + }).when(mockContext).setBody(Mockito.any()); + request = new VertxServerRequestToHttpServletRequest(mockContext); + + Buffer bodyBuffer = Buffer.buffer(); + request.setBodyBuffer(bodyBuffer); + + Assertions.assertSame(bodyBuffer, bodyHolder.value); + Assertions.assertSame(bodyBuffer, request.getBodyBuffer()); + } + + @Test + public void testGetContentType() { + VertxServerRequestToHttpServletRequest request = new VertxServerRequestToHttpServletRequest(mockContext); + Mockito.when(mockHttpServerRequest.getHeader(HttpHeaders.CONTENT_TYPE)).thenReturn("json"); + Assertions.assertEquals("json", request.getContentType()); + } + + @Test + public void testGetCookies() { + Set vertxCookies = new HashSet<>(); + vertxCookies.add(io.vertx.core.http.Cookie.cookie("c1", "c1v")); + vertxCookies.add(io.vertx.core.http.Cookie.cookie("c2", "c2v")); + new Expectations() { + { + context.request().cookies(); + result = vertxCookies; + } + }; + + Cookie[] cookies = request.getCookies(); + // we can't ensure the sequence when set to list + if (cookies[0].getName().equals("c1")) { + Assertions.assertEquals("c1", cookies[0].getName()); + Assertions.assertEquals("c1v", cookies[0].getValue()); + Assertions.assertEquals("c2", cookies[1].getName()); + Assertions.assertEquals("c2v", cookies[1].getValue()); + } else { + Assertions.assertEquals("c2", cookies[0].getName()); + Assertions.assertEquals("c2v", cookies[0].getValue()); + Assertions.assertEquals("c1", cookies[1].getName()); + Assertions.assertEquals("c1v", cookies[1].getValue()); + } + } + + @Test + public void testGetParameter() { + new Expectations() { + { + vertxRequest.getParam("name"); + result = "value"; + } + }; + + Assertions.assertEquals("value", request.getParameter("name")); + } + + @Test + public void testGetParameterValuesNull() { + Assertions.assertEquals(0, request.getParameterValues("name").length); + } + + @Test + public void testGetParameterValuesNormal() { + MultiMap params = MultiMap.caseInsensitiveMultiMap(); + params.add("name", "value"); + + new Expectations() { + { + vertxRequest.params(); + result = params; + } + }; + + MatcherAssert.assertThat(request.getParameterValues("name"), Matchers.arrayContaining("value")); + } + + @Test + public void testGetParameterMap() { + MultiMap params = MultiMap.caseInsensitiveMultiMap(); + params.add("name", "value"); + + new Expectations() { + { + vertxRequest.params(); + result = params; + } + }; + + Map result = request.getParameterMap(); + MatcherAssert.assertThat(result.keySet(), Matchers.contains("name")); + MatcherAssert.assertThat(result.get("name"), Matchers.arrayContaining("value")); + Assertions.assertSame(result, request.getParameterMap()); + } + + @Test + public void testScheme() { + new Expectations() { + { + vertxRequest.scheme(); + result = "abc"; + } + }; + + Assertions.assertEquals("abc", request.getScheme()); + } + + @Test + public void testGetRemoteAddr() { + new Expectations() { + { + socketAddress.host(); + result = "host"; + } + }; + + Assertions.assertEquals("host", request.getRemoteAddr()); + } + + @Test + public void testGetRemoteAddrNull() { + new Expectations() { + { + socketAddress.host(); + result = null; + } + }; + Assertions.assertNull(request.getRemoteAddr()); + } + + @Test + public void testGetRemoteHost() { + new Expectations() { + { + socketAddress.host(); + result = "host"; + } + }; + + Assertions.assertEquals("host", request.getRemoteHost()); + } + + @Test + public void testGetRemotePort() { + new Expectations() { + { + socketAddress.port(); + result = 1234; + } + }; + + Assertions.assertEquals(1234, request.getRemotePort()); + } + + @Test + public void testGetgetLocalAddr(@Mocked SocketAddress sa) { + new Expectations() { + { + sa.host(); + result = "host"; + vertxRequest.localAddress(); + result = sa; + } + }; + + Assertions.assertEquals("host", request.getLocalAddr()); + } + + @Test + public void testGetLocalPort(@Mocked SocketAddress sa) { + new Expectations() { + { + sa.port(); + result = 1234; + vertxRequest.localAddress(); + result = sa; + } + }; + + Assertions.assertEquals(1234, request.getLocalPort()); + } + + @Test + public void testGetHeader() { + new Expectations() { + { + vertxRequest.getHeader("key"); + result = "value"; + } + }; + + Assertions.assertEquals("value", request.getHeader("key")); + } + + @Test + public void testGetHeaders() { + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + headers.add("name", "value"); + new Expectations() { + { + vertxRequest.headers(); + result = headers; + } + }; + + MatcherAssert.assertThat(Collections.list(request.getHeaders("name")), Matchers.contains("value")); + } + + @Test + public void testGetHeaderNames() { + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + headers.add("name", "value"); + new Expectations() { + { + vertxRequest.headers(); + result = headers; + } + }; + + MatcherAssert.assertThat(Collections.list(request.getHeaderNames()), Matchers.contains("name")); + } + + @Test + public void testGetIntHeaderNotExist() { + Assertions.assertEquals(-1, request.getIntHeader("key")); + } + + @Test + public void testGetIntHeaderNotNumber() { + new Expectations() { + { + vertxRequest.getHeader("key"); + result = "value"; + } + }; + + try { + request.getIntHeader("key"); + Assertions.fail("must throw exception"); + } catch (NumberFormatException e) { + Assertions.assertEquals("For input string: \"value\"", e.getMessage()); + } + } + + @Test + public void testGetIntHeaderNormal() { + new Expectations() { + { + vertxRequest.getHeader("key"); + result = "1"; + } + }; + + Assertions.assertEquals(1, request.getIntHeader("key")); + } + + @Test + public void testGetMethod() { + new Expectations() { + { + vertxRequest.method(); + result = HttpMethod.GET; + } + }; + + Assertions.assertEquals("GET", request.getMethod()); + } + + @Test + public void testGetPathInfo() { + new Expectations() { + { + vertxRequest.path(); + result = "/path"; + } + }; + + Assertions.assertEquals("/path", request.getPathInfo()); + } + + @Test + public void testGetQueryString() { + new Expectations() { + { + vertxRequest.query(); + result = "k1=v1&k2=v2"; + } + }; + + Assertions.assertEquals("k1=v1&k2=v2", request.getQueryString()); + } + + @Test + public void testGetRequestURI() { + new Expectations() { + { + vertxRequest.path(); + result = "/path"; + } + }; + + Assertions.assertEquals("/path", request.getRequestURI()); + } + + @Test + public void testGetServletPath() { + new Expectations() { + { + vertxRequest.path(); + result = "/path"; + } + }; + + Assertions.assertEquals("/path", request.getServletPath()); + } + + @Test + public void testGetContextPath() { + Assertions.assertEquals("", request.getContextPath()); + } + + @Test + public void testGetInputStream() throws IOException { + Buffer body = Buffer.buffer(); + body.appendByte((byte) 1); + + Mockito.when(mockRequestBody.buffer()).thenReturn(body); + Mockito.when(mockContext.request()).thenReturn(vertxRequest); + Mockito.when(mockContext.body()).thenReturn(mockRequestBody); + + VertxServerRequestToHttpServletRequest request = new VertxServerRequestToHttpServletRequest(mockContext); + ServletInputStream is1 = request.getInputStream(); + Assertions.assertSame(is1, request.getInputStream()); + int value = is1.read(); + is1.close(); + Assertions.assertEquals(1, value); + Assertions.assertSame(is1, request.getInputStream()); + + request.setBodyBuffer(Buffer.buffer().appendByte((byte) 2)); + ServletInputStream is2 = request.getInputStream(); + Assertions.assertNotSame(is1, is2); + } + + @Test + public void testGetAsyncContext() { + AsyncContext asyncContext = + Deencapsulation.getField(VertxServerRequestToHttpServletRequest.class, "EMPTY_ASYNC_CONTEXT"); + + Assertions.assertSame(asyncContext, request.getAsyncContext()); + } + + @Test + public void getCharacterEncoding() { + new Expectations(HttpUtils.class) { + { + vertxRequest.getHeader(HttpHeaders.CONTENT_TYPE); + result = "ct"; + HttpUtils.getCharsetFromContentType("ct"); + result = "ce"; + } + }; + + Assertions.assertEquals("ce", request.getCharacterEncoding()); + } + + + @Test + public void setParameter() { + Map parameterMap = new HashMap<>(); + Deencapsulation.setField(request, "parameterMap", parameterMap); + + request.setParameter("k1", "v1"); + request.setParameter("k2", "v2"); + + Assertions.assertEquals("v1", request.getParameter("k1")); + Assertions.assertEquals("v2", request.getParameter("k2")); + + Assertions.assertSame(parameterMap, request.getParameterMap()); + + MatcherAssert.assertThat(Collections.list(request.getParameterNames()), Matchers.containsInAnyOrder("k1", "k2")); + } +} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultHttpClientMetrics.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultHttpClientMetrics.java new file mode 100644 index 00000000000..7049fb9aa78 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultHttpClientMetrics.java @@ -0,0 +1,308 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.metrics; + +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultRequestMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.net.SocketAddress; +import io.vertx.core.net.impl.SocketAddressImpl; +import io.vertx.core.spi.observability.HttpRequest; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; +import org.junit.jupiter.api.Assertions; + +public class TestDefaultHttpClientMetrics { + @Mocked + Vertx vertx; + + VertxOptions vertxOptions = new VertxOptions(); + + MetricsOptionsEx metricsOptionsEx = new MetricsOptionsEx(); + + @Mocked + HttpClient anyHttpClient; + + HttpClientOptions options = new HttpClientOptions(); + + DefaultVertxMetrics defaultVertxMetrics; + + DefaultHttpClientMetrics clientMetrics_a; + + DefaultHttpClientMetrics clientMetrics_b; + + String host = "host"; + + int port1 = 1; + + int port2 = 2; + + SocketAddress address1 = new SocketAddressImpl(port1, host); + + SocketAddress address2 = new SocketAddressImpl(port2, host); + + DefaultClientMetrics clientMetrics_a_1; + + DefaultEndpointMetric endpointMetric_a_1; + + DefaultTcpSocketMetric socketMetric_a_1; + + DefaultClientMetrics clientMetrics_a_2; + + DefaultEndpointMetric endpointMetric_a_2; + + DefaultTcpSocketMetric socketMetric_a_2; + + DefaultClientMetrics clientMetrics_b_1; + + DefaultEndpointMetric endpointMetric_b_1; + + DefaultTcpSocketMetric socketMetric_b_1; + + DefaultClientMetrics clientMetrics_b_2; + + DefaultEndpointMetric endpointMetric_b_2; + + DefaultTcpSocketMetric socketMetric_b_2; + + static long nanoTime; + + @BeforeClass + public static void classSetup() { + new MockUp() { + @Mock + long nanoTime() { + return nanoTime; + } + }; + } + + private static DefaultTcpSocketMetric initSocketMetric(DefaultClientMetrics clientMetrics, + DefaultHttpClientMetrics metrics, + SocketAddress address) { + DefaultTcpSocketMetric socketMetric = metrics.connected(address, address.toString()); + metrics.endpointConnected(clientMetrics); + return socketMetric; + } + + @Before + public void setup() { + vertxOptions.setMetricsOptions(metricsOptionsEx); + defaultVertxMetrics = new DefaultVertxMetrics(vertxOptions); + defaultVertxMetrics.setVertx(vertx); + clientMetrics_a = (DefaultHttpClientMetrics) defaultVertxMetrics.createHttpClientMetrics(options); + clientMetrics_b = (DefaultHttpClientMetrics) defaultVertxMetrics.createHttpClientMetrics(options); + + nanoTime = 1; + + clientMetrics_a_1 = clientMetrics_a.createEndpointMetrics(address1, 0); + socketMetric_a_1 = initSocketMetric(clientMetrics_a_1, clientMetrics_a, address1); + endpointMetric_a_1 = socketMetric_a_1.getEndpointMetric(); + + clientMetrics_a_2 = clientMetrics_a.createEndpointMetrics(address2, 0); + socketMetric_a_2 = initSocketMetric(clientMetrics_a_2, clientMetrics_a, address2); + endpointMetric_a_2 = socketMetric_a_2.getEndpointMetric(); + + clientMetrics_b_1 = clientMetrics_b.createEndpointMetrics(address1, 0); + socketMetric_b_1 = initSocketMetric(clientMetrics_b_1, clientMetrics_b, address1); + endpointMetric_b_1 = socketMetric_b_1.getEndpointMetric(); + + clientMetrics_b_2 = clientMetrics_b.createEndpointMetrics(address2, 0); + socketMetric_b_2 = initSocketMetric(clientMetrics_b_2, clientMetrics_b, address2); + endpointMetric_b_2 = socketMetric_b_2.getEndpointMetric(); + } + + @Test + public void createMetrics() { + Assertions.assertNotSame(clientMetrics_a, clientMetrics_b); + } + + @Test + public void createEndpoint() { + Assertions.assertSame(endpointMetric_a_1, endpointMetric_b_1); + Assertions.assertNotSame(endpointMetric_a_1, endpointMetric_a_2); + + Assertions.assertNotSame(endpointMetric_a_2, endpointMetric_b_1); + Assertions.assertSame(endpointMetric_a_2, endpointMetric_b_2); + + Assertions.assertEquals(2, endpointMetric_a_1.getCurrentConnectionCount()); + Assertions.assertEquals(2, endpointMetric_a_2.getCurrentConnectionCount()); + } + + @Test + public void expire() { + metricsOptionsEx.setCheckClientEndpointMetricExpiredInNano(10); + + nanoTime = 2; + clientMetrics_a.disconnected(socketMetric_a_1, null); + clientMetrics_a.disconnected(socketMetric_a_2, null); + + nanoTime = 13; + defaultVertxMetrics.getClientEndpointMetricManager().onCheckClientEndpointMetricExpired(0); + Assertions.assertNotNull( + defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("http://" + address1.toString())); + Assertions.assertNotNull( + defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("http://" + address2.toString())); + + clientMetrics_b.disconnected(socketMetric_b_1, null); + clientMetrics_b.disconnected(socketMetric_b_2, null); + + nanoTime = 23; + defaultVertxMetrics.getClientEndpointMetricManager().onCheckClientEndpointMetricExpired(0); + Assertions.assertNotNull( + defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("http://" + address1.toString())); + Assertions.assertNotNull( + defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("http://" + address2.toString())); + + nanoTime = 24; + defaultVertxMetrics.getClientEndpointMetricManager().onCheckClientEndpointMetricExpired(0); + Assertions + .assertNull(defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric(address1.toString())); + Assertions + .assertNull(defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric(address2.toString())); + } + + @Test + public void connect() { + { + Assertions.assertSame(endpointMetric_a_1, socketMetric_a_1.getEndpointMetric()); + Assertions.assertTrue(socketMetric_a_1.isConnected()); + Assertions.assertEquals(1, socketMetric_a_1.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_a_1.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(0, socketMetric_a_1.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(2, socketMetric_a_1.getEndpointMetric().getCurrentConnectionCount()); + + nanoTime = 2; + clientMetrics_a.disconnected(socketMetric_a_1, null); + + Assertions.assertEquals(2, ((DefaultClientEndpointMetric) endpointMetric_a_1).getLastNanoTime()); + Assertions.assertFalse(socketMetric_a_1.isConnected()); + Assertions.assertEquals(1, socketMetric_a_1.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_a_1.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(1, socketMetric_a_1.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(1, socketMetric_a_1.getEndpointMetric().getCurrentConnectionCount()); + } + + { + Assertions.assertSame(endpointMetric_a_2, socketMetric_a_2.getEndpointMetric()); + Assertions.assertTrue(socketMetric_a_2.isConnected()); + Assertions.assertEquals(1, socketMetric_a_2.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_a_2.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(0, socketMetric_a_2.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(2, socketMetric_a_2.getEndpointMetric().getCurrentConnectionCount()); + + nanoTime = 4; + clientMetrics_a.disconnected(socketMetric_a_2, null); + + Assertions.assertEquals(4, ((DefaultClientEndpointMetric) endpointMetric_a_2).getLastNanoTime()); + Assertions.assertFalse(socketMetric_a_2.isConnected()); + Assertions.assertEquals(1, socketMetric_a_2.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_a_2.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(1, socketMetric_a_2.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(1, socketMetric_a_2.getEndpointMetric().getCurrentConnectionCount()); + } + + { + Assertions.assertSame(endpointMetric_b_1, socketMetric_b_1.getEndpointMetric()); + Assertions.assertTrue(socketMetric_b_1.isConnected()); + Assertions.assertEquals(1, socketMetric_b_1.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_b_1.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(1, socketMetric_b_1.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(1, socketMetric_b_1.getEndpointMetric().getCurrentConnectionCount()); + + nanoTime = 6; + clientMetrics_b.disconnected(socketMetric_b_1, null); + + Assertions.assertEquals(6, ((DefaultClientEndpointMetric) endpointMetric_b_1).getLastNanoTime()); + Assertions.assertFalse(socketMetric_b_1.isConnected()); + Assertions.assertEquals(1, socketMetric_b_1.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_b_1.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(2, socketMetric_b_1.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(0, socketMetric_b_1.getEndpointMetric().getCurrentConnectionCount()); + } + + { + Assertions.assertSame(endpointMetric_b_2, socketMetric_b_2.getEndpointMetric()); + Assertions.assertTrue(socketMetric_b_2.isConnected()); + Assertions.assertEquals(1, socketMetric_b_2.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_b_2.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(1, socketMetric_b_2.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(1, socketMetric_b_2.getEndpointMetric().getCurrentConnectionCount()); + + nanoTime = 7; + clientMetrics_b.disconnected(socketMetric_b_2, null); + + Assertions.assertEquals(7, ((DefaultClientEndpointMetric) endpointMetric_b_2).getLastNanoTime()); + Assertions.assertFalse(socketMetric_b_2.isConnected()); + Assertions.assertEquals(1, socketMetric_b_2.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_b_2.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(2, socketMetric_b_2.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(0, socketMetric_b_2.getEndpointMetric().getCurrentConnectionCount()); + } + } + + @Test + public void bytesReadAndWritten() { + DefaultTcpSocketMetric socketMetric = clientMetrics_a.connected(address1, host); + clientMetrics_a.endpointConnected(clientMetrics_a_1); + clientMetrics_a.bytesRead(socketMetric, address1, 1); + clientMetrics_a.bytesWritten(socketMetric, address1, 1); + + socketMetric = clientMetrics_a.connected(address2, host); + clientMetrics_a.endpointConnected(clientMetrics_a_2); + clientMetrics_a.bytesRead(socketMetric, address2, 1); + clientMetrics_a.bytesWritten(socketMetric, address2, 1); + + socketMetric = clientMetrics_b.connected(address1, host); + clientMetrics_b.endpointConnected(clientMetrics_b_1); + clientMetrics_b.bytesRead(socketMetric, address1, 1); + clientMetrics_b.bytesWritten(socketMetric, address1, 1); + + socketMetric = clientMetrics_b.connected(address2, host); + clientMetrics_b.endpointConnected(clientMetrics_b_2); + clientMetrics_b.bytesRead(socketMetric, address2, 1); + clientMetrics_b.bytesWritten(socketMetric, address2, 1); + + Assertions.assertEquals(2, endpointMetric_a_1.getBytesRead()); + Assertions.assertEquals(2, endpointMetric_a_2.getBytesRead()); + Assertions.assertEquals(2, endpointMetric_a_1.getBytesWritten()); + Assertions.assertEquals(2, endpointMetric_a_2.getBytesWritten()); + } + + @Test + public void requestBegin(@Mocked HttpRequest request) { + DefaultTcpSocketMetric socketMetric = clientMetrics_a.connected(address1, host); + + nanoTime = 2; + DefaultRequestMetric requestMetric = clientMetrics_a_1.requestBegin("/ui", request); + nanoTime = 3; + clientMetrics_a_1.requestEnd(requestMetric); + + Assertions.assertEquals(2, requestMetric.getRequestBeginTime()); + Assertions.assertEquals(3, requestMetric.getRequestEndTime()); + } +} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultHttpServerMetrics.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultHttpServerMetrics.java new file mode 100644 index 00000000000..d8cbc40c95b --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultHttpServerMetrics.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.metrics; + +import java.util.IdentityHashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; +import org.junit.Before; +import org.junit.Test; + +import io.vertx.core.VertxOptions; +import io.vertx.core.http.HttpServerOptions; +import io.vertx.core.net.SocketAddress; +import mockit.Mocked; +import org.junit.jupiter.api.Assertions; + +public class TestDefaultHttpServerMetrics { + + VertxOptions vertxOptions = new VertxOptions(); + + MetricsOptionsEx metricsOptionsEx = new MetricsOptionsEx(); + + DefaultVertxMetrics defaultVertxMetrics; + + @Mocked + SocketAddress listen1_addr; + + @Mocked + SocketAddress listen2_addr; + + @Mocked + HttpServerOptions options; + + @Mocked + SocketAddress anyRemoteAddr; + + DefaultHttpServerMetrics metrics_listen1_server1; + + DefaultHttpServerMetrics metrics_listen1_server2; + + DefaultEndpointMetric endpointMetric1; + + DefaultHttpServerMetrics metrics_listen2_server1; + + DefaultHttpServerMetrics metrics_listen2_server2; + + DefaultEndpointMetric endpointMetric2; + + String remoteName = "remote"; + + DefaultTcpSocketMetric socketMetric_listen1_1; + + DefaultTcpSocketMetric socketMetric_listen1_2; + + DefaultTcpSocketMetric socketMetric_listen2_1; + + DefaultTcpSocketMetric socketMetric_listen2_2; + + DefaultTcpSocketMetric socketMetric_listen2_3; + + @Before + public void setup() { + vertxOptions.setMetricsOptions(metricsOptionsEx); + defaultVertxMetrics = new DefaultVertxMetrics(vertxOptions); + + metrics_listen1_server1 = (DefaultHttpServerMetrics) defaultVertxMetrics + .createHttpServerMetrics(options, listen1_addr); + metrics_listen1_server2 = (DefaultHttpServerMetrics) defaultVertxMetrics + .createHttpServerMetrics(options, listen1_addr); + endpointMetric1 = metrics_listen1_server1.getEndpointMetric(); + + metrics_listen2_server1 = (DefaultHttpServerMetrics) defaultVertxMetrics + .createHttpServerMetrics(options, listen2_addr); + metrics_listen2_server2 = (DefaultHttpServerMetrics) defaultVertxMetrics + .createHttpServerMetrics(options, listen2_addr); + endpointMetric2 = metrics_listen2_server1.getEndpointMetric(); + + socketMetric_listen1_1 = metrics_listen1_server1.connected(anyRemoteAddr, remoteName); + socketMetric_listen1_2 = metrics_listen1_server2.connected(anyRemoteAddr, remoteName); + socketMetric_listen2_1 = metrics_listen2_server1.connected(anyRemoteAddr, remoteName); + socketMetric_listen2_2 = metrics_listen2_server2.connected(anyRemoteAddr, remoteName); + socketMetric_listen2_3 = metrics_listen2_server2.connected(anyRemoteAddr, remoteName); + } + + @Test + public void createMetrics() { + Map instances = new IdentityHashMap<>(); + instances.put(metrics_listen1_server1, null); + instances.put(metrics_listen1_server2, null); + instances.put(metrics_listen2_server1, null); + instances.put(metrics_listen2_server2, null); + Assertions.assertEquals(4, instances.size()); + + Assertions.assertSame(metrics_listen1_server1.getEndpointMetric(), metrics_listen1_server2.getEndpointMetric()); + Assertions.assertNotSame(metrics_listen1_server1.getEndpointMetric(), metrics_listen2_server1.getEndpointMetric()); + Assertions.assertSame(metrics_listen2_server1.getEndpointMetric(), metrics_listen2_server2.getEndpointMetric()); + } + + @Test + public void connectionCount() { + Map instances = new IdentityHashMap<>(); + instances.put(socketMetric_listen1_1, null); + instances.put(socketMetric_listen1_2, null); + instances.put(socketMetric_listen2_1, null); + instances.put(socketMetric_listen2_2, null); + instances.put(socketMetric_listen2_3, null); + Assertions.assertEquals(5, instances.size()); + + Assertions.assertTrue(socketMetric_listen1_1.isConnected()); + Assertions.assertTrue(socketMetric_listen1_2.isConnected()); + Assertions.assertTrue(socketMetric_listen2_1.isConnected()); + Assertions.assertTrue(socketMetric_listen2_2.isConnected()); + Assertions.assertTrue(socketMetric_listen2_3.isConnected()); + + Assertions.assertEquals(2, endpointMetric1.getCurrentConnectionCount()); + Assertions.assertEquals(3, endpointMetric2.getCurrentConnectionCount()); + + // disconnect + metrics_listen1_server1.disconnected(socketMetric_listen1_1, anyRemoteAddr); + metrics_listen1_server2.disconnected(socketMetric_listen1_2, anyRemoteAddr); + metrics_listen2_server1.disconnected(socketMetric_listen2_1, anyRemoteAddr); + metrics_listen2_server2.disconnected(socketMetric_listen2_2, anyRemoteAddr); + metrics_listen2_server2.disconnected(socketMetric_listen2_3, anyRemoteAddr); + + Assertions.assertFalse(socketMetric_listen1_1.isConnected()); + Assertions.assertFalse(socketMetric_listen1_2.isConnected()); + Assertions.assertFalse(socketMetric_listen2_1.isConnected()); + Assertions.assertFalse(socketMetric_listen2_2.isConnected()); + Assertions.assertFalse(socketMetric_listen2_3.isConnected()); + + Assertions.assertEquals(0, endpointMetric1.getCurrentConnectionCount()); + Assertions.assertEquals(0, endpointMetric2.getCurrentConnectionCount()); + } + + @Test + public void bytesRead() { + metrics_listen1_server1.bytesRead(socketMetric_listen1_1, anyRemoteAddr, 1); + metrics_listen1_server2.bytesRead(socketMetric_listen1_2, anyRemoteAddr, 2); + metrics_listen2_server1.bytesRead(socketMetric_listen2_1, anyRemoteAddr, 3); + metrics_listen2_server2.bytesRead(socketMetric_listen2_2, anyRemoteAddr, 4); + metrics_listen2_server2.bytesRead(socketMetric_listen2_3, anyRemoteAddr, 5); + + Assertions.assertEquals(3, endpointMetric1.getBytesRead()); + Assertions.assertEquals(12, endpointMetric2.getBytesRead()); + } + + @Test + public void bytesWritten() { + metrics_listen1_server1.bytesWritten(socketMetric_listen1_1, anyRemoteAddr, 1); + metrics_listen1_server2.bytesWritten(socketMetric_listen1_2, anyRemoteAddr, 2); + metrics_listen2_server1.bytesWritten(socketMetric_listen2_1, anyRemoteAddr, 3); + metrics_listen2_server2.bytesWritten(socketMetric_listen2_2, anyRemoteAddr, 4); + metrics_listen2_server2.bytesWritten(socketMetric_listen2_3, anyRemoteAddr, 5); + + Assertions.assertEquals(3, endpointMetric1.getBytesWritten()); + Assertions.assertEquals(12, endpointMetric2.getBytesWritten()); + } +} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultTcpClientMetrics.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultTcpClientMetrics.java new file mode 100644 index 00000000000..8c893b60ca9 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultTcpClientMetrics.java @@ -0,0 +1,264 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.metrics; + +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.net.NetClientOptions; +import io.vertx.core.net.SocketAddress; +import io.vertx.core.net.impl.SocketAddressImpl; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; +import org.junit.jupiter.api.Assertions; + +public class TestDefaultTcpClientMetrics { + @Mocked + Vertx vertx; + + VertxOptions vertxOptions = new VertxOptions(); + + MetricsOptionsEx metricsOptionsEx = new MetricsOptionsEx(); + + NetClientOptions options = new NetClientOptions(); + + DefaultVertxMetrics defaultVertxMetrics; + + DefaultTcpClientMetrics clientMetrics_a; + + DefaultTcpClientMetrics clientMetrics_b; + + String host = "host"; + + int port1 = 1; + + int port2 = 2; + + SocketAddress address1 = new SocketAddressImpl(port1, host); + + SocketAddress address2 = new SocketAddressImpl(port2, host); + + DefaultEndpointMetric endpointMetric_a_1; + + DefaultTcpSocketMetric socketMetric_a_1; + + DefaultEndpointMetric endpointMetric_a_2; + + DefaultTcpSocketMetric socketMetric_a_2; + + DefaultEndpointMetric endpointMetric_b_1; + + DefaultTcpSocketMetric socketMetric_b_1; + + DefaultEndpointMetric endpointMetric_b_2; + + DefaultTcpSocketMetric socketMetric_b_2; + + static long nanoTime; + + @BeforeClass + public static void classSetup() { + new MockUp() { + @Mock + long nanoTime() { + return nanoTime; + } + }; + } + + private static DefaultTcpSocketMetric initSocketMetric(DefaultTcpClientMetrics metrics, + SocketAddress address) { + return metrics.connected(address, address.toString()); + } + + @Before + public void setup() { + vertxOptions.setMetricsOptions(metricsOptionsEx); + defaultVertxMetrics = new DefaultVertxMetrics(vertxOptions); + defaultVertxMetrics.setVertx(vertx); + clientMetrics_a = (DefaultTcpClientMetrics) defaultVertxMetrics.createNetClientMetrics(options); + clientMetrics_b = (DefaultTcpClientMetrics) defaultVertxMetrics.createNetClientMetrics(options); + + nanoTime = 1; + + socketMetric_a_1 = initSocketMetric(clientMetrics_a, address1); + endpointMetric_a_1 = socketMetric_a_1.getEndpointMetric(); + socketMetric_a_2 = initSocketMetric(clientMetrics_a, address2); + endpointMetric_a_2 = socketMetric_a_2.getEndpointMetric(); + + socketMetric_b_1 = initSocketMetric(clientMetrics_b, address1); + endpointMetric_b_1 = socketMetric_b_1.getEndpointMetric(); + socketMetric_b_2 = initSocketMetric(clientMetrics_b, address2); + endpointMetric_b_2 = socketMetric_b_2.getEndpointMetric(); + } + + @Test + public void createMetrics() { + Assertions.assertNotSame(clientMetrics_a, clientMetrics_b); + } + + @Test + public void createEndpoint() { + Assertions.assertSame(endpointMetric_a_1, endpointMetric_b_1); + Assertions.assertNotSame(endpointMetric_a_1, endpointMetric_a_2); + + Assertions.assertNotSame(endpointMetric_a_2, endpointMetric_b_1); + Assertions.assertSame(endpointMetric_a_2, endpointMetric_b_2); + + Assertions.assertEquals(2, endpointMetric_a_1.getCurrentConnectionCount()); + Assertions.assertEquals(2, endpointMetric_a_2.getCurrentConnectionCount()); + } + + @Test + public void expire() { + metricsOptionsEx.setCheckClientEndpointMetricExpiredInNano(10); + + nanoTime = 2; + clientMetrics_a.disconnected(socketMetric_a_1, null); + clientMetrics_a.disconnected(socketMetric_a_2, null); + + nanoTime = 13; + defaultVertxMetrics.getClientEndpointMetricManager().onCheckClientEndpointMetricExpired(0); + Assertions.assertNotNull( + defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("tcp://" + address1.toString())); + Assertions.assertNotNull( + defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("tcp://" + address2.toString())); + + clientMetrics_b.disconnected(socketMetric_b_1, null); + clientMetrics_b.disconnected(socketMetric_b_2, null); + + nanoTime = 23; + defaultVertxMetrics.getClientEndpointMetricManager().onCheckClientEndpointMetricExpired(0); + Assertions.assertNotNull( + defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("tcp://" + address1.toString())); + Assertions.assertNotNull( + defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric("tcp://" + address2.toString())); + + nanoTime = 24; + defaultVertxMetrics.getClientEndpointMetricManager().onCheckClientEndpointMetricExpired(0); + Assertions + .assertNull(defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric(address1.toString())); + Assertions + .assertNull(defaultVertxMetrics.getClientEndpointMetricManager().getClientEndpointMetric(address2.toString())); + } + + @Test + public void connect() { + { + Assertions.assertSame(endpointMetric_a_1, socketMetric_a_1.getEndpointMetric()); + Assertions.assertTrue(socketMetric_a_1.isConnected()); + Assertions.assertEquals(1, socketMetric_a_1.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_a_1.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(0, socketMetric_a_1.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(2, socketMetric_a_1.getEndpointMetric().getCurrentConnectionCount()); + + nanoTime = 2; + clientMetrics_a.disconnected(socketMetric_a_1, null); + + Assertions.assertEquals(2, ((DefaultClientEndpointMetric) endpointMetric_a_1).getLastNanoTime()); + Assertions.assertFalse(socketMetric_a_1.isConnected()); + Assertions.assertEquals(1, socketMetric_a_1.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_a_1.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(1, socketMetric_a_1.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(1, socketMetric_a_1.getEndpointMetric().getCurrentConnectionCount()); + } + + { + Assertions.assertSame(endpointMetric_a_2, socketMetric_a_2.getEndpointMetric()); + Assertions.assertTrue(socketMetric_a_2.isConnected()); + Assertions.assertEquals(1, socketMetric_a_2.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_a_2.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(0, socketMetric_a_2.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(2, socketMetric_a_2.getEndpointMetric().getCurrentConnectionCount()); + + nanoTime = 4; + clientMetrics_a.disconnected(socketMetric_a_2, null); + + Assertions.assertEquals(4, ((DefaultClientEndpointMetric) endpointMetric_a_2).getLastNanoTime()); + Assertions.assertFalse(socketMetric_a_2.isConnected()); + Assertions.assertEquals(1, socketMetric_a_2.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_a_2.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(1, socketMetric_a_2.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(1, socketMetric_a_2.getEndpointMetric().getCurrentConnectionCount()); + } + + { + Assertions.assertSame(endpointMetric_b_1, socketMetric_b_1.getEndpointMetric()); + Assertions.assertTrue(socketMetric_b_1.isConnected()); + Assertions.assertEquals(1, socketMetric_b_1.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_b_1.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(1, socketMetric_b_1.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(1, socketMetric_b_1.getEndpointMetric().getCurrentConnectionCount()); + + nanoTime = 6; + clientMetrics_b.disconnected(socketMetric_b_1, null); + + Assertions.assertEquals(6, ((DefaultClientEndpointMetric) endpointMetric_b_1).getLastNanoTime()); + Assertions.assertFalse(socketMetric_b_1.isConnected()); + Assertions.assertEquals(1, socketMetric_b_1.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_b_1.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(2, socketMetric_b_1.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(0, socketMetric_b_1.getEndpointMetric().getCurrentConnectionCount()); + } + + { + Assertions.assertSame(endpointMetric_b_2, socketMetric_b_2.getEndpointMetric()); + Assertions.assertTrue(socketMetric_b_2.isConnected()); + Assertions.assertEquals(1, socketMetric_b_2.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_b_2.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(1, socketMetric_b_2.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(1, socketMetric_b_2.getEndpointMetric().getCurrentConnectionCount()); + + nanoTime = 7; + clientMetrics_b.disconnected(socketMetric_b_2, null); + + Assertions.assertEquals(7, ((DefaultClientEndpointMetric) endpointMetric_b_2).getLastNanoTime()); + Assertions.assertFalse(socketMetric_b_2.isConnected()); + Assertions.assertEquals(1, socketMetric_b_2.getConnectedTime()); + Assertions.assertEquals(2, socketMetric_b_2.getEndpointMetric().getConnectCount()); + Assertions.assertEquals(2, socketMetric_b_2.getEndpointMetric().getDisconnectCount()); + Assertions.assertEquals(0, socketMetric_b_2.getEndpointMetric().getCurrentConnectionCount()); + } + } + + @Test + public void bytesReadAndWritten() { + clientMetrics_a.bytesRead(socketMetric_a_1, address1, 1); + clientMetrics_a.bytesWritten(socketMetric_a_1, address1, 1); + + clientMetrics_a.bytesRead(socketMetric_a_2, address2, 1); + clientMetrics_a.bytesWritten(socketMetric_a_2, address2, 1); + + clientMetrics_b.bytesRead(socketMetric_b_1, address1, 1); + clientMetrics_b.bytesWritten(socketMetric_b_1, address1, 1); + + clientMetrics_b.bytesRead(socketMetric_b_2, address2, 1); + clientMetrics_b.bytesWritten(socketMetric_b_2, address2, 1); + + Assertions.assertEquals(2, endpointMetric_a_1.getBytesRead()); + Assertions.assertEquals(2, endpointMetric_a_2.getBytesRead()); + Assertions.assertEquals(2, endpointMetric_a_1.getBytesWritten()); + Assertions.assertEquals(2, endpointMetric_a_2.getBytesWritten()); + } +} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultTcpServerMetrics.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultTcpServerMetrics.java new file mode 100644 index 00000000000..f9b93f1ce99 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultTcpServerMetrics.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.metrics; + +import java.util.IdentityHashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; +import org.junit.Before; +import org.junit.Test; + +import io.vertx.core.VertxOptions; +import io.vertx.core.net.NetServerOptions; +import io.vertx.core.net.SocketAddress; +import mockit.Mocked; +import org.junit.jupiter.api.Assertions; + +public class TestDefaultTcpServerMetrics { + + VertxOptions vertxOptions = new VertxOptions(); + + MetricsOptionsEx metricsOptionsEx = new MetricsOptionsEx(); + + DefaultVertxMetrics defaultVertxMetrics; + + @Mocked + SocketAddress listen1_addr; + + @Mocked + SocketAddress listen2_addr; + + @Mocked + NetServerOptions options; + + DefaultTcpServerMetrics metrics_listen1_server1; + + DefaultTcpServerMetrics metrics_listen1_server2; + + DefaultEndpointMetric endpointMetric1; + + DefaultTcpServerMetrics metrics_listen2_server1; + + DefaultTcpServerMetrics metrics_listen2_server2; + + DefaultEndpointMetric endpointMetric2; + + @Mocked + SocketAddress anyRemoteAddr; + + String remoteName = "remote"; + + DefaultTcpSocketMetric socketMetric_listen1_1; + + DefaultTcpSocketMetric socketMetric_listen1_2; + + DefaultTcpSocketMetric socketMetric_listen2_1; + + DefaultTcpSocketMetric socketMetric_listen2_2; + + DefaultTcpSocketMetric socketMetric_listen2_3; + + @Before + public void setup() { + vertxOptions.setMetricsOptions(metricsOptionsEx); + defaultVertxMetrics = new DefaultVertxMetrics(vertxOptions); + + metrics_listen1_server1 = (DefaultTcpServerMetrics) defaultVertxMetrics + .createNetServerMetrics(options, listen1_addr); + metrics_listen1_server2 = (DefaultTcpServerMetrics) defaultVertxMetrics + .createNetServerMetrics(options, listen1_addr); + endpointMetric1 = metrics_listen1_server1.getEndpointMetric(); + + metrics_listen2_server1 = (DefaultTcpServerMetrics) defaultVertxMetrics + .createNetServerMetrics(options, listen2_addr); + metrics_listen2_server2 = (DefaultTcpServerMetrics) defaultVertxMetrics + .createNetServerMetrics(options, listen2_addr); + endpointMetric2 = metrics_listen2_server1.getEndpointMetric(); + + socketMetric_listen1_1 = metrics_listen1_server1.connected(anyRemoteAddr, remoteName); + socketMetric_listen1_2 = metrics_listen1_server2.connected(anyRemoteAddr, remoteName); + socketMetric_listen2_1 = metrics_listen2_server1.connected(anyRemoteAddr, remoteName); + socketMetric_listen2_2 = metrics_listen2_server2.connected(anyRemoteAddr, remoteName); + socketMetric_listen2_3 = metrics_listen2_server2.connected(anyRemoteAddr, remoteName); + } + + @Test + public void createMetrics() { + Map instances = new IdentityHashMap<>(); + instances.put(metrics_listen1_server1, null); + instances.put(metrics_listen1_server2, null); + instances.put(metrics_listen2_server1, null); + instances.put(metrics_listen2_server2, null); + Assertions.assertEquals(4, instances.size()); + + Assertions.assertSame(metrics_listen1_server1.getEndpointMetric(), metrics_listen1_server2.getEndpointMetric()); + Assertions.assertNotSame(metrics_listen1_server1.getEndpointMetric(), metrics_listen2_server1.getEndpointMetric()); + Assertions.assertSame(metrics_listen2_server1.getEndpointMetric(), metrics_listen2_server2.getEndpointMetric()); + } + + @Test + public void connectionCount() { + Map instances = new IdentityHashMap<>(); + instances.put(socketMetric_listen1_1, null); + instances.put(socketMetric_listen1_2, null); + instances.put(socketMetric_listen2_1, null); + instances.put(socketMetric_listen2_2, null); + instances.put(socketMetric_listen2_3, null); + Assertions.assertEquals(5, instances.size()); + + Assertions.assertTrue(socketMetric_listen1_1.isConnected()); + Assertions.assertTrue(socketMetric_listen1_2.isConnected()); + Assertions.assertTrue(socketMetric_listen2_1.isConnected()); + Assertions.assertTrue(socketMetric_listen2_2.isConnected()); + Assertions.assertTrue(socketMetric_listen2_3.isConnected()); + + Assertions.assertEquals(2, endpointMetric1.getCurrentConnectionCount()); + Assertions.assertEquals(3, endpointMetric2.getCurrentConnectionCount()); + + // disconnect + metrics_listen1_server1.disconnected(socketMetric_listen1_1, anyRemoteAddr); + metrics_listen1_server2.disconnected(socketMetric_listen1_2, anyRemoteAddr); + metrics_listen2_server1.disconnected(socketMetric_listen2_1, anyRemoteAddr); + metrics_listen2_server2.disconnected(socketMetric_listen2_2, anyRemoteAddr); + metrics_listen2_server2.disconnected(socketMetric_listen2_3, anyRemoteAddr); + + Assertions.assertFalse(socketMetric_listen1_1.isConnected()); + Assertions.assertFalse(socketMetric_listen1_2.isConnected()); + Assertions.assertFalse(socketMetric_listen2_1.isConnected()); + Assertions.assertFalse(socketMetric_listen2_2.isConnected()); + Assertions.assertFalse(socketMetric_listen2_3.isConnected()); + + Assertions.assertEquals(0, endpointMetric1.getCurrentConnectionCount()); + Assertions.assertEquals(0, endpointMetric2.getCurrentConnectionCount()); + } + + @Test + public void bytesRead() { + metrics_listen1_server1.bytesRead(socketMetric_listen1_1, anyRemoteAddr, 1); + metrics_listen1_server2.bytesRead(socketMetric_listen1_2, anyRemoteAddr, 2); + metrics_listen2_server1.bytesRead(socketMetric_listen2_1, anyRemoteAddr, 3); + metrics_listen2_server2.bytesRead(socketMetric_listen2_2, anyRemoteAddr, 4); + metrics_listen2_server2.bytesRead(socketMetric_listen2_3, anyRemoteAddr, 5); + + Assertions.assertEquals(3, endpointMetric1.getBytesRead()); + Assertions.assertEquals(12, endpointMetric2.getBytesRead()); + } + + @Test + public void bytesWritten() { + metrics_listen1_server1.bytesWritten(socketMetric_listen1_1, anyRemoteAddr, 1); + metrics_listen1_server2.bytesWritten(socketMetric_listen1_2, anyRemoteAddr, 2); + metrics_listen2_server1.bytesWritten(socketMetric_listen2_1, anyRemoteAddr, 3); + metrics_listen2_server2.bytesWritten(socketMetric_listen2_2, anyRemoteAddr, 4); + metrics_listen2_server2.bytesWritten(socketMetric_listen2_3, anyRemoteAddr, 5); + + Assertions.assertEquals(3, endpointMetric1.getBytesWritten()); + Assertions.assertEquals(12, endpointMetric2.getBytesWritten()); + } +} 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 new file mode 100644 index 00000000000..ccc67374c82 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultVertxMetricsFactory.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.foundation.vertx.metrics; + +import org.junit.Test; + +import io.vertx.core.VertxOptions; +import io.vertx.core.metrics.MetricsOptions; +import io.vertx.core.spi.metrics.VertxMetrics; +import org.junit.jupiter.api.Assertions; + +public class TestDefaultVertxMetricsFactory { + + VertxOptions options = new VertxOptions(); + + DefaultVertxMetricsFactory factory = new DefaultVertxMetricsFactory(); + + @Test + public void metrics() { + MetricsOptions metricsOptions = factory.newOptions(); + options.setMetricsOptions(metricsOptions); + VertxMetrics vertxMetrics = factory.metrics(options); + + Assertions.assertTrue(metricsOptions.isEnabled()); + + Assertions.assertSame(factory.getVertxMetrics(), vertxMetrics); + Assertions.assertTrue(vertxMetrics.isMetricsEnabled()); + } +} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestMetricsOptionsEx.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestMetricsOptionsEx.java new file mode 100644 index 00000000000..ced6ecb2996 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestMetricsOptionsEx.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.foundation.vertx.metrics; + +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestMetricsOptionsEx { + MetricsOptionsEx metricsOptionsEx = new MetricsOptionsEx(); + + @Test + public void interval() { + Assertions.assertEquals(TimeUnit.MINUTES.toMillis(1), + metricsOptionsEx.getCheckClientEndpointMetricIntervalInMilliseconds()); + + metricsOptionsEx.setCheckClientEndpointMetricIntervalInMinute(2); + Assertions.assertEquals(TimeUnit.MINUTES.toMillis(2), + metricsOptionsEx.getCheckClientEndpointMetricIntervalInMilliseconds()); + } + + @Test + public void expired() { + Assertions.assertEquals(TimeUnit.MINUTES.toNanos(15), metricsOptionsEx.getCheckClientEndpointMetricExpiredInNano()); + + metricsOptionsEx.setCheckClientEndpointMetricExpiredInNano(10); + Assertions.assertEquals(10, metricsOptionsEx.getCheckClientEndpointMetricExpiredInNano()); + + metricsOptionsEx.setCheckClientEndpointMetricExpiredInMinute(60); + Assertions.assertEquals(TimeUnit.MINUTES.toNanos(60), metricsOptionsEx.getCheckClientEndpointMetricExpiredInNano()); + } +} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpParser.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpParser.java new file mode 100644 index 00000000000..5a1f7b4af12 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpParser.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.foundation.vertx.server; + +import java.io.UnsupportedEncodingException; + +import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; + +import io.vertx.core.buffer.Buffer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestTcpParser { + long msgId; + + Buffer headerBuffer; + + Buffer bodyBuffer; + + @Test + public void test() throws UnsupportedEncodingException { + TcpBufferHandler output = (_msgId, _headerBuffer, _bodyBuffer) -> { + msgId = _msgId; + headerBuffer = _headerBuffer; + bodyBuffer = _bodyBuffer; + }; + + byte[] header = new byte[] {1, 2, 3}; + byte[] body = new byte[] {1, 2, 3, 4}; + TcpOutputStream os = new TcpOutputStream(1); + os.writeInt(header.length + body.length); + os.writeInt(header.length); + os.write(header); + os.write(body); + + TcpParser parser = new TcpParser(output); + parser.handle(os.getBuffer()); + os.close(); + + Assertions.assertEquals(1, msgId); + Assertions.assertArrayEquals(header, headerBuffer.getBytes()); + Assertions.assertArrayEquals(body, bodyBuffer.getBytes()); + } +} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpServerConnection.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpServerConnection.java new file mode 100644 index 00000000000..ac15a8e6baf --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpServerConnection.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.foundation.vertx.server; + +import io.vertx.core.net.SocketAddress; +import io.vertx.core.net.impl.NetSocketImpl; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestTcpServerConnection { + @Test + public void test() { + SocketAddress socketAddress = Mockito.mock(SocketAddress.class); + NetSocketImpl netSocket = Mockito.mock(NetSocketImpl.class); + Mockito.when(netSocket.remoteAddress()).thenReturn(socketAddress); + TcpServerConnection connection = new TcpServerConnection(); + connection.setProtocol("p"); + connection.setZipName("z"); + + connection.init(netSocket); + + Assertions.assertEquals(netSocket, connection.getNetSocket()); + } +} 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 new file mode 100644 index 00000000000..40be5d48642 --- /dev/null +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestBufferInputStream.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; + +import io.vertx.core.buffer.Buffer; + +public class TestBufferInputStream { + + private BufferInputStream instance; + + @Before + public void setUp() throws Exception { + Buffer buffer = Mockito.mock(Buffer.class); + instance = new BufferInputStream(buffer); + } + + @After + public void tearDown() throws Exception { + instance = null; + } + + @Test + public void testRead() { + Assertions.assertEquals(0, instance.read()); + } + + @Test + public void testReadDecorate() throws IOException { + String text = "abcdefg123456789"; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPOutputStream gzipOutputStream = new GZIPOutputStream(out); + gzipOutputStream.write(text.getBytes()); + gzipOutputStream.close(); + + Buffer buffer = Buffer.buffer(); + buffer.appendBytes(out.toByteArray()); + out.close(); + BufferInputStream bufferInputStream = new BufferInputStream(buffer); + GZIPInputStream gzipInputStream = new GZIPInputStream(bufferInputStream); + StringBuilder sb = new StringBuilder(); + byte[] bufferByte = new byte[256]; + int n; + while ((n = gzipInputStream.read(bufferByte)) >= 0) { + sb.append(new String(bufferByte, 0, n)); + } + gzipInputStream.close(); + + Assertions.assertEquals(text, sb.toString()); + } + + @Test + public void testReadByteArray() { + byte[] b = "csr".getBytes(StandardCharsets.UTF_8); + Assertions.assertEquals(-1, instance.read(b)); + } + + @Test + public void testReadByteArrayIntInt() { + byte[] b = "csr".getBytes(StandardCharsets.UTF_8); + Assertions.assertEquals(-1, instance.read(b, 1, 0)); + } + + @Test + public void testSkip() { + Assertions.assertEquals(0, instance.skip(1)); + } + + @Test + public void testAvailable() { + Assertions.assertEquals(0, instance.available()); + } + + @Test + public void testClose() { + try { + instance.close(); + } catch (Exception e) { + Assertions.fail(); // This assertion is made to fail the test case in case the close() throws exception + } + } + + @Test + public void testBufferInputStream() { + Assertions.assertNotNull(instance); + } + + @Test + public void testReadBoolean() { + Assertions.assertFalse(instance.readBoolean()); + } + + @Test + public void testReadShort() { + Assertions.assertEquals(0, instance.readShort()); + } + + @Test + public void testReadInt() { + Assertions.assertEquals(0, instance.readInt()); + } + + @Test + public void testReadLong() { + Assertions.assertEquals(0, instance.readLong()); + } + + @Test + public void testGetIndex() { + Assertions.assertEquals(0, instance.getIndex()); + } + + @Test + public void testReadString() { + Assertions.assertNotNull(instance.readString()); + } +} diff --git a/foundations/foundation-vertx/src/test/resources/log4j.properties b/foundations/foundation-vertx/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/foundations/foundation-vertx/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/foundations/pom.xml b/foundations/pom.xml index 91ec552096b..7b3f55d334a 100644 --- a/foundations/pom.xml +++ b/foundations/pom.xml @@ -1,11 +1,12 @@ - + 4.0.0 - io.servicecomb + org.apache.servicecomb java-chassis-parent - 0.3.0-SNAPSHOT - ../parent + 3.4.0-SNAPSHOT + ../parents/default foundations + Java Chassis::Foundations pom - foundation-vertx - foundation-common - foundation-config - foundation-metrics - foundation-ssl + foundation-spi + foundation-vertx + foundation-common + foundation-config + foundation-metrics + foundation-ssl + foundation-test-scaffolding + foundation-protobuf + foundation-registry - \ No newline at end of file + diff --git a/governance/pom.xml b/governance/pom.xml new file mode 100644 index 00000000000..3f78c36ade5 --- /dev/null +++ b/governance/pom.xml @@ -0,0 +1,100 @@ + + + + + + org.apache.servicecomb + java-chassis-parent + 3.4.0-SNAPSHOT + ../parents/default + + 4.0.0 + + servicecomb-governance + Java Chassis::Governance + + + + org.apache.servicecomb + foundation-common + + + io.github.resilience4j + resilience4j-all + + + io.github.resilience4j + resilience4j-micrometer + + + io.micrometer + micrometer-core + + + org.apache.commons + commons-lang3 + + + org.springframework + spring-beans + + + org.springframework + spring-context + + + org.yaml + snakeyaml + + + com.google.guava + guava + + + com.google.guava + failureaccess + test + + + org.springframework + spring-test + test + + + org.springframework.boot + spring-boot-test + test + + + io.micrometer + micrometer-registry-prometheus + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + diff --git a/governance/src/main/java/org/apache/servicecomb/governance/GovernanceCommonConfiguration.java b/governance/src/main/java/org/apache/servicecomb/governance/GovernanceCommonConfiguration.java new file mode 100644 index 00000000000..b5fd042a723 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/GovernanceCommonConfiguration.java @@ -0,0 +1,240 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.util.Map; + +import org.apache.servicecomb.governance.handler.BulkheadHandler; +import org.apache.servicecomb.governance.handler.CircuitBreakerHandler; +import org.apache.servicecomb.governance.handler.FaultInjectionHandler; +import org.apache.servicecomb.governance.handler.GovernanceCacheHandler; +import org.apache.servicecomb.governance.handler.IdentifierRateLimitingHandler; +import org.apache.servicecomb.governance.handler.InstanceBulkheadHandler; +import org.apache.servicecomb.governance.handler.InstanceIsolationHandler; +import org.apache.servicecomb.governance.handler.LoadBalanceHandler; +import org.apache.servicecomb.governance.handler.MapperHandler; +import org.apache.servicecomb.governance.handler.RateLimitingHandler; +import org.apache.servicecomb.governance.handler.RetryHandler; +import org.apache.servicecomb.governance.handler.TimeLimiterHandler; +import org.apache.servicecomb.governance.handler.ext.AbstractCircuitBreakerExtension; +import org.apache.servicecomb.governance.handler.ext.AbstractInstanceIsolationExtension; +import org.apache.servicecomb.governance.handler.ext.AbstractRetryExtension; +import org.apache.servicecomb.governance.marker.RequestProcessor; +import org.apache.servicecomb.governance.marker.operator.CompareOperator; +import org.apache.servicecomb.governance.marker.operator.ContainsOperator; +import org.apache.servicecomb.governance.marker.operator.ExactOperator; +import org.apache.servicecomb.governance.marker.operator.MatchOperator; +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.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; +import org.apache.servicecomb.governance.properties.LoadBalanceProperties; +import org.apache.servicecomb.governance.properties.MapperProperties; +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; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.micrometer.core.instrument.MeterRegistry; + +@Configuration +public class GovernanceCommonConfiguration { + // properties configuration + @Bean + public BulkheadProperties scbBulkheadProperties() { + return new BulkheadProperties(); + } + + @Bean + public InstanceBulkheadProperties scbInstanceBulkheadProperties() { + return new InstanceBulkheadProperties(); + } + + @Bean + public CircuitBreakerProperties scbCircuitBreakerProperties() { + return new CircuitBreakerProperties(); + } + + @Bean + public InstanceIsolationProperties scbInstanceIsolationProperties() { + return new InstanceIsolationProperties(); + } + + @Bean + public MatchProperties scbMatchProperties() { + return new MatchProperties(); + } + + @Bean + public RateLimitProperties scbRateLimitProperties() { + return new RateLimitProperties(); + } + + @Bean + public IdentifierRateLimitProperties scbIdentifierRateLimitProperties() { + return new IdentifierRateLimitProperties(); + } + + @Bean + public RetryProperties scbRetryProperties() { + return new RetryProperties(); + } + + @Bean + public TimeLimiterProperties scbTimeLimiterProperties() { + return new TimeLimiterProperties(); + } + + @Bean + public GovernanceCacheProperties scbCacheProperties() { + return new GovernanceCacheProperties(); + } + + @Bean + public FaultInjectionProperties scbFaultInjectionProperties() { + return new FaultInjectionProperties(); + } + + @Bean + public LoadBalanceProperties scbLoadBalanceProperties() { + return new LoadBalanceProperties(); + } + + @Bean + public MapperProperties scbMapperProperties() { + return new MapperProperties(); + } + + // handlers configuration + @Bean + public BulkheadHandler scbBulkheadHandler(BulkheadProperties scbBulkheadProperties) { + return new BulkheadHandler(scbBulkheadProperties); + } + + @Bean + public InstanceBulkheadHandler scbInstanceBulkheadHandler(InstanceBulkheadProperties scbInstanceBulkheadProperties) { + return new InstanceBulkheadHandler(scbInstanceBulkheadProperties); + } + + @Bean + public LoadBalanceHandler scbLoadBalanceHandler(LoadBalanceProperties loadBalanceProperties) { + return new LoadBalanceHandler(loadBalanceProperties); + } + + @Bean + public CircuitBreakerHandler scbCircuitBreakerHandler(CircuitBreakerProperties scbCircuitBreakerProperties, + AbstractCircuitBreakerExtension circuitBreakerExtension) { + return new CircuitBreakerHandler(scbCircuitBreakerProperties, circuitBreakerExtension); + } + + @Bean + public InstanceIsolationHandler scbInstanceIsolationHandler(InstanceIsolationProperties scbInstanceIsolationProperties, + AbstractInstanceIsolationExtension isolationExtension, + ObjectProvider meterRegistry) { + return new InstanceIsolationHandler(scbInstanceIsolationProperties, isolationExtension, meterRegistry); + } + + @Bean + public RateLimitingHandler scbRateLimitingHandler(RateLimitProperties rateLimitProperties) { + return new RateLimitingHandler(rateLimitProperties); + } + + @Bean + public IdentifierRateLimitingHandler scbIdentifierRateLimitingHandler( + IdentifierRateLimitProperties identifierRateLimitProperties) { + return new IdentifierRateLimitingHandler(identifierRateLimitProperties); + } + + @Bean + public RetryHandler scbRetryHandler(RetryProperties retryProperties, AbstractRetryExtension retryExtension) { + return new RetryHandler(retryProperties, retryExtension); + } + + @Bean + public TimeLimiterHandler scbTimeLimiterHandler(TimeLimiterProperties timeLimiterProperties) { + return new TimeLimiterHandler(timeLimiterProperties); + } + + @Bean + public GovernanceCacheHandler scbGovernanceCacheHandler(GovernanceCacheProperties cacheProperties) { + return new GovernanceCacheHandler(cacheProperties); + } + + @Bean + public FaultInjectionHandler scbFaultInjectionHandler(FaultInjectionProperties scbFaultInjectionProperties) { + return new FaultInjectionHandler(scbFaultInjectionProperties); + } + + @Bean + public MapperHandler scbMapperHandler(MapperProperties scbMapperProperties) { + return new MapperHandler(scbMapperProperties); + } + + // request processor + @Bean + public RequestProcessor scbRequestProcessor(Map operatorMap) { + return new RequestProcessor(operatorMap); + } + + // matchers + @Bean + public MatchersService scbMatchersService(RequestProcessor requestProcessor, MatchProperties scbMatchProperties) { + return new MatchersServiceImpl(requestProcessor, scbMatchProperties); + } + + @Bean + public MatchersManager scbMatchersManager(MatchersService matchersService) { + return new MatchersManager(matchersService); + } + + // operators + @Bean + public CompareOperator scbCompareOperator() { + return new CompareOperator(); + } + + @Bean + public ContainsOperator scbContainsOperator() { + return new ContainsOperator(); + } + + @Bean + public ExactOperator scbExactOperator() { + return new ExactOperator(); + } + + @Bean + public PrefixOperator scbPrefixOperator() { + return new PrefixOperator(); + } + + @Bean + public SuffixOperator scbSuffixOperator() { + return new SuffixOperator(); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/MatchersManager.java b/governance/src/main/java/org/apache/servicecomb/governance/MatchersManager.java new file mode 100644 index 00000000000..00caaafdbff --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/MatchersManager.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.governance; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.AbstractPolicy; +import org.apache.servicecomb.governance.service.MatchersService; + +public class MatchersManager { + private MatchersService matchersService; + + public MatchersManager(MatchersService matchersService) { + this.matchersService = matchersService; + } + + public T match(GovernanceRequestExtractor requestExtractor, Map policies) { + List sortPolicies = new ArrayList<>(policies.size()); + sortPolicies.addAll(policies.values()); + sortPolicies.sort(T::compareTo); + + for (T policy : sortPolicies) { + if (matchersService.checkMatch(requestExtractor, policy.getName())) { + return policy; + } + } + return null; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/MicroserviceMeta.java b/governance/src/main/java/org/apache/servicecomb/governance/MicroserviceMeta.java new file mode 100644 index 00000000000..4aac6e6d813 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/MicroserviceMeta.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.governance; + +public interface MicroserviceMeta { + String getName(); + + String getVersion(); +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/entity/Configurable.java b/governance/src/main/java/org/apache/servicecomb/governance/entity/Configurable.java new file mode 100644 index 00000000000..ad15a84ac0c --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/entity/Configurable.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.governance.entity; + +/** + * Indicates a object can be configure in configuration file or config center. + */ +public abstract class Configurable { + protected String name; + + protected String services; + + public abstract boolean isValid(); + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getServices() { + return this.services; + } + + public void setServices(String services) { + this.services = services; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/event/GovernanceConfigurationChangedEvent.java b/governance/src/main/java/org/apache/servicecomb/governance/event/GovernanceConfigurationChangedEvent.java new file mode 100644 index 00000000000..bce4ec729e7 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/event/GovernanceConfigurationChangedEvent.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.governance.event; + +import java.util.Set; + +public class GovernanceConfigurationChangedEvent { + private final Set changedConfigurations; + + public GovernanceConfigurationChangedEvent(Set changedConfigurations) { + this.changedConfigurations = changedConfigurations; + } + + public Set getChangedConfigurations() { + return changedConfigurations; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/event/GovernanceEventManager.java b/governance/src/main/java/org/apache/servicecomb/governance/event/GovernanceEventManager.java new file mode 100644 index 00000000000..a89103174a2 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/event/GovernanceEventManager.java @@ -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. + */ + +package org.apache.servicecomb.governance.event; + +import com.google.common.eventbus.EventBus; + +public class GovernanceEventManager { + private static final EventBus eventBus = new EventBus(); + + public static EventBus getEventBus() { + return eventBus; + } + + public static void post(Object event) { + eventBus.post(event); + } + + public static void register(Object subscriber) { + eventBus.register(subscriber); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/exception/IllegalArgsOperatorException.java b/governance/src/main/java/org/apache/servicecomb/governance/exception/IllegalArgsOperatorException.java new file mode 100644 index 00000000000..a00502b33b5 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/exception/IllegalArgsOperatorException.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.governance.exception; + +public class IllegalArgsOperatorException extends RuntimeException { + private static final long serialVersionUID = 793575987576638624L; + + public IllegalArgsOperatorException(String message) { + super(message); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/AbstractGovernanceHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/AbstractGovernanceHandler.java new file mode 100644 index 00000000000..d28392e413c --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/AbstractGovernanceHandler.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import org.apache.servicecomb.governance.MatchersManager; +import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; +import org.apache.servicecomb.governance.event.GovernanceEventManager; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.AbstractPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import com.google.common.eventbus.Subscribe; + +import io.micrometer.core.instrument.MeterRegistry; + +public abstract class AbstractGovernanceHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGovernanceHandler.class); + + protected final DisposableMap processors; + + private final Object lock = new Object(); + + protected MatchersManager matchersManager; + + protected MeterRegistry meterRegistry; + + protected AbstractGovernanceHandler() { + GovernanceEventManager.register(this); + processors = new DisposableMap<>(this::onConfigurationChanged); + } + + @Autowired + public void setMatchersManager(MatchersManager matchersManager) { + this.matchersManager = matchersManager; + } + + @Autowired(required = false) + public void setMeterRegistry(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + public PROCESSOR getActuator(GovernanceRequestExtractor requestExtractor) { + POLICY policy = matchPolicy(requestExtractor); + if (policy == null) { + return null; + } + + String key = createKey(requestExtractor, policy); + if (key == null) { + return null; + } + + Disposable processor = processors.get(key); + if (processor == null) { + synchronized (lock) { + processor = processors.get(key); + if (processor == null) { + processor = createProcessor(key, requestExtractor, policy); + processors.put(key, processor); + } + } + } + + return processor.get(); + } + + protected abstract String createKey(GovernanceRequestExtractor requestExtractor, POLICY policy); + + protected abstract POLICY matchPolicy(GovernanceRequestExtractor requestExtractor); + + protected abstract Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, + POLICY policy); + + protected void onConfigurationChanged(String key) { + Disposable processor = processors.remove(key); + if (processor != null) { + LOGGER.info("remove governance processor {}", key); + processor.dispose(); + } + } + + @Subscribe + public void onDynamicConfigurationListener(GovernanceConfigurationChangedEvent event) { + event.getChangedConfigurations().forEach(this::onConfigurationChanged); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/BulkheadHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/BulkheadHandler.java new file mode 100644 index 00000000000..12d9ddc4eb2 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/BulkheadHandler.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import java.time.Duration; + +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.BulkheadPolicy; +import org.apache.servicecomb.governance.properties.BulkheadProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.github.resilience4j.bulkhead.Bulkhead; +import io.github.resilience4j.bulkhead.BulkheadConfig; +import io.github.resilience4j.bulkhead.BulkheadRegistry; +import io.github.resilience4j.micrometer.tagged.BulkheadMetricNames; +import io.github.resilience4j.micrometer.tagged.TaggedBulkheadMetrics; + +public class BulkheadHandler extends AbstractGovernanceHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(BulkheadHandler.class); + + private final BulkheadProperties bulkheadProperties; + + public BulkheadHandler(BulkheadProperties bulkheadProperties) { + this.bulkheadProperties = bulkheadProperties; + } + + @Override + protected String createKey(GovernanceRequestExtractor requestExtractor, BulkheadPolicy policy) { + return bulkheadProperties.getConfigKey() + "." + policy.getName(); + } + + @Override + public BulkheadPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { + return matchersManager.match(requestExtractor, bulkheadProperties.getParsedEntity()); + } + + @Override + public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, BulkheadPolicy policy) { + return getBulkhead(key, policy); + } + + private Disposable getBulkhead(String key, BulkheadPolicy policy) { + LOGGER.info("applying new policy {} for {}", key, policy.toString()); + + BulkheadConfig config = BulkheadConfig.custom() + .maxConcurrentCalls(policy.getMaxConcurrentCalls()) + .maxWaitDuration(Duration.parse(policy.getMaxWaitDuration())) + .build(); + + BulkheadRegistry registry = BulkheadRegistry.of(config); + if (meterRegistry != null) { + TaggedBulkheadMetrics + .ofBulkheadRegistry(BulkheadMetricNames.custom() + .availableConcurrentCallsMetricName(bulkheadProperties.getConfigKey() + ".available.concurrent.calls") + .maxAllowedConcurrentCallsMetricName( + bulkheadProperties.getConfigKey() + ".max.allowed.concurrent.calls").build(), registry) + .bindTo(meterRegistry); + } + return new DisposableBulkhead(key, registry, registry.bulkhead(key)); + } +} 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 new file mode 100644 index 00000000000..39082661b24 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import java.time.Duration; + +import org.apache.servicecomb.governance.handler.ext.AbstractCircuitBreakerExtension; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; +import org.apache.servicecomb.governance.properties.CircuitBreakerProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import io.github.resilience4j.micrometer.tagged.CircuitBreakerMetricNames; +import io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics; + +public class CircuitBreakerHandler extends AbstractGovernanceHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(CircuitBreakerHandler.class); + + private final CircuitBreakerProperties circuitBreakerProperties; + + private final AbstractCircuitBreakerExtension circuitBreakerExtension; + + public CircuitBreakerHandler(CircuitBreakerProperties circuitBreakerProperties, + AbstractCircuitBreakerExtension circuitBreakerExtension) { + this.circuitBreakerProperties = circuitBreakerProperties; + this.circuitBreakerExtension = circuitBreakerExtension; + } + + @Override + protected String createKey(GovernanceRequestExtractor requestExtractor, CircuitBreakerPolicy policy) { + return this.circuitBreakerProperties.getConfigKey() + "." + policy.getName(); + } + + @Override + public CircuitBreakerPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { + return matchersManager.match(requestExtractor, circuitBreakerProperties.getParsedEntity()); + } + + @Override + public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, + CircuitBreakerPolicy policy) { + return getCircuitBreaker(key, policy); + } + + private Disposable getCircuitBreaker(String key, CircuitBreakerPolicy policy) { + LOGGER.info("applying new policy {} for {}", key, policy); + + CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() + .failureRateThreshold(policy.getFailureRateThreshold()) + .slowCallRateThreshold(policy.getSlowCallRateThreshold()) + .waitDurationInOpenState(Duration.parse(policy.getWaitDurationInOpenState())) + .slowCallDurationThreshold(Duration.parse(policy.getSlowCallDurationThreshold())) + .permittedNumberOfCallsInHalfOpenState(policy.getPermittedNumberOfCallsInHalfOpenState()) + .minimumNumberOfCalls(policy.getMinimumNumberOfCalls()) + .slidingWindowType(policy.getSlidingWindowTypeEnum()) + .slidingWindowSize(Integer.parseInt(policy.getSlidingWindowSize())) + .recordException(e -> circuitBreakerExtension.isFailedResult(policy.getRecordFailureStatus(), e)) + .recordResult(r -> circuitBreakerExtension.isFailedResult(policy.getRecordFailureStatus(), r)) + .build(); + CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig); + if (meterRegistry != null) { + TaggedCircuitBreakerMetrics + .ofCircuitBreakerRegistry(CircuitBreakerMetricNames.custom() + .callsMetricName(this.circuitBreakerProperties.getConfigKey() + ".calls") + .notPermittedCallsMetricName(this.circuitBreakerProperties.getConfigKey() + ".not.permitted.calls") + .stateMetricName(this.circuitBreakerProperties.getConfigKey() + ".state") + .bufferedCallsMetricName(this.circuitBreakerProperties.getConfigKey() + ".buffered.calls") + .slowCallsMetricName(this.circuitBreakerProperties.getConfigKey() + ".slow.calls") + .failureRateMetricName(this.circuitBreakerProperties.getConfigKey() + ".failure.rate") + .slowCallRateMetricName(this.circuitBreakerProperties.getConfigKey() + ".slow.call.rate") + .build(), circuitBreakerRegistry) + .bindTo(meterRegistry); + } + return new DisposableCircuitBreaker(key, circuitBreakerRegistry, + circuitBreakerRegistry.circuitBreaker(key, circuitBreakerConfig)); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/Disposable.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/Disposable.java new file mode 100644 index 00000000000..8f6e68909da --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/Disposable.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.governance.handler; + +public abstract class Disposable { + private long lastAccessed; + + protected Disposable() { + lastAccessed = System.currentTimeMillis(); + } + + public abstract void dispose(); + + public V get() { + lastAccessed = System.currentTimeMillis(); + return getValue(); + } + + public abstract V getValue(); + + public abstract String getKey(); + + public long lastAccessed() { + return lastAccessed; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableBulkhead.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableBulkhead.java new file mode 100644 index 00000000000..41f7ccc5212 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableBulkhead.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.governance.handler; + +import io.github.resilience4j.bulkhead.Bulkhead; +import io.github.resilience4j.bulkhead.BulkheadRegistry; + +public class DisposableBulkhead extends Disposable { + private final String key; + + private final BulkheadRegistry bulkheadRegistry; + + private final Bulkhead bulkhead; + + public DisposableBulkhead(String key, BulkheadRegistry bulkheadRegistry, + Bulkhead bulkhead) { + this.key = key; + this.bulkheadRegistry = bulkheadRegistry; + this.bulkhead = bulkhead; + } + + @Override + public void dispose() { + bulkheadRegistry.remove(key); + } + + @Override + public Bulkhead getValue() { + return bulkhead; + } + + @Override + public String getKey() { + return key; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableCircuitBreaker.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableCircuitBreaker.java new file mode 100644 index 00000000000..d85292ee593 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableCircuitBreaker.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.governance.handler; + +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; + +public class DisposableCircuitBreaker extends Disposable { + private final CircuitBreaker circuitBreaker; + + private final CircuitBreakerRegistry circuitBreakerRegistry; + + private final String key; + + public DisposableCircuitBreaker(String key, CircuitBreakerRegistry registry, CircuitBreaker circuitBreaker) { + this.key = key; + this.circuitBreaker = circuitBreaker; + this.circuitBreakerRegistry = registry; + } + + @Override + public void dispose() { + this.circuitBreakerRegistry.remove(key); + } + + @Override + public CircuitBreaker getValue() { + return circuitBreaker; + } + + @Override + public String getKey() { + return key; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableHolder.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableHolder.java new file mode 100644 index 00000000000..9108df2a91a --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableHolder.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.governance.handler; + +public class DisposableHolder extends Disposable { + private final E e; + + private final String key; + + public DisposableHolder(String key, E e) { + this.key = key; + this.e = e; + } + + @Override + public void dispose() { + + } + + @Override + public E getValue() { + return e; + } + + @Override + public String getKey() { + return key; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableMap.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableMap.java new file mode 100644 index 00000000000..d5d78668fe0 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableMap.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.governance.handler; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +/** + * check if some items are expired when put new item to map and remove expired items. + */ +public class DisposableMap extends ConcurrentHashMap> { + private static final long serialVersionUID = 7249069246763182397L; + + public interface RemoveListener { + void onRemoveEntry(String k); + } + + private static final int EXPIRE_TIME = 10 * 60 * 1000; + + private final RemoveListener listener; + + public DisposableMap(RemoveListener listener) { + this.listener = listener; + } + + @Override + public Disposable put(String key, Disposable value) { + Disposable result = super.put(key, value); + + checkExpired(); + + return result; + } + + private void checkExpired() { + List expired = new ArrayList<>(); + this.values().forEach(v -> { + if (System.currentTimeMillis() - v.lastAccessed() >= EXPIRE_TIME) { + expired.add(v.getKey()); + } + }); + expired.forEach(listener::onRemoveEntry); + } + + @Override + public Disposable get(Object key) { + return super.get(key); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableRateLimiter.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableRateLimiter.java new file mode 100644 index 00000000000..a078cb58215 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableRateLimiter.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.governance.handler; + +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RateLimiterRegistry; + +public class DisposableRateLimiter extends Disposable { + private final String key; + + private final RateLimiter rateLimiter; + + private final RateLimiterRegistry registry; + + public DisposableRateLimiter(String key, RateLimiter rateLimiter, + RateLimiterRegistry registry) { + this.key = key; + this.rateLimiter = rateLimiter; + this.registry = registry; + } + + @Override + public void dispose() { + registry.remove(key); + } + + @Override + public RateLimiter getValue() { + return rateLimiter; + } + + @Override + public String getKey() { + return key; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableRetry.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableRetry.java new file mode 100644 index 00000000000..c7e7be86b6f --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableRetry.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.governance.handler; + +import io.github.resilience4j.retry.Retry; +import io.github.resilience4j.retry.RetryRegistry; + +public class DisposableRetry extends Disposable { + private final String key; + + private final RetryRegistry registry; + + private final Retry retry; + + public DisposableRetry(String key, RetryRegistry registry, Retry retry) { + this.key = key; + this.registry = registry; + this.retry = retry; + } + + @Override + public void dispose() { + registry.remove(key); + } + + @Override + public Retry getValue() { + return retry; + } + + @Override + public String getKey() { + return key; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableTimeLimiter.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableTimeLimiter.java new file mode 100644 index 00000000000..4a23b72d69c --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/DisposableTimeLimiter.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.governance.handler; + +import io.github.resilience4j.timelimiter.TimeLimiter; +import io.github.resilience4j.timelimiter.TimeLimiterRegistry; + +public class DisposableTimeLimiter extends Disposable { + private final String key; + + private final TimeLimiterRegistry timeLimiterRegistry; + + private final TimeLimiter timeLimiter; + + public DisposableTimeLimiter(String key, TimeLimiterRegistry registry, TimeLimiter timeLimiter) { + this.key = key; + this.timeLimiterRegistry = registry; + this.timeLimiter = timeLimiter; + } + + @Override + public void dispose() { + timeLimiterRegistry.remove(key); + } + + @Override + public TimeLimiter getValue() { + return timeLimiter; + } + + @Override + public String getKey() { + return key; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/FaultInjectionHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/FaultInjectionHandler.java new file mode 100644 index 00000000000..e44b1685fe5 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/FaultInjectionHandler.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.governance.handler; + +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; +import org.apache.servicecomb.governance.processor.injection.Fault; +import org.apache.servicecomb.governance.processor.injection.FaultInjectionUtil; +import org.apache.servicecomb.governance.properties.FaultInjectionProperties; + +public class FaultInjectionHandler extends AbstractGovernanceHandler { + + private final FaultInjectionProperties faultInjectionProperties; + + public FaultInjectionHandler(FaultInjectionProperties faultInjectionProperties) { + this.faultInjectionProperties = faultInjectionProperties; + } + + @Override + protected String createKey(GovernanceRequestExtractor requestExtractor, FaultInjectionPolicy policy) { + return FaultInjectionProperties.MATCH_FAULT_INJECTION_KEY + "." + policy.getName(); + } + + @Override + public FaultInjectionPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { + return matchersManager.match(requestExtractor, faultInjectionProperties.getParsedEntity()); + } + + @Override + protected DisposableHolder createProcessor(String key, GovernanceRequestExtractor requestExtractor, + FaultInjectionPolicy policy) { + return new DisposableHolder<>(key, FaultInjectionUtil.getFault(key, policy)); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/GovernanceCacheHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/GovernanceCacheHandler.java new file mode 100644 index 00000000000..7e7b6e6dd72 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/GovernanceCacheHandler.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.governance.handler; + +import java.time.Duration; + +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.GovernanceCachePolicy; +import org.apache.servicecomb.governance.properties.GovernanceCacheProperties; +import org.apache.servicecomb.governance.service.GovernanceCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +public class GovernanceCacheHandler + extends AbstractGovernanceHandler, GovernanceCachePolicy> { + private static final Logger LOGGER = LoggerFactory.getLogger(GovernanceCacheHandler.class); + + private final GovernanceCacheProperties cacheProperties; + + public GovernanceCacheHandler(GovernanceCacheProperties cacheProperties) { + this.cacheProperties = cacheProperties; + } + + @Override + public GovernanceCachePolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { + return matchersManager.match(requestExtractor, cacheProperties.getParsedEntity()); + } + + @Override + protected String createKey(GovernanceRequestExtractor requestExtractor, GovernanceCachePolicy policy) { + return cacheProperties.getConfigKey() + "." + policy.getName(); + } + + @Override + protected Disposable> createProcessor(String key, GovernanceRequestExtractor requestExtractor, + GovernanceCachePolicy policy) { + return getGovernanceCache(key, policy); + } + + protected Disposable> getGovernanceCache(String key, GovernanceCachePolicy policy) { + LOGGER.info("applying new policy {} for {}", key, policy.toString()); + Cache cache = CacheBuilder.newBuilder() + .expireAfterWrite(Duration.parse(policy.getTtl())) + .maximumSize(policy.getMaximumSize()) + .concurrencyLevel(policy.getConcurrencyLevel()) + .build(); + GovernanceCache governanceCache = GovernanceCache.of(cache); + return new DisposableHolder<>(key, governanceCache); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/IdentifierRateLimitingHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/IdentifierRateLimitingHandler.java new file mode 100644 index 00000000000..26751c64dfa --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/IdentifierRateLimitingHandler.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.governance.handler; + +import java.time.Duration; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.IdentifierRateLimitingPolicy; +import org.apache.servicecomb.governance.properties.IdentifierRateLimitProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.github.resilience4j.micrometer.tagged.RateLimiterMetricNames; +import io.github.resilience4j.micrometer.tagged.TaggedRateLimiterMetrics; +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RateLimiterConfig; +import io.github.resilience4j.ratelimiter.RateLimiterRegistry; + +public class IdentifierRateLimitingHandler extends + AbstractGovernanceHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(IdentifierRateLimitingHandler.class); + + private final IdentifierRateLimitProperties rateLimitProperties; + + public IdentifierRateLimitingHandler(IdentifierRateLimitProperties rateLimitProperties) { + this.rateLimitProperties = rateLimitProperties; + } + + @Override + protected String createKey(GovernanceRequestExtractor requestExtractor, IdentifierRateLimitingPolicy policy) { + if (StringUtils.isEmpty(policy.getIdentifier()) || + StringUtils.isEmpty(requestExtractor.header(policy.getIdentifier()))) { + LOGGER.info("identifier rate limiting is not properly configured, identifier is empty."); + return null; + } + return this.rateLimitProperties.getConfigKey() + + "." + policy.getName() + + "." + requestExtractor.header(policy.getIdentifier()); + } + + @Override + protected void onConfigurationChanged(String key) { + if (key.startsWith(this.rateLimitProperties.getConfigKey())) { + for (String processorKey : processors.keySet()) { + if (processorKey.startsWith(key)) { + Disposable processor = processors.remove(processorKey); + if (processor != null) { + LOGGER.info("remove identifier rate limiting processor {}", key); + processor.dispose(); + } + } + } + } + } + + @Override + public IdentifierRateLimitingPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { + return matchersManager.match(requestExtractor, rateLimitProperties.getParsedEntity()); + } + + @Override + public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, + IdentifierRateLimitingPolicy policy) { + return getRateLimiter(key, policy); + } + + private Disposable getRateLimiter(String key, IdentifierRateLimitingPolicy policy) { + LOGGER.info("applying new policy {} for {}", key, policy.toString()); + + RateLimiterConfig config = RateLimiterConfig.custom() + .limitForPeriod(policy.getRate()) + .limitRefreshPeriod(Duration.parse(policy.getLimitRefreshPeriod())) + .timeoutDuration(Duration.parse(policy.getTimeoutDuration())) + .build(); + RateLimiterRegistry rateLimiterRegistry = RateLimiterRegistry.of(config); + if (meterRegistry != null) { + TaggedRateLimiterMetrics + .ofRateLimiterRegistry(RateLimiterMetricNames.custom() + .availablePermissionsMetricName( + this.rateLimitProperties.getConfigKey() + ".available.permissions") + .waitingThreadsMetricName(this.rateLimitProperties.getConfigKey() + ".waiting.threads") + .build(), + rateLimiterRegistry) + .bindTo(meterRegistry); + } + return new DisposableRateLimiter(key, rateLimiterRegistry.rateLimiter(key), rateLimiterRegistry); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceBulkheadHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceBulkheadHandler.java new file mode 100644 index 00000000000..e90f2472915 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceBulkheadHandler.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import java.time.Duration; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.BulkheadPolicy; +import org.apache.servicecomb.governance.properties.InstanceBulkheadProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.github.resilience4j.bulkhead.Bulkhead; +import io.github.resilience4j.bulkhead.BulkheadConfig; +import io.github.resilience4j.bulkhead.BulkheadRegistry; +import io.github.resilience4j.micrometer.tagged.BulkheadMetricNames; +import io.github.resilience4j.micrometer.tagged.TaggedBulkheadMetrics; + +public class InstanceBulkheadHandler extends AbstractGovernanceHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(InstanceBulkheadHandler.class); + + private final InstanceBulkheadProperties bulkheadProperties; + + public InstanceBulkheadHandler(InstanceBulkheadProperties bulkheadProperties) { + this.bulkheadProperties = bulkheadProperties; + } + + @Override + protected String createKey(GovernanceRequestExtractor requestExtractor, BulkheadPolicy policy) { + return this.bulkheadProperties.getConfigKey() + + "." + policy.getName() + + "." + requestExtractor.serviceName() + + "." + requestExtractor.instanceId(); + } + + @Override + protected void onConfigurationChanged(String key) { + if (key.startsWith(this.bulkheadProperties.getConfigKey())) { + for (String processorKey : processors.keySet()) { + if (processorKey.startsWith(key)) { + Disposable processor = processors.remove(processorKey); + if (processor != null) { + LOGGER.info("remove instance bulkhead processor {}", key); + processor.dispose(); + } + } + } + } + } + + @Override + public BulkheadPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { + if (StringUtils.isEmpty(requestExtractor.serviceName()) || StringUtils.isEmpty( + requestExtractor.instanceId())) { + LOGGER.info("Instance bulkhead is not properly configured, service id or instance id is empty."); + return null; + } + return matchersManager.match(requestExtractor, bulkheadProperties.getParsedEntity()); + } + + @Override + public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, BulkheadPolicy policy) { + return getBulkhead(key, policy); + } + + private Disposable getBulkhead(String key, BulkheadPolicy policy) { + LOGGER.info("applying new policy {} for {}", key, policy.toString()); + + BulkheadConfig config = BulkheadConfig.custom() + .maxConcurrentCalls(policy.getMaxConcurrentCalls()) + .maxWaitDuration(Duration.parse(policy.getMaxWaitDuration())) + .build(); + + BulkheadRegistry registry = BulkheadRegistry.of(config); + if (meterRegistry != null) { + TaggedBulkheadMetrics + .ofBulkheadRegistry(BulkheadMetricNames.custom() + .availableConcurrentCallsMetricName( + this.bulkheadProperties.getConfigKey() + ".available.concurrent.calls") + .maxAllowedConcurrentCallsMetricName( + this.bulkheadProperties.getConfigKey() + ".max.allowed.concurrent.calls").build(), + registry) + .bindTo(meterRegistry); + } + return new DisposableBulkhead(key, registry, registry.bulkhead(key, config)); + } +} 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 new file mode 100644 index 00000000000..894dd170e99 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import java.time.Duration; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.governance.handler.ext.AbstractInstanceIsolationExtension; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; +import org.apache.servicecomb.governance.properties.InstanceIsolationProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.ObjectProvider; + +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import io.github.resilience4j.micrometer.tagged.CircuitBreakerMetricNames; +import io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics; +import io.micrometer.core.instrument.MeterRegistry; + +public final class InstanceIsolationHandler extends AbstractGovernanceHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(InstanceIsolationHandler.class); + + private final InstanceIsolationProperties instanceIsolationProperties; + + private final AbstractInstanceIsolationExtension isolationExtension; + + private final MeterRegistry meterRegistry; + + public InstanceIsolationHandler(InstanceIsolationProperties instanceIsolationProperties, + AbstractInstanceIsolationExtension isolationExtension, + ObjectProvider meterRegistry) { + this.instanceIsolationProperties = instanceIsolationProperties; + this.isolationExtension = isolationExtension; + this.meterRegistry = meterRegistry.getIfAvailable(); + } + + @Override + protected String createKey(GovernanceRequestExtractor requestExtractor, CircuitBreakerPolicy policy) { + return this.instanceIsolationProperties.getConfigKey() + + "." + policy.getName() + + "." + requestExtractor.serviceName() + + "." + requestExtractor.instanceId(); + } + + @Override + protected void onConfigurationChanged(String key) { + if (key.startsWith(this.instanceIsolationProperties.getConfigKey())) { + for (String processorKey : processors.keySet()) { + if (processorKey.startsWith(key)) { + Disposable circuitBreaker = processors.remove(processorKey); + if (circuitBreaker != null) { + LOGGER.info("remove instance isolation processor {}", key); + circuitBreaker.dispose(); + } + } + } + } + } + + @Override + public CircuitBreakerPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { + if (StringUtils.isEmpty(requestExtractor.serviceName()) || StringUtils.isEmpty( + requestExtractor.instanceId())) { + LOGGER.debug("Isolation is not properly configured, service id or instance id is empty."); + return null; + } + return matchersManager.match(requestExtractor, instanceIsolationProperties.getParsedEntity()); + } + + @Override + public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, + CircuitBreakerPolicy policy) { + return getCircuitBreaker(key, policy); + } + + private Disposable getCircuitBreaker(String key, CircuitBreakerPolicy policy) { + LOGGER.info("applying new policy {} for {}", key, policy.toString()); + + CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() + .failureRateThreshold(policy.getFailureRateThreshold()) + .slowCallRateThreshold(policy.getSlowCallRateThreshold()) + .waitDurationInOpenState(Duration.parse(policy.getWaitDurationInOpenState())) + .slowCallDurationThreshold(Duration.parse(policy.getSlowCallDurationThreshold())) + .permittedNumberOfCallsInHalfOpenState(policy.getPermittedNumberOfCallsInHalfOpenState()) + .minimumNumberOfCalls(policy.getMinimumNumberOfCalls()) + .slidingWindowType(policy.getSlidingWindowTypeEnum()) + .slidingWindowSize(Integer.parseInt(policy.getSlidingWindowSize())) + .recordException(e -> isolationExtension.isFailedResult(policy.getRecordFailureStatus(), e)) + .recordResult(r -> isolationExtension.isFailedResult(policy.getRecordFailureStatus(), r)) + .build(); + CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig); + if (meterRegistry != null) { + TaggedCircuitBreakerMetrics + .ofCircuitBreakerRegistry(CircuitBreakerMetricNames.custom() + .callsMetricName(this.instanceIsolationProperties.getConfigKey() + ".calls") + .notPermittedCallsMetricName( + this.instanceIsolationProperties.getConfigKey() + ".not.permitted.calls") + .stateMetricName(this.instanceIsolationProperties.getConfigKey() + ".state") + .bufferedCallsMetricName(this.instanceIsolationProperties.getConfigKey() + ".buffered.calls") + .slowCallsMetricName(this.instanceIsolationProperties.getConfigKey() + ".slow.calls") + .failureRateMetricName(this.instanceIsolationProperties.getConfigKey() + ".failure.rate") + .slowCallRateMetricName(this.instanceIsolationProperties.getConfigKey() + ".slow.call.rate") + .build(), circuitBreakerRegistry) + .bindTo(meterRegistry); + } + return new DisposableCircuitBreaker(key, circuitBreakerRegistry, + circuitBreakerRegistry.circuitBreaker(key, circuitBreakerConfig)); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/LoadBalanceHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/LoadBalanceHandler.java new file mode 100644 index 00000000000..ab549c759f2 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/LoadBalanceHandler.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.governance.handler; + +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.LoadBalancerPolicy; +import org.apache.servicecomb.governance.processor.loadbanlance.LoadBalance; +import org.apache.servicecomb.governance.properties.LoadBalanceProperties; + +public class LoadBalanceHandler extends AbstractGovernanceHandler { + + private final LoadBalanceProperties loadBalanceProperties; + + public LoadBalanceHandler(LoadBalanceProperties loadBalanceProperties) { + this.loadBalanceProperties = loadBalanceProperties; + } + + @Override + protected String createKey(GovernanceRequestExtractor requestExtractor, LoadBalancerPolicy policy) { + return this.loadBalanceProperties.getConfigKey() + "." + policy.getName(); + } + + @Override + public LoadBalancerPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { + return matchersManager.match(requestExtractor, loadBalanceProperties.getParsedEntity()); + } + + @Override + protected DisposableHolder createProcessor(String key, GovernanceRequestExtractor requestExtractor, + LoadBalancerPolicy policy) { + return new DisposableHolder<>(key, LoadBalance.getLoadBalance(key, policy)); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/MapperHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/MapperHandler.java new file mode 100644 index 00000000000..53726a2b0e4 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/MapperHandler.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.governance.handler; + +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.MapperPolicy; +import org.apache.servicecomb.governance.processor.mapping.Mapper; +import org.apache.servicecomb.governance.properties.MapperProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MapperHandler extends AbstractGovernanceHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(RateLimitingHandler.class); + + private final MapperProperties mapperProperties; + + public MapperHandler(MapperProperties mapperProperties) { + this.mapperProperties = mapperProperties; + } + + @Override + protected String createKey(GovernanceRequestExtractor requestExtractor, MapperPolicy policy) { + return mapperProperties.getConfigKey() + "." + policy.getName(); + } + + @Override + public MapperPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { + return matchersManager.match(requestExtractor, mapperProperties.getParsedEntity()); + } + + @Override + public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, + MapperPolicy policy) { + return getMapper(key, policy); + } + + private Disposable getMapper(String key, MapperPolicy policy) { + LOGGER.info("applying new policy {} for {}", key, policy.toString()); + + return new DisposableHolder<>(key, Mapper.create(policy.getTarget())); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/RateLimitingHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/RateLimitingHandler.java new file mode 100644 index 00000000000..e70ae60f78e --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/RateLimitingHandler.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.governance.handler; + +import java.time.Duration; + +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.RateLimitingPolicy; +import org.apache.servicecomb.governance.properties.RateLimitProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.github.resilience4j.micrometer.tagged.RateLimiterMetricNames; +import io.github.resilience4j.micrometer.tagged.TaggedRateLimiterMetrics; +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RateLimiterConfig; +import io.github.resilience4j.ratelimiter.RateLimiterRegistry; + +public class RateLimitingHandler extends AbstractGovernanceHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(RateLimitingHandler.class); + + private final RateLimitProperties rateLimitProperties; + + public RateLimitingHandler(RateLimitProperties rateLimitProperties) { + this.rateLimitProperties = rateLimitProperties; + } + + @Override + protected String createKey(GovernanceRequestExtractor requestExtractor, RateLimitingPolicy policy) { + return this.rateLimitProperties.getConfigKey() + "." + policy.getName(); + } + + @Override + public RateLimitingPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { + return matchersManager.match(requestExtractor, rateLimitProperties.getParsedEntity()); + } + + @Override + public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, + RateLimitingPolicy policy) { + return getRateLimiter(key, policy); + } + + private Disposable getRateLimiter(String key, RateLimitingPolicy policy) { + LOGGER.info("applying new policy {} for {}", key, policy.toString()); + + RateLimiterConfig config = RateLimiterConfig.custom() + .limitForPeriod(policy.getRate()) + .limitRefreshPeriod(Duration.parse(policy.getLimitRefreshPeriod())) + .timeoutDuration(Duration.parse(policy.getTimeoutDuration())) + .build(); + RateLimiterRegistry rateLimiterRegistry = RateLimiterRegistry.of(config); + if (meterRegistry != null) { + TaggedRateLimiterMetrics + .ofRateLimiterRegistry(RateLimiterMetricNames.custom() + .availablePermissionsMetricName( + this.rateLimitProperties.getConfigKey() + ".available.permissions") + .waitingThreadsMetricName(this.rateLimitProperties.getConfigKey() + ".waiting.threads") + .build(), + rateLimiterRegistry) + .bindTo(meterRegistry); + } + return new DisposableRateLimiter(key, rateLimiterRegistry.rateLimiter(key), rateLimiterRegistry); + } +} 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 new file mode 100644 index 00000000000..f7f2476f667 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.handler; + +import java.time.Duration; + +import org.apache.servicecomb.governance.handler.ext.AbstractRetryExtension; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.RetryPolicy; +import org.apache.servicecomb.governance.properties.RetryProperties; +import org.apache.servicecomb.governance.utils.GovernanceUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.github.resilience4j.core.IntervalFunction; +import io.github.resilience4j.micrometer.tagged.RetryMetricNames; +import io.github.resilience4j.micrometer.tagged.TaggedRetryMetrics; +import io.github.resilience4j.retry.Retry; +import io.github.resilience4j.retry.RetryConfig; +import io.github.resilience4j.retry.RetryRegistry; + +public class RetryHandler extends AbstractGovernanceHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(RetryHandler.class); + + private final RetryProperties retryProperties; + + private final AbstractRetryExtension retryExtension; + + public RetryHandler(RetryProperties retryProperties, AbstractRetryExtension retryExtension) { + this.retryProperties = retryProperties; + this.retryExtension = retryExtension; + } + + @Override + protected String createKey(GovernanceRequestExtractor requestExtractor, RetryPolicy policy) { + return this.retryProperties.getConfigKey() + "." + policy.getName(); + } + + @Override + public RetryPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { + return matchersManager.match(requestExtractor, retryProperties.getParsedEntity()); + } + + @Override + public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, + RetryPolicy policy) { + return getRetry(key, policy); + } + + private Disposable getRetry(String key, RetryPolicy retryPolicy) { + LOGGER.info("applying new policy {} for {}", key, retryPolicy.toString()); + + RetryConfig config = RetryConfig.custom() + .maxAttempts(retryPolicy.getMaxAttempts() + 1) + .retryOnResult(response -> retryExtension.isFailedResult(retryPolicy.getRetryOnResponseStatus(), response)) + .retryOnException(exception -> retryExtension.isFailedResult(retryPolicy.getRetryOnResponseStatus(), exception)) + .intervalFunction(getIntervalFunction(retryPolicy)) + .failAfterMaxAttempts(retryPolicy.isFailAfterMaxAttempts()) + .build(); + + RetryRegistry registry = RetryRegistry.of(config); + if (meterRegistry != null) { + TaggedRetryMetrics + .ofRetryRegistry(RetryMetricNames.custom() + .callsMetricName(this.retryProperties.getConfigKey() + ".calls") + .build(), + registry) + .bindTo(meterRegistry); + } + return new DisposableRetry(key, registry, registry.retry(key)); + } + + private IntervalFunction getIntervalFunction(RetryPolicy retryPolicy) { + if (GovernanceUtils.STRATEGY_RANDOM_BACKOFF.equals(retryPolicy.getRetryStrategy())) { + return IntervalFunction.ofExponentialRandomBackoff(Duration.parse(retryPolicy.getInitialInterval()), + retryPolicy.getMultiplier(), retryPolicy.getRandomizationFactor()); + } + return IntervalFunction.of(Duration.parse(retryPolicy.getWaitDuration())); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/TimeLimiterHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/TimeLimiterHandler.java new file mode 100644 index 00000000000..d9f03034d07 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/TimeLimiterHandler.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.governance.handler; + +import java.time.Duration; + +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.policy.TimeLimiterPolicy; +import org.apache.servicecomb.governance.properties.TimeLimiterProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.github.resilience4j.micrometer.tagged.TaggedTimeLimiterMetrics; +import io.github.resilience4j.micrometer.tagged.TimeLimiterMetricNames; +import io.github.resilience4j.timelimiter.TimeLimiter; +import io.github.resilience4j.timelimiter.TimeLimiterConfig; +import io.github.resilience4j.timelimiter.TimeLimiterRegistry; + +public class TimeLimiterHandler extends AbstractGovernanceHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(TimeLimiterHandler.class); + + private final TimeLimiterProperties timeLimiterProperties; + + public TimeLimiterHandler(TimeLimiterProperties timeLimiterProperties) { + this.timeLimiterProperties = timeLimiterProperties; + } + + @Override + protected String createKey(GovernanceRequestExtractor requestExtractor, TimeLimiterPolicy policy) { + return timeLimiterProperties.getConfigKey() + "." + policy.getName(); + } + + @Override + public TimeLimiterPolicy matchPolicy(GovernanceRequestExtractor requestExtractor) { + return matchersManager.match(requestExtractor, timeLimiterProperties.getParsedEntity()); + } + + @Override + public Disposable createProcessor(String key, GovernanceRequestExtractor requestExtractor, + TimeLimiterPolicy policy) { + return getTimeLimiter(key, policy); + } + + private Disposable getTimeLimiter(String key, TimeLimiterPolicy policy) { + LOGGER.info("applying new policy {} for {}", key, policy); + TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom() + .timeoutDuration(Duration.parse(policy.getTimeoutDuration())) + .cancelRunningFuture(policy.isCancelRunningFuture()) + .build(); + TimeLimiterRegistry timeLimiterRegistry = TimeLimiterRegistry.of(timeLimiterConfig); + if (meterRegistry != null) { + TaggedTimeLimiterMetrics.ofTimeLimiterRegistry(TimeLimiterMetricNames.custom() + .callsMetricName(timeLimiterProperties.getConfigKey() + ".calls") + .build(), timeLimiterRegistry).bindTo(meterRegistry); + } + return new DisposableTimeLimiter(key, timeLimiterRegistry, timeLimiterRegistry.timeLimiter(key)); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractCircuitBreakerExtension.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractCircuitBreakerExtension.java new file mode 100644 index 00000000000..bf885a9003d --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractCircuitBreakerExtension.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.governance.handler.ext; + +public abstract class AbstractCircuitBreakerExtension extends AbstractFailurePredictor { + +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractFailurePredictor.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractFailurePredictor.java new file mode 100644 index 00000000000..e7fea24f962 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractFailurePredictor.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.governance.handler.ext; + +import java.util.List; +import java.util.stream.IntStream; + +import org.apache.commons.lang3.StringUtils; + +public abstract class AbstractFailurePredictor implements FailurePredictor { + @Override + public boolean isFailedResult(List statusList, Object result) { + String statusCode = extractStatusCode(result); + if (StringUtils.isEmpty(statusCode)) { + return false; + } + return statusCodeContains(statusList, statusCode); + } + + protected abstract String extractStatusCode(Object result); + + protected static boolean statusCodeContains(List statusList, String responseStatus) { + return statusList.stream().anyMatch(status -> statusCodeMatch(status, responseStatus)); + } + + private static boolean statusCodeMatch(String status, String responseStatus) { + if (status == null) { + return false; + } + if (responseStatus.length() != status.length()) { + return false; + } + char[] statusChar = status.toCharArray(); + char[] responseChar = responseStatus.toCharArray(); + return IntStream.range(0, statusChar.length).noneMatch(i -> + statusChar[i] != responseChar[i] && statusChar[i] != 'x'); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractInstanceIsolationExtension.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractInstanceIsolationExtension.java new file mode 100644 index 00000000000..6b4a5a9934d --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractInstanceIsolationExtension.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.governance.handler.ext; + +public abstract class AbstractInstanceIsolationExtension extends AbstractFailurePredictor { + +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractRetryExtension.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractRetryExtension.java new file mode 100644 index 00000000000..cd21bdacc24 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/AbstractRetryExtension.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.governance.handler.ext; + +public abstract class AbstractRetryExtension extends AbstractFailurePredictor { + +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/ClientRecoverPolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/ClientRecoverPolicy.java new file mode 100644 index 00000000000..fc8c86a55ae --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/ClientRecoverPolicy.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.governance.handler.ext; + +public interface ClientRecoverPolicy { + + T apply(Throwable th); +} 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 new file mode 100644 index 00000000000..c07ebe39bdd --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.governance.handler.ext; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.NoRouteToHostException; +import java.net.SocketTimeoutException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.net.ssl.SSLHandshakeException; + +import com.google.common.collect.ImmutableMap; + +import io.netty.handler.ssl.SslHandshakeTimeoutException; +import io.vertx.core.VertxException; + +public interface FailurePredictor { + Map, List> STRICT_RETRIABLE = + ImmutableMap., List>builder() + .put(ConnectException.class, Collections.emptyList()) + .put(SocketTimeoutException.class, Collections.emptyList()) + /* + * deal with some special exceptions caused by the server side close the connection + */ + .put(IOException.class, Collections.singletonList("Connection reset by peer")) + .put(VertxException.class, Collections.singletonList("Connection was closed")) + .put(NoRouteToHostException.class, Collections.emptyList()) + .put(SSLHandshakeException.class, Collections.emptyList()) + .put(SslHandshakeTimeoutException.class, Collections.emptyList()) + .build(); + + boolean isFailedResult(List statusList, Object result); + + default boolean isFailedResult(List statusList, Throwable e) { + return canRetryForException(STRICT_RETRIABLE, e); + } + + static boolean canRetryForException(Map, List> retryList, + Throwable throwableToSearchIn) { + // retry on exception type on message match + int infiniteLoopPreventionCounter = 10; + while (throwableToSearchIn != null && infiniteLoopPreventionCounter > 0) { + infiniteLoopPreventionCounter--; + for (Entry, List> c : retryList.entrySet()) { + Class key = c.getKey(); + if (key.isAssignableFrom(throwableToSearchIn.getClass())) { + if (c.getValue() == null || c.getValue().isEmpty()) { + return true; + } else { + String msg = throwableToSearchIn.getMessage(); + for (String val : c.getValue()) { + if (val.equals(msg)) { + return true; + } + } + } + } + } + throwableToSearchIn = throwableToSearchIn.getCause(); + } + return false; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/ServerRecoverPolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/ServerRecoverPolicy.java new file mode 100644 index 00000000000..4662c46dd38 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/ServerRecoverPolicy.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.governance.handler.ext; + +public interface ServerRecoverPolicy { + + T apply(Throwable th); +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/CustomMatcher.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/CustomMatcher.java new file mode 100644 index 00000000000..c6ec5363791 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/CustomMatcher.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.governance.marker; + +public class CustomMatcher { + + private String customMatcherHandler; + + private String customMatcherParameters; + + public String getCustomMatcherHandler() { + return customMatcherHandler; + } + + public void setCustomMatcherHandler(String customMatcherHandler) { + this.customMatcherHandler = customMatcherHandler; + } + + public String getCustomMatcherParameters() { + return customMatcherParameters; + } + + public void setCustomMatcherParameters(String customMatcherParameters) { + this.customMatcherParameters = customMatcherParameters; + } +} 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 new file mode 100644 index 00000000000..e3ac0b2abce --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.marker; + +import java.util.Collections; +import java.util.Map; + +import org.springframework.util.LinkedCaseInsensitiveMap; + +public class GovernanceRequest implements GovernanceRequestExtractor { + /** + * headers with this request, maybe null. + * For provider: headers indicates the request headers to me. + * For consumer: headers indicates the request headers to the target. + */ + 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 + * For provider: uri indicates the request uri to me. + * For consumer: uri indicates the request uri to the target. + */ + private String apiPath; + + /** + * method with this request, maybe null. + * For provider: method indicates the request method to me. + * For consumer: method indicates the request method to the target. + */ + private String method; + + /** + * instance id with this request, maybe null. + * For provider: instanceId indicates who calls me. + * For consumer: instanceId indicates the target instance. + */ + private String instanceId; + + /** + * microservice id (microservice name or application name + microservice name) with this request, maybe null. + * For provider: serviceName indicates who calls me. + * For consumer: serviceName indicates the target service. + */ + private String serviceName; + + /** + * sourceRequest the source request for creating this governanceRequest + * For provider: uri indicates the request to me. + * For consumer: uri indicates the request to the target. + * the type of sourceRequest could be ClientRequest, ServerWebExchange, HttpRequest, HttpServletRequest and so on, + * User will use this request to extract the information he need + */ + private Object sourceRequest; + + @Override + 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; + } + + public void setApiPath(String apiPath) { + this.apiPath = apiPath; + } + + @Override + public String method() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + @Override + public String instanceId() { + return instanceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + @Override + public String serviceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + @Override + public Object sourceRequest() { + return sourceRequest; + } + + public void setSourceRequest(Object sourceRequest) { + this.sourceRequest = sourceRequest; + } +} 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 new file mode 100644 index 00000000000..28beb8e8384 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequestExtractor.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.governance.marker; + +public interface GovernanceRequestExtractor { + String apiPath(); + + String method(); + + String header(String key); + + String query(String key); + + String instanceId(); + + String serviceName(); + + Object sourceRequest(); +} 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 new file mode 100644 index 00000000000..6fb18fada3d --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/Matcher.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.governance.marker; + +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.governance.marker.operator.RawOperator; + +public class Matcher { + private String name; + + private Map headers; + + private Map queries; + + private RawOperator apiPath; + + private List method; + + private String serviceName; + + private CustomMatcher customMatcher; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map getHeaders() { + return 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; + } + + public void setApiPath(RawOperator apiPath) { + this.apiPath = apiPath; + } + + public List getMethod() { + return method; + } + + public void setMethod(List method) { + this.method = method; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public CustomMatcher getCustomMatcher() { + return customMatcher; + } + + public void setCustomMatcher(CustomMatcher customMatcher) { + this.customMatcher = customMatcher; + } +} 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 new file mode 100644 index 00000000000..9651d8a7740 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/RequestProcessor.java @@ -0,0 +1,205 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.marker; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.governance.marker.operator.MatchOperator; +import org.apache.servicecomb.governance.marker.operator.RawOperator; +import org.apache.servicecomb.governance.utils.CustomMatch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * Request Processor checks if a request matches a configuration. + */ +public class RequestProcessor implements ApplicationContextAware { + + private static final Logger LOGGER = LoggerFactory.getLogger(RequestProcessor.class); + + public static final String errorMessageForNotImplements = " didn't implement interface org.apache.servicecomb.governance.utils.CustomMatch"; + + public static final String errorMessageForAbstractClass = " should be a instantiable class rather than abstract class or other else"; + + public static final String infoMessageForCreatingClass = "is not in spring container, create one and register it to spring container"; + + private final Map operatorMap; + + private ApplicationContext applicationContext; + + public RequestProcessor(Map operatorMap) { + this.operatorMap = new HashMap<>(operatorMap.size()); + operatorMap.forEach((k, v) -> this.operatorMap.put(v.name(), v)); + } + + public boolean match(GovernanceRequestExtractor request, Matcher matcher) { + if (!methodMatch(request, matcher)) { + return false; + } + if (!apiPathMatch(request, matcher)) { + return false; + } + if (!headersMatch(request, matcher)) { + return false; + } + if (!queriesMatch(request, matcher)) { + return false; + } + if (!serviceNameMatch(request, matcher)) { + return false; + } + return customMatch(request, matcher); + } + + private boolean serviceNameMatch(GovernanceRequestExtractor request, Matcher matcher) { + if (matcher.getServiceName() == null) { + return true; + } + return matcher.getServiceName().equals(request.serviceName()); + } + + private boolean headersMatch(GovernanceRequestExtractor request, Matcher matcher) { + if (matcher.getHeaders() == null) { + return true; + } + for (Entry entry : matcher.getHeaders().entrySet()) { + if (request.header(entry.getKey()) == null || + !operatorMatch(request.header(entry.getKey()), entry.getValue())) { + return false; + } + } + 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; + } + return operatorMatch(request.apiPath(), matcher.getApiPath()); + } + + private boolean methodMatch(GovernanceRequestExtractor request, Matcher matcher) { + if (matcher.getMethod() == null) { + return true; + } + return matcher.getMethod().contains(request.method()); + } + + private boolean operatorMatch(String str, RawOperator rawOperator) { + if (rawOperator.isEmpty()) { + return false; + } + + for (Entry entry : rawOperator.entrySet()) { + MatchOperator operator = operatorMap.get(entry.getKey()); + if (operator == null) { + LOGGER.error("unsupported operator:" + entry.getKey() + ", please use one of :" + operatorMap.keySet()); + return false; + } + if (!operator.match(str, entry.getValue())) { + return false; + } + } + return true; + } + + private boolean customMatch(GovernanceRequestExtractor request, Matcher matcher) { + if (matcher.getCustomMatcher() == null) { + return true; + } + String customMatcherHandlerName = matcher.getCustomMatcher().getCustomMatcherHandler(); + String customMatcherParameters = matcher.getCustomMatcher().getCustomMatcherParameters(); + + if (StringUtils.isEmpty(customMatcherHandlerName) || StringUtils.isEmpty(customMatcherParameters)) { + return true; + } + + CustomMatch customMatcherHandler = generateHandler(customMatcherHandlerName); + return customMatcherHandler.matchRequest(request, customMatcherParameters); + } + + private CustomMatch getBeanByHandlerName(String customMatcherHandler) { + Object extractObject = null; + if (applicationContext.containsBean(customMatcherHandler)) { + extractObject = applicationContext.getBean(customMatcherHandler); + if (!(extractObject instanceof CustomMatch)) { + LOGGER.error("{} {}", customMatcherHandler, errorMessageForNotImplements); + throw new RuntimeException(customMatcherHandler + errorMessageForNotImplements); + } + return (CustomMatch) extractObject; + } + return null; + } + + + public CustomMatch generateHandler(String customMatcherHandler) { + CustomMatch extractObject = getBeanByHandlerName(customMatcherHandler); + if (extractObject != null) { + return extractObject; + } + + LOGGER.info("{} {}", customMatcherHandler, infoMessageForCreatingClass); + Class extractionHandlerClass = null; + try { + extractionHandlerClass = Class.forName(customMatcherHandler); + } catch (ClassNotFoundException e) { + LOGGER.error(e.getMessage(), e); + throw new RuntimeException(e); + } + + if (!CustomMatch.class.isAssignableFrom(extractionHandlerClass)) { + LOGGER.error("{} {}", customMatcherHandler, errorMessageForNotImplements); + throw new RuntimeException(customMatcherHandler + errorMessageForNotImplements); + } + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(extractionHandlerClass); + BeanDefinitionRegistry registry = (BeanDefinitionRegistry) applicationContext; + registry.registerBeanDefinition(customMatcherHandler, builder.getBeanDefinition()); + try { + extractObject = (CustomMatch) applicationContext.getBean(customMatcherHandler); + return extractObject; + } catch (BeansException e) { + LOGGER.error(e.getMessage(), e); + throw new RuntimeException(customMatcherHandler + errorMessageForAbstractClass, e); + } + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/TrafficMarker.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/TrafficMarker.java new file mode 100644 index 00000000000..73dcd526c9a --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/TrafficMarker.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.governance.marker; + +import java.util.List; + +import org.apache.servicecomb.governance.entity.Configurable; + +public class TrafficMarker extends Configurable { + private List matches; + + @Override + public boolean isValid() { + if (matches == null || matches.isEmpty()) { + return false; + } + return true; + } + + public List getMatches() { + return matches; + } + + public void setMatches(List matches) { + this.matches = matches; + } + + public boolean checkMatch(GovernanceRequestExtractor extractor, RequestProcessor requestProcessor) { + return this.matches.stream().anyMatch(match -> requestProcessor.match(extractor, match)); + } +} 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 new file mode 100644 index 00000000000..5b73b9aa939 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/CompareOperator.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.governance.marker.operator; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.servicecomb.governance.exception.IllegalArgsOperatorException; + +public class CompareOperator implements MatchOperator { + + private final Set charSet = new HashSet<>(); + + public CompareOperator() { + charSet.add('>'); + charSet.add('<'); + charSet.add('='); + charSet.add('!'); + } + + @Override + public String name() { + return "compare"; + } + + /** + * 支持 {@code > < = >= <= !} 后面加数字 + * + * @param targetStr + * @param patternStr + * @return + */ + @Override + public boolean match(String targetStr, String patternStr) { + char[] chars = patternStr.toCharArray(); + if (isLegalChar(chars[0]) && isLegalChar(chars[1])) { + return process(targetStr, patternStr.substring(0, 2), patternStr.substring(2)); + } else if (isLegalChar(chars[0])) { + return process(targetStr, patternStr.substring(0, 1), patternStr.substring(1)); + } else { + throw new IllegalArgsOperatorException("operator " + patternStr + " is illegal."); + } + } + + private boolean process(String targetStr, String charStr, String numStr) { + double result; + double target; + try { + target = Double.parseDouble(targetStr); + if (numStr.startsWith("-")) { + result = -Double.parseDouble(numStr.substring(1)); + } else { + result = Double.parseDouble(numStr); + } + } catch (NumberFormatException e) { + throw new IllegalArgsOperatorException("operator " + charStr + numStr + " is illegal."); + } + switch (charStr) { + case ">": + return target > result; + case "<": + return target < result; + case "=": + return doubleEquals(target, result); + case ">=": + return target >= result; + case "<=": + return target <= result; + case "!": + case "!=": + return !doubleEquals(target, result); + default: + throw new IllegalArgsOperatorException("operator " + charStr + numStr + " is illegal."); + } + } + + private boolean isLegalChar(char c) { + return charSet.contains(c); + } + + private boolean doubleEquals(double target, double result) { + return Math.abs(target - result) < 1e-6; + } +} 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 new file mode 100644 index 00000000000..faa40521441 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ContainsOperator.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.governance.marker.operator; + +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 new file mode 100644 index 00000000000..cee8d48a27e --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ExactOperator.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.governance.marker.operator; + +import org.apache.commons.lang3.StringUtils; + +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 new file mode 100644 index 00000000000..28abc1b8df5 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/MatchOperator.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.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 new file mode 100644 index 00000000000..6bb84c45a53 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/PrefixOperator.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.governance.marker.operator; + +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/RawOperator.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/RawOperator.java new file mode 100644 index 00000000000..48bd06d8cd8 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/RawOperator.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.governance.marker.operator; + +import java.util.HashMap; + +public class RawOperator extends HashMap { + private static final long serialVersionUID = 659728839992490564L; +} 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 new file mode 100644 index 00000000000..99adf9fed1e --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/SuffixOperator.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.governance.marker.operator; + +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/policy/AbstractPolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/policy/AbstractPolicy.java new file mode 100644 index 00000000000..c1638bde086 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/policy/AbstractPolicy.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.governance.policy; + +import java.time.Duration; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.governance.entity.Configurable; +import org.apache.servicecomb.governance.utils.GovernanceUtils; + +public abstract class AbstractPolicy extends Configurable implements Comparable { + protected int order = 0; + + @Override + public boolean isValid() { + return !StringUtils.isEmpty(name); + } + + @Override + public int compareTo(AbstractPolicy o) { + return this.order - o.order; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + private Duration parseToDuration(String time, Duration defaultValue) { + if (StringUtils.isEmpty(time)) { + return defaultValue; + } + if (time.matches(GovernanceUtils.DIGIT_REGEX)) { + if (Long.parseLong(time) < 0) { + throw new RuntimeException("The value of time should not be less than 0."); + } + return Duration.ofMillis(Long.parseLong(time)); + } + return Duration.parse(GovernanceUtils.DIGIT_PREFIX + time); + } + + public String stringOfDuration(String time, Duration defaultValue) { + return parseToDuration(time, defaultValue).toString(); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/policy/BulkheadPolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/policy/BulkheadPolicy.java new file mode 100644 index 00000000000..e542ae9a091 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/policy/BulkheadPolicy.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.governance.policy; + +import java.time.Duration; + +public class BulkheadPolicy extends AbstractPolicy { + + public static final int DEFAULT_MAX_CONCURRENT_CALLS = 1000; + + public static final Duration DEFAULT_MAX_WAIT_DURATION = Duration.ofMillis(0); + + private int maxConcurrentCalls = DEFAULT_MAX_CONCURRENT_CALLS; + + private String maxWaitDuration = DEFAULT_MAX_WAIT_DURATION.toString(); + + public int getMaxConcurrentCalls() { + return maxConcurrentCalls; + } + + public void setMaxConcurrentCalls(int maxConcurrentCalls) { + this.maxConcurrentCalls = maxConcurrentCalls; + } + + public String getMaxWaitDuration() { + return maxWaitDuration; + } + + public void setMaxWaitDuration(String maxWaitDuration) { + this.maxWaitDuration = stringOfDuration(maxWaitDuration, DEFAULT_MAX_WAIT_DURATION); + } + + @Override + public boolean isValid() { + if (maxConcurrentCalls < 0) { + return false; + } + if (Duration.parse(maxWaitDuration).toMillis() < 0) { + return false; + } + return super.isValid(); + } + + @Override + public String toString() { + return "BulkheadPolicy{" + + "maxConcurrentCalls=" + maxConcurrentCalls + + ", maxWaitDuration=" + maxWaitDuration + + '}'; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/policy/CircuitBreakerPolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/policy/CircuitBreakerPolicy.java new file mode 100644 index 00000000000..1d1bdfeec79 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/policy/CircuitBreakerPolicy.java @@ -0,0 +1,243 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.policy; + +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.governance.utils.GovernanceUtils; +import org.springframework.util.CollectionUtils; + +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.SlidingWindowType; + +public class CircuitBreakerPolicy extends AbstractPolicy { + + public static final float DEFAULT_FAILURE_RATE_THRESHOLD = 50; + + public static final float DEFAULT_SLOW_CALL_RATE_THRESHOLD = 100; + + public static final Duration DEFAULT_WAIT_DURATION_IN_OPEN_STATUS = Duration.ofMillis(60000); + + //ms + public static final Duration DEFAULT_SLOW_CALL_DURATION_THRESHOLD = Duration.ofMillis(60000); + + // the number of permitted calls when the CircuitBreaker is half open. + public static final int DEFAULT_PERMITTED = 10; + + public static final int DEFAULT_MINIMUM_NUMBER_CALLS = 100; + + public static final String DEFAULT_SLIDING_WINDOW_SIZE = "100"; + + public static final String DEFAULT_FAILURE_RESPONSE_STATUS_502 = "502"; + + public static final String DEFAULT_FAILURE_RESPONSE_STATUS_503 = "503"; + + public static final List DEFAULT_STATUS_LIST = Arrays.asList(DEFAULT_FAILURE_RESPONSE_STATUS_502, + DEFAULT_FAILURE_RESPONSE_STATUS_503); + + private float failureRateThreshold = DEFAULT_FAILURE_RATE_THRESHOLD; + + private float slowCallRateThreshold = DEFAULT_SLOW_CALL_RATE_THRESHOLD; + + private String waitDurationInOpenState = DEFAULT_WAIT_DURATION_IN_OPEN_STATUS.toString(); + + private String slowCallDurationThreshold = DEFAULT_SLOW_CALL_DURATION_THRESHOLD.toString(); + + private int permittedNumberOfCallsInHalfOpenState = DEFAULT_PERMITTED; + + private int minimumNumberOfCalls = DEFAULT_MINIMUM_NUMBER_CALLS; + + private String slidingWindowType; + + private String slidingWindowSize = DEFAULT_SLIDING_WINDOW_SIZE; + + //status code that need record as a failure + private List recordFailureStatus = DEFAULT_STATUS_LIST; + + //force close this circuit breaker. This parameter is not used by circuit breaker directly + private boolean forceClosed = false; + + //force open this circuit breaker. This parameter is not used by circuit breaker directly + private boolean forceOpen = false; + + public CircuitBreakerPolicy() { + } + + @Override + public boolean isValid() { + if (failureRateThreshold > 100.0F || failureRateThreshold <= 0.0F) { + return false; + } + if (slowCallRateThreshold > 100.0F || slowCallRateThreshold <= 0.0F) { + return false; + } + if (Duration.parse(waitDurationInOpenState).toMillis() <= 0) { + return false; + } + if (Duration.parse(slowCallDurationThreshold).toMillis() <= 0) { + return false; + } + if (permittedNumberOfCallsInHalfOpenState <= 0) { + return false; + } + if (minimumNumberOfCalls <= 0) { + return false; + } + + return super.isValid(); + } + + public float getFailureRateThreshold() { + return failureRateThreshold; + } + + public void setFailureRateThreshold(float failureRateThreshold) { + this.failureRateThreshold = failureRateThreshold; + } + + public float getSlowCallRateThreshold() { + return slowCallRateThreshold; + } + + public void setSlowCallRateThreshold(float slowCallRateThreshold) { + this.slowCallRateThreshold = slowCallRateThreshold; + } + + public String getWaitDurationInOpenState() { + return waitDurationInOpenState; + } + + public void setWaitDurationInOpenState(String waitDurationInOpenState) { + this.waitDurationInOpenState = stringOfDuration(waitDurationInOpenState, DEFAULT_WAIT_DURATION_IN_OPEN_STATUS); + } + + public String getSlowCallDurationThreshold() { + return slowCallDurationThreshold; + } + + public void setSlowCallDurationThreshold(String slowCallDurationThreshold) { + this.slowCallDurationThreshold = stringOfDuration(slowCallDurationThreshold, DEFAULT_SLOW_CALL_DURATION_THRESHOLD); + } + + public int getPermittedNumberOfCallsInHalfOpenState() { + return permittedNumberOfCallsInHalfOpenState; + } + + public void setPermittedNumberOfCallsInHalfOpenState(int permittedNumberOfCallsInHalfOpenState) { + this.permittedNumberOfCallsInHalfOpenState = permittedNumberOfCallsInHalfOpenState; + } + + public int getMinimumNumberOfCalls() { + return minimumNumberOfCalls; + } + + public void setMinimumNumberOfCalls(int minimumNumberOfCalls) { + this.minimumNumberOfCalls = minimumNumberOfCalls; + } + + public SlidingWindowType getSlidingWindowTypeEnum() { + if (StringUtils.isEmpty(slidingWindowType)) { + return SlidingWindowType.TIME_BASED; + } + + try { + return SlidingWindowType.valueOf(slidingWindowType); + } catch (Exception e) { + return SlidingWindowType.TIME_BASED; + } + } + + public String getSlidingWindowType() { + return this.slidingWindowType; + } + + public void setSlidingWindowType(String slidingWindowType) { + this.slidingWindowType = slidingWindowType; + } + + // time's unit is second + public String getSlidingWindowSize() { + return slidingWindowSize; + } + + public void setSlidingWindowSize(String slidingWindowSize) { + this.slidingWindowSize = getValue(slidingWindowSize); + } + + private String getValue(String slidingWindowSize) { + if (StringUtils.isEmpty(slidingWindowSize)) { + return DEFAULT_SLIDING_WINDOW_SIZE; + } + if (slidingWindowSize.matches(GovernanceUtils.DIGIT_REGEX)) { + if (Long.parseLong(slidingWindowSize) < 0) { + throw new RuntimeException("The value should be more than 0."); + } + return slidingWindowSize; + } + Duration duration = Duration.parse(GovernanceUtils.DIGIT_PREFIX + slidingWindowSize); + return String.valueOf(duration.getSeconds()); + } + + public List getRecordFailureStatus() { + if (CollectionUtils.isEmpty(this.recordFailureStatus)) { + return DEFAULT_STATUS_LIST; + } + return this.recordFailureStatus; + } + + public void setRecordFailureStatus(List recordFailureStatus) { + if (recordFailureStatus == null) { + return; + } + this.recordFailureStatus = recordFailureStatus.stream().filter(e -> !StringUtils.isEmpty(e)) + .collect(Collectors.toList()); + } + + public boolean isForceClosed() { + return forceClosed; + } + + public void setForceClosed(boolean forceClosed) { + this.forceClosed = forceClosed; + } + + public boolean isForceOpen() { + return forceOpen; + } + + public void setForceOpen(boolean forceOpen) { + this.forceOpen = forceOpen; + } + + @Override + public String toString() { + return "CircuitBreakerPolicy{" + + "failureRateThreshold=" + failureRateThreshold + + ", slowCallRateThreshold=" + slowCallRateThreshold + + ", waitDurationInOpenState=" + waitDurationInOpenState + + ", slowCallDurationThreshold=" + slowCallDurationThreshold + + ", permittedNumberOfCallsInHalfOpenState=" + permittedNumberOfCallsInHalfOpenState + + ", minimumNumberOfCalls=" + minimumNumberOfCalls + + ", slidingWindowType='" + slidingWindowType + '\'' + + ", slidingWindowSize=" + slidingWindowSize + + ", recordFailureStatus=" + recordFailureStatus + + '}'; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/policy/FaultInjectionPolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/policy/FaultInjectionPolicy.java new file mode 100644 index 00000000000..4f9500d692e --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/policy/FaultInjectionPolicy.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.governance.policy; + +import java.time.Duration; + +import org.apache.servicecomb.governance.processor.injection.FaultInjectionConst; + +public class FaultInjectionPolicy extends AbstractPolicy { + public static final Duration DEFAULT_TIMEOUT_DURATION = Duration.ofMillis(0); + + private String type = FaultInjectionConst.TYPE_DELAY; + + private String delayTime = DEFAULT_TIMEOUT_DURATION.toString(); + + private int percentage = -1; + + private int errorCode = 500; + + private boolean forceClosed = false; + + private String fallbackType = FaultInjectionConst.FALLBACK_THROWEXCEPTION; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDelayTime() { + return delayTime; + } + + public void setDelayTime(String delayTime) { + this.delayTime = stringOfDuration(delayTime, Duration.ofMillis(-1)); + } + + public int getPercentage() { + return percentage; + } + + public void setPercentage(int percentage) { + this.percentage = percentage; + } + + public int getErrorCode() { + return errorCode; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + public long getDelayTimeToMillis() { + return Duration.parse(delayTime).toMillis(); + } + + public boolean isForceClosed() { + return forceClosed; + } + + public void setForceClosed(boolean forceClosed) { + this.forceClosed = forceClosed; + } + + public String getFallbackType() { + return fallbackType; + } + + public void setFallbackType(String fallbackType) { + this.fallbackType = fallbackType; + } + + @Override + public boolean isValid() { + if (getDelayTimeToMillis() < 0 && FaultInjectionConst.TYPE_DELAY.equals(type)) { + return false; + } + if ((getErrorCode() < FaultInjectionConst.ERROR_CODE_MIN + || getErrorCode() > FaultInjectionConst.ERROR_CODE_MAX) + && FaultInjectionConst.TYPE_ABORT.equals(type)) { + return false; + } + return super.isValid(); + } + + @Override + public String toString() { + return "FaultInjectionPolicy{" + + "type=" + type + + ", delayTime=" + delayTime + + ", percentage=" + percentage + + ", errorCode=" + errorCode + + '}'; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/policy/GovernanceCachePolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/policy/GovernanceCachePolicy.java new file mode 100644 index 00000000000..719c9cf4134 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/policy/GovernanceCachePolicy.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.policy; + +import java.time.Duration; + +public class GovernanceCachePolicy extends AbstractPolicy { + public static final Duration DEFAULT_TTL = Duration.ofMillis(21600000); + + public static final long DEFAULT_MAXIMUM_SIZE = 60000; + + public static final int DEFAULT_CONCURRENCY_LEVEL = 8; + + private String ttl = DEFAULT_TTL.toString(); + + private long maximumSize = DEFAULT_MAXIMUM_SIZE; + + private int concurrencyLevel = DEFAULT_CONCURRENCY_LEVEL; + + public String getTtl() { + return ttl; + } + + public void setTtl(String ttl) { + this.ttl = stringOfDuration(ttl, DEFAULT_TTL); + } + + public Long getMaximumSize() { + return maximumSize; + } + + public void setMaximumSize(Long maximumSize) { + this.maximumSize = maximumSize; + } + + public int getConcurrencyLevel() { + return concurrencyLevel; + } + + public void setConcurrencyLevel(int concurrencyLevel) { + this.concurrencyLevel = concurrencyLevel; + } + + @Override + public String toString() { + return "CachePolicy{" + "ttl=" + ttl + ",concurrencyLevel=" + concurrencyLevel + ", maximumSize=" + maximumSize + + '}'; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/policy/IdentifierRateLimitingPolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/policy/IdentifierRateLimitingPolicy.java new file mode 100644 index 00000000000..e49da76ffff --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/policy/IdentifierRateLimitingPolicy.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.governance.policy; + +import org.apache.commons.lang3.StringUtils; + +public class IdentifierRateLimitingPolicy extends RateLimitingPolicy { + /** + * header name used to identify this rate limiter + */ + protected String identifier; + + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + @Override + public boolean isValid() { + if (StringUtils.isEmpty(identifier)) { + return false; + } + return super.isValid(); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/policy/LoadBalancerPolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/policy/LoadBalancerPolicy.java new file mode 100644 index 00000000000..4e7e07e9f58 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/policy/LoadBalancerPolicy.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.governance.policy; + +public class LoadBalancerPolicy extends AbstractPolicy { + private String rule; + + public String getRule() { + return rule; + } + + public void setRule(String rule) { + this.rule = rule; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/policy/MapperPolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/policy/MapperPolicy.java new file mode 100644 index 00000000000..76db6933602 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/policy/MapperPolicy.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.governance.policy; + +import java.util.Map; + +public class MapperPolicy extends AbstractPolicy { + private Map target; + + public Map getTarget() { + return target; + } + + public void setTarget(Map target) { + this.target = target; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/policy/RateLimitingPolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/policy/RateLimitingPolicy.java new file mode 100644 index 00000000000..29a9f6d9f69 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/policy/RateLimitingPolicy.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.policy; + +import java.time.Duration; + +public class RateLimitingPolicy extends AbstractPolicy { + + public static final Duration DEFAULT_TIMEOUT_DURATION = Duration.ofMillis(0); + + public static final Duration DEFAULT_LIMIT_REFRESH_PERIOD = Duration.ofMillis(1000); + + public static final int DEFAULT_LIMIT_FOR_PERIOD = 1000; + + protected String timeoutDuration = DEFAULT_TIMEOUT_DURATION.toString(); + + protected String limitRefreshPeriod = DEFAULT_LIMIT_REFRESH_PERIOD.toString(); + + // 配置项名称使用 rate, 对应于 resilience4j 的 limitForPeriod + protected int rate = DEFAULT_LIMIT_FOR_PERIOD; + + public String getTimeoutDuration() { + return timeoutDuration; + } + + public void setTimeoutDuration(String timeoutDuration) { + this.timeoutDuration = stringOfDuration(timeoutDuration, DEFAULT_TIMEOUT_DURATION); + } + + public String getLimitRefreshPeriod() { + return limitRefreshPeriod; + } + + public void setLimitRefreshPeriod(String limitRefreshPeriod) { + this.limitRefreshPeriod = stringOfDuration(limitRefreshPeriod, DEFAULT_LIMIT_REFRESH_PERIOD); + } + + public int getRate() { + return rate; + } + + public void setRate(int rate) { + this.rate = rate; + } + + public RateLimitingPolicy() { + } + + @Override + public boolean isValid() { + if (Duration.parse(timeoutDuration).toMillis() < 0) { + return false; + } + if (Duration.parse(limitRefreshPeriod).toMillis() <= 0) { + return false; + } + if (rate <= 0) { + return false; + } + return super.isValid(); + } + + @Override + public String toString() { + return "RateLimitingPolicy{" + + "timeoutDuration=" + timeoutDuration + + ", limitRefreshPeriod=" + limitRefreshPeriod + + ", rate=" + rate + " req/s" + + '}'; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/policy/RetryPolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/policy/RetryPolicy.java new file mode 100644 index 00000000000..726121a2c20 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/policy/RetryPolicy.java @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.policy; + +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.CollectionUtils; + +public class RetryPolicy extends AbstractPolicy { + + public static final int DEFAULT_MAX_ATTEMPTS = 3; + + public static final Duration DEFAULT_WAIT_DURATION = Duration.ofMillis(1); + + public static final String DEFAULT_RETRY_ON_RESPONSE_STATUS_502 = "502"; + + public static final String DEFAULT_RETRY_ON_RESPONSE_STATUS_503 = "503"; + + public static final List DEFAULT_STATUS_LIST = Arrays.asList(DEFAULT_RETRY_ON_RESPONSE_STATUS_502, + DEFAULT_RETRY_ON_RESPONSE_STATUS_503); + + private static final Duration INITIAL_INTERVAL = Duration.ofMillis(1000); + + private static final float MULTIPLIER = 2; + + private static final double RANDOMIZATION_FACTOR = 0.5; + + private static final String DEFAULT_RETRY_STRATEGY = "FixedInterval"; + + //max retry attempts + private int maxAttempts = DEFAULT_MAX_ATTEMPTS; + + //wait duration for each retry + private String waitDuration = DEFAULT_WAIT_DURATION.toString(); + + //status code that need retry + private List retryOnResponseStatus = DEFAULT_STATUS_LIST; + + //retry strategy + private String retryStrategy = DEFAULT_RETRY_STRATEGY; + + // initial interval for backoff retry + private String initialInterval = INITIAL_INTERVAL.toString(); + + // multiplier for backoff retry + private float multiplier = MULTIPLIER; + + // randomization factor for backoff retry + private double randomizationFactor = RANDOMIZATION_FACTOR; + + // if throw an MaxRetriesExceededException if retry condition is based on result + private boolean failAfterMaxAttempts = false; + + // if retry on the same instance. This property is not directly used in + // RetryHandler, but used for loadbalancers + private int retryOnSame = 0; + + public List getRetryOnResponseStatus() { + if (CollectionUtils.isEmpty(retryOnResponseStatus)) { + return DEFAULT_STATUS_LIST; + } + return retryOnResponseStatus; + } + + public void setRetryOnResponseStatus(List retryOnResponseStatus) { + if (retryOnResponseStatus == null) { + return; + } + this.retryOnResponseStatus = retryOnResponseStatus.stream().filter(e -> !StringUtils.isEmpty(e)) + .collect(Collectors.toList()); + } + + public int getMaxAttempts() { + return maxAttempts; + } + + public void setMaxAttempts(int maxAttempts) { + this.maxAttempts = maxAttempts; + } + + public String getWaitDuration() { + return Duration.parse(waitDuration).toMillis() < 1 ? DEFAULT_WAIT_DURATION.toString() : waitDuration; + } + + public void setWaitDuration(String waitDuration) { + this.waitDuration = stringOfDuration(waitDuration, DEFAULT_WAIT_DURATION); + } + + public String getRetryStrategy() { + if (StringUtils.isEmpty(retryStrategy)) { + retryStrategy = DEFAULT_RETRY_STRATEGY; + } + return retryStrategy; + } + + public void setRetryStrategy(String retryStrategy) { + this.retryStrategy = retryStrategy; + } + + public String getInitialInterval() { + return initialInterval; + } + + public void setInitialInterval(String initialInterval) { + this.initialInterval = stringOfDuration(initialInterval, INITIAL_INTERVAL); + } + + public float getMultiplier() { + return multiplier; + } + + public void setMultiplier(float multiplier) { + this.multiplier = multiplier; + } + + public double getRandomizationFactor() { + return randomizationFactor; + } + + public void setRandomizationFactor(double randomizationFactor) { + this.randomizationFactor = randomizationFactor; + } + + public boolean isFailAfterMaxAttempts() { + return failAfterMaxAttempts; + } + + public void setFailAfterMaxAttempts(boolean failAfterMaxAttempts) { + this.failAfterMaxAttempts = failAfterMaxAttempts; + } + + public int getRetryOnSame() { + return retryOnSame; + } + + public void setRetryOnSame(int retryOnSame) { + this.retryOnSame = retryOnSame; + } + + @Override + public boolean isValid() { + if (maxAttempts < 1) { + return false; + } + if (Duration.parse(waitDuration).toMillis() < 0) { + return false; + } + if (Duration.parse(initialInterval).toMillis() < 10) { + return false; + } + return super.isValid(); + } + + @Override + public String toString() { + return "RetryPolicy{" + + "maxAttempts=" + maxAttempts + + ", waitDuration=" + waitDuration + + ", retryOnResponseStatus='" + retryOnResponseStatus + '\'' + + '}'; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/policy/TimeLimiterPolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/policy/TimeLimiterPolicy.java new file mode 100644 index 00000000000..e00300127d6 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/policy/TimeLimiterPolicy.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.governance.policy; + +import java.time.Duration; + +public class TimeLimiterPolicy extends AbstractPolicy { + public static final Duration DEFAULT_TIMEOUT_DURATION = Duration.ofMillis(1000); + + public static final boolean DEFAULT_CANCEL_RUNNING_FUTURE = true; + + private String timeoutDuration = DEFAULT_TIMEOUT_DURATION.toString(); + + private boolean cancelRunningFuture = DEFAULT_CANCEL_RUNNING_FUTURE; + + public String getTimeoutDuration() { + return timeoutDuration; + } + + public void setTimeoutDuration(String timeoutDuration) { + this.timeoutDuration = stringOfDuration(timeoutDuration, DEFAULT_TIMEOUT_DURATION); + } + + public boolean isCancelRunningFuture() { + return cancelRunningFuture; + } + + public void setCancelRunningFuture(boolean cancelRunningFuture) { + this.cancelRunningFuture = cancelRunningFuture; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/AbortFault.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/AbortFault.java new file mode 100644 index 00000000000..c6423967601 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/AbortFault.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.governance.processor.injection; + +import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AbortFault extends AbstractFault { + + private static final Logger LOGGER = LoggerFactory.getLogger(AbortFault.class); + + public static final String ABORTED_ERROR_MSG = "aborted by fault inject"; + + public AbortFault(String key, FaultInjectionPolicy policy) { + super(key, policy); + } + + @Override + public boolean injectFault(FaultParam faultParam) { + return shouldAbort(faultParam, policy); + } + + private boolean shouldAbort(FaultParam param, FaultInjectionPolicy policy) { + // get the config values related to abort. + int abortPercent = policy.getPercentage(); + if (abortPercent == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { + LOGGER.debug("Fault injection: Abort percentage is not configured"); + return false; + } + + // check fault abort condition. + return FaultInjectionUtil.isFaultNeedToInject(param.getReqCount(), abortPercent); + } + + @Override + public int getOrder() { + return 200; + } + + @Override + public String getName() { + return FaultInjectionConst.TYPE_ABORT; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/AbstractFault.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/AbstractFault.java new file mode 100644 index 00000000000..c225ea9a2c6 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/AbstractFault.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.governance.processor.injection; + +import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; + +public abstract class AbstractFault implements Fault { + protected String key; + + protected FaultInjectionPolicy policy; + + public AbstractFault(String key, FaultInjectionPolicy policy) { + this.key = key; + this.policy = policy; + } + + @Override + public boolean injectFault() { + if (policy.isForceClosed()) { + return false; + } + FaultParam faultParam = FaultInjectionUtil.initFaultParam(key); + return injectFault(faultParam); + } + + @Override + public String getKey() { + return key; + } + + @Override + public FaultInjectionPolicy getPolicy() { + return policy; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/DelayFault.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/DelayFault.java new file mode 100644 index 00000000000..bc82c9178ff --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/DelayFault.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.governance.processor.injection; + +import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DelayFault extends AbstractFault { + private static final Logger LOGGER = LoggerFactory.getLogger(DelayFault.class); + + public DelayFault(String key, FaultInjectionPolicy policy) { + super(key, policy); + } + + @Override + public int getOrder() { + return 100; + } + + @Override + public boolean injectFault(FaultParam faultParam) { + if (!shouldDelay(faultParam, policy)) { + return false; + } + + LOGGER.debug("Fault injection: delay is added for the request by fault inject handler"); + long delay = policy.getDelayTimeToMillis(); + if (delay == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { + LOGGER.debug("Fault injection: delay is not configured"); + return false; + } + + executeDelay(faultParam, delay); + return false; + } + + private void executeDelay(FaultParam faultParam, long delay) { + Sleepable sleepable = faultParam.getSleepable(); + if (sleepable != null) { + sleepable.sleep(delay); + } + } + + private boolean shouldDelay(FaultParam param, FaultInjectionPolicy policy) { + int delayPercent = policy.getPercentage(); + if (delayPercent == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { + LOGGER.debug("Fault injection: delay percentage is not configured"); + return false; + } + // check fault delay condition. + return FaultInjectionUtil.isFaultNeedToInject(param.getReqCount(), delayPercent); + } + + @Override + public String getName() { + return FaultInjectionConst.TYPE_DELAY; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/Fault.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/Fault.java new file mode 100644 index 00000000000..204186eaed4 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/Fault.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.governance.processor.injection; + +import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; + +import io.vavr.CheckedFunction0; + +public interface Fault { + static CheckedFunction0 decorateCheckedSupplier(Fault fault, CheckedFunction0 supplier) { + return () -> { + if (fault.injectFault()) { + if (FaultInjectionConst.FALLBACK_THROWEXCEPTION.equals(fault.getPolicy().getFallbackType())) { + throw new FaultInjectionException( + FaultResponse.createFail(fault.getPolicy().getErrorCode(), AbortFault.ABORTED_ERROR_MSG)); + } else { + return null; + } + } + return supplier.apply(); + }; + } + + int getOrder(); + + String getName(); + + /* + * If true is returned,the downgrade governance policy is executed. + * Otherwise,the original request is directly executed. + * */ + boolean injectFault(); + + /* + * If true is returned,the downgrade governance policy is executed. + * Otherwise,the original request is directly executed. + * */ + boolean injectFault(FaultParam faultParam); + + String getKey(); + + FaultInjectionPolicy getPolicy(); +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionConst.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionConst.java new file mode 100644 index 00000000000..35f27691028 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionConst.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.governance.processor.injection; + +/** + * Handles the all constant values for fault injection. + */ +public class FaultInjectionConst { + + public static final int FAULT_INJECTION_DEFAULT_VALUE = -1; + + public static final String TYPE_DELAY = "delay"; + + public static final String TYPE_ABORT = "abort"; + + public static final String FALLBACK_THROWEXCEPTION = "ThrowException"; + + public static final String FALLBACK_RETURNNULL = "ReturnNull"; + + public static final int ERROR_CODE_MIN = 200; + + public static final int ERROR_CODE_MAX = 600; +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionDecorators.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionDecorators.java new file mode 100644 index 00000000000..e5eb7e0ffce --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionDecorators.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.governance.processor.injection; + +import io.vavr.CheckedFunction0; + +public interface FaultInjectionDecorators { + static FaultInjectionDecorateCheckedSupplier ofCheckedSupplier(CheckedFunction0 supplier) { + return new FaultInjectionDecorateCheckedSupplier<>(supplier); + } + + class FaultInjectionDecorateCheckedSupplier { + + private CheckedFunction0 supplier; + + protected FaultInjectionDecorateCheckedSupplier(CheckedFunction0 supplier) { + this.supplier = supplier; + } + + public FaultInjectionDecorateCheckedSupplier withFaultInjection(Fault fault) { + supplier = Fault.decorateCheckedSupplier(fault, supplier); + return this; + } + + public T get() throws Throwable { + return supplier.apply(); + } + } + +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionException.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionException.java new file mode 100644 index 00000000000..13058eca196 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionException.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.governance.processor.injection; + +public class FaultInjectionException extends RuntimeException { + private static final long serialVersionUID = 1675558351029273343L; + + private final FaultResponse faultResponse; + + public FaultInjectionException(FaultResponse faultResponse) { + super(faultResponse.getErrorMsg()); + this.faultResponse = faultResponse; + } + + public FaultResponse getFaultResponse() { + return faultResponse; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionUtil.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionUtil.java new file mode 100644 index 00000000000..c7a1fdb6d7d --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultInjectionUtil.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.governance.processor.injection; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; + +import io.vertx.core.Context; +import io.vertx.core.Vertx; + +/** + * Handles the count for all request based key[transport + microservice qualified name]. + */ +public final class FaultInjectionUtil { + + private FaultInjectionUtil() { + } + + /** + * key is transport+operQualifiedName + */ + private static final Map REQUEST_COUNT = new ConcurrentHashMapEx<>(); + + /** + * Returns total requests per provider for operational level. + * + * @param key + * transport+operational name + * @return long total requests + */ + public static AtomicLong getOperMetTotalReq(String key) { + return REQUEST_COUNT.computeIfAbsent(key, p -> new AtomicLong(1)); + } + + /** + * It will check the delay/abort condition based on request count and percentage + * received. + * + * @param reqCount total request count of the uri + * @param percentage the percentage of hitting fault injection + * @return true: delay/abort is needed. false: delay/abort is not needed. + */ + public static boolean isFaultNeedToInject(long reqCount, int percentage) { + /* + * Example: delay/abort percentage configured is 10% and Get the count(suppose + * if it is 10th request) from map and calculate resultNew(10th request) and + * requestOld(9th request). Like this for every request it will calculate + * current request count and previous count. if both not matched need to add + * delay/abort otherwise no need to add. + */ + + // calculate the value with current request count. + long resultNew = (reqCount * percentage) / 100; + + // calculate the value with previous count value. + long resultOld = ((reqCount - 1) * percentage) / 100; + + // if both are not matching then delay/abort should be added. + return (resultNew != resultOld); + } + + public static Fault getFault(String key, FaultInjectionPolicy policy) { + Fault fault = null; + if (FaultInjectionConst.TYPE_DELAY.equals(policy.getType())) { + fault = new DelayFault(key, policy); + } else if (FaultInjectionConst.TYPE_ABORT.equals(policy.getType())) { + fault = new AbortFault(key, policy); + } + return fault; + } + + public static FaultParam initFaultParam(String key) { + AtomicLong reqCount = FaultInjectionUtil.getOperMetTotalReq(key); + // increment the request count here after checking the delay/abort condition. + long reqCountCurrent = reqCount.getAndIncrement(); + + FaultParam param = new FaultParam(reqCountCurrent); + Context currentContext = Vertx.currentContext(); + if (currentContext != null && currentContext.owner() != null && currentContext.isEventLoopContext()) { + param.setSleepable( + (delay) -> currentContext.owner().setTimer(delay, timeId -> {})); + } + return param; + } + +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultParam.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultParam.java new file mode 100644 index 00000000000..9cd4c6611cb --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultParam.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.governance.processor.injection; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Fault injection parameters which decides the fault injection condition. + */ +public class FaultParam { + private static final Logger LOGGER = LoggerFactory.getLogger(FaultParam.class); + + private final long reqCount; + + private Sleepable sleepable = (delay) -> { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + LOGGER.info("Interrupted exception is received"); + } + }; + + public long getReqCount() { + return reqCount; + } + + public FaultParam(long reqCount) { + this.reqCount = reqCount; + } + + public Sleepable getSleepable() { + return sleepable; + } + + public void setSleepable(Sleepable sleepable) { + this.sleepable = sleepable; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultResponse.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultResponse.java new file mode 100644 index 00000000000..01b8fc8d263 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/FaultResponse.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.governance.processor.injection; + +public class FaultResponse { + + private int errorCode; + + private String errorMsg; + + public static FaultResponse createFail(int errorCode, String errorMsg) { + FaultResponse faultResponse = new FaultResponse(); + faultResponse.setErrorCode(errorCode); + faultResponse.setErrorMsg(errorMsg); + return faultResponse; + } + + public int getErrorCode() { + return errorCode; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/Sleepable.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/Sleepable.java new file mode 100644 index 00000000000..22a95074f29 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/injection/Sleepable.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.governance.processor.injection; + +public interface Sleepable { + /** + * sleep some time + * @param delay time unit is millisecond + */ + void sleep(long delay); +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/loadbanlance/LoadBalance.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/loadbanlance/LoadBalance.java new file mode 100644 index 00000000000..105c4fc59dc --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/loadbanlance/LoadBalance.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.governance.processor.loadbanlance; + +import org.apache.servicecomb.governance.policy.LoadBalancerPolicy; + +public interface LoadBalance { + + static LoadBalance getLoadBalance(String key, LoadBalancerPolicy policy) { + LoadBalance loadBalance = new LoadBalanceImpl(policy.getRule()); + return loadBalance; + } + + String getRule(); +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/loadbanlance/LoadBalanceImpl.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/loadbanlance/LoadBalanceImpl.java new file mode 100644 index 00000000000..48fb023fac7 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/loadbanlance/LoadBalanceImpl.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.governance.processor.loadbanlance; + +public class LoadBalanceImpl implements LoadBalance { + private final String rule; + + public LoadBalanceImpl(String rule) { + this.rule = rule; + } + + public String getRule() { + return rule; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/processor/mapping/Mapper.java b/governance/src/main/java/org/apache/servicecomb/governance/processor/mapping/Mapper.java new file mode 100644 index 00000000000..f9c435ecec1 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/processor/mapping/Mapper.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.processor.mapping; + +import java.util.Map; + +public interface Mapper { + static Mapper create(Map target) { + return () -> target; + } + + Map target(); +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/BulkheadProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/BulkheadProperties.java new file mode 100644 index 00000000000..62d6cc2c7e9 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/BulkheadProperties.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.governance.properties; + +import org.apache.servicecomb.governance.policy.BulkheadPolicy; + +public class BulkheadProperties extends PolicyProperties { + public static final String MATCH_BULKHEAD_KEY = "servicecomb.bulkhead"; + + public BulkheadProperties() { + super(MATCH_BULKHEAD_KEY); + } + + public BulkheadProperties(String key) { + super(key); + } + + @Override + public Class getEntityClass() { + return BulkheadPolicy.class; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/CircuitBreakerProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/CircuitBreakerProperties.java new file mode 100644 index 00000000000..96aa701ad70 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/CircuitBreakerProperties.java @@ -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. + */ +package org.apache.servicecomb.governance.properties; + +import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; + +public class CircuitBreakerProperties extends PolicyProperties { + public static final String MATCH_CIRCUITBREAKER_KEY = "servicecomb.circuitBreaker"; + + public CircuitBreakerProperties() { + super(MATCH_CIRCUITBREAKER_KEY); + } + + public CircuitBreakerProperties(String key) { + super(key); + } + + @Override + public Class getEntityClass() { + return CircuitBreakerPolicy.class; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/FaultInjectionProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/FaultInjectionProperties.java new file mode 100644 index 00000000000..3a881352cb4 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/FaultInjectionProperties.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.governance.properties; + +import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; + +public class FaultInjectionProperties extends PolicyProperties { + public static final String MATCH_FAULT_INJECTION_KEY = "servicecomb.faultInjection"; + + public FaultInjectionProperties() { + super(MATCH_FAULT_INJECTION_KEY); + } + + public FaultInjectionProperties(String key) { + super(key); + } + + @Override + public Class getEntityClass() { + return FaultInjectionPolicy.class; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceCacheProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceCacheProperties.java new file mode 100644 index 00000000000..9ae9a8daa96 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceCacheProperties.java @@ -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. + */ +package org.apache.servicecomb.governance.properties; + +import org.apache.servicecomb.governance.policy.GovernanceCachePolicy; + +public class GovernanceCacheProperties extends PolicyProperties { + public static final String MATCH_CACHE_KEY = "servicecomb.cache"; + + public GovernanceCacheProperties() { + super(MATCH_CACHE_KEY); + } + + public GovernanceCacheProperties(String key) { + super(key); + } + + @Override + public Class getEntityClass() { + return GovernanceCachePolicy.class; + } +} 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 new file mode 100644 index 00000000000..05f42dfa5b1 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceProperties.java @@ -0,0 +1,214 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.governance.properties; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.governance.MicroserviceMeta; +import org.apache.servicecomb.governance.entity.Configurable; +import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; +import org.apache.servicecomb.governance.event.GovernanceEventManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.CompositePropertySource; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertySource; +import org.springframework.util.CollectionUtils; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.LoaderOptions; +import org.yaml.snakeyaml.TypeDescription; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.representer.Representer; + +import com.google.common.eventbus.Subscribe; + +public abstract class GovernanceProperties implements InitializingBean { + private static final Logger LOGGER = LoggerFactory.getLogger(GovernanceProperties.class); + + private final Representer representer = new Representer(new DumperOptions()); + + private final String configKey; + + protected Environment environment; + + private MicroserviceMeta microserviceMeta; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + public void setMicroserviceMeta(MicroserviceMeta microserviceMeta) { + this.microserviceMeta = microserviceMeta; + } + + protected Map parsedEntity; + + protected Class entityClass; + + protected GovernanceProperties(String key) { + configKey = key; + representer.getPropertyUtils().setSkipMissingProperties(true); + GovernanceEventManager.register(this); + entityClass = getEntityClass(); + } + + public String getConfigKey() { + return this.configKey; + } + + @Override + public void afterPropertiesSet() { + parsedEntity = parseEntity(readPropertiesFromPrefix()); + } + + @Subscribe + public void onConfigurationChangedEvent(GovernanceConfigurationChangedEvent event) { + for (String key : event.getChangedConfigurations()) { + if (key.startsWith(configKey + ".")) { + String mapKey = key.substring((configKey + ".").length()); + parsedEntity.remove(mapKey); + T entityItem = parseEntityItem(mapKey, environment.getProperty(key)); + if (entityItem != null) { + parsedEntity.put(mapKey, entityItem); + } + } + } + } + + private Map readPropertiesFromPrefix() { + Set allKeys = getAllKeys(environment); + Map result = new HashMap<>(); + allKeys.forEach(key -> { + if (key.startsWith(configKey + ".")) { + result.put(key.substring(configKey.length() + 1), environment.getProperty(key)); + } + }); + return result; + } + + private Set getAllKeys(Environment environment) { + Set allKeys = new HashSet<>(); + + if (!(environment instanceof ConfigurableEnvironment)) { + return allKeys; + } + + ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; + + for (PropertySource propertySource : configurableEnvironment.getPropertySources()) { + getProperties(propertySource, allKeys); + } + return allKeys; + } + + private void getProperties(PropertySource propertySource, + Set allKeys) { + if (propertySource instanceof CompositePropertySource) { + // recursively get EnumerablePropertySource + CompositePropertySource compositePropertySource = (CompositePropertySource) propertySource; + compositePropertySource.getPropertySources().forEach(ps -> getProperties(ps, allKeys)); + return; + } + if (propertySource instanceof EnumerablePropertySource) { + EnumerablePropertySource enumerablePropertySource = (EnumerablePropertySource) propertySource; + Collections.addAll(allKeys, enumerablePropertySource.getPropertyNames()); + return; + } + + LOGGER.debug("None EnumerablePropertySource ignored in {}, propertySourceName = [{}]", this.getClass().getName(), + propertySource.getName()); + } + + public Map getParsedEntity() { + return this.parsedEntity; + } + + protected Map parseEntity(Map yamlEntity) { + if (CollectionUtils.isEmpty(yamlEntity)) { + return new HashMap<>(); + } + + Map resultMap = new HashMap<>(); + for (Entry entry : yamlEntity.entrySet()) { + T marker = parseEntityItem(entry.getKey(), entry.getValue()); + if (marker != null) { + resultMap.put(entry.getKey(), marker); + } + } + return resultMap; + } + + protected abstract Class getEntityClass(); + + protected T parseEntityItem(String key, String value) { + if (StringUtils.isEmpty(value)) { + return null; + } + + try { + Yaml entityParser = new Yaml(new Constructor(new TypeDescription(entityClass, entityClass), new LoaderOptions()), + representer); + T result = entityParser.loadAs(value, entityClass); + result.setName(key); + + if (!result.isValid()) { + LOGGER.warn("Entity configuration is not valid and ignored. Key [{}], value [{}]", key, value); + return null; + } + if (!servicesMatch(result.getServices())) { + LOGGER.info("Configuration belongs to other service is ignored. Key [{}]", key); + return null; + } + return result; + } catch (RuntimeException e) { + LOGGER.error("governance config yaml is illegal : {}", e.getMessage()); + } + return null; + } + + private boolean servicesMatch(String services) { + if (StringUtils.isEmpty(services)) { + return true; + } + + 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) { + return microserviceMeta.getName().equals(serviceAndVersion[0]) && microserviceMeta.getVersion() + .equals(serviceAndVersion[1]); + } else { + return false; + } + }); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/IdentifierRateLimitProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/IdentifierRateLimitProperties.java new file mode 100644 index 00000000000..7db52c45b4b --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/IdentifierRateLimitProperties.java @@ -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. + */ +package org.apache.servicecomb.governance.properties; + +import org.apache.servicecomb.governance.policy.IdentifierRateLimitingPolicy; + +public class IdentifierRateLimitProperties extends PolicyProperties { + public static final String MATCH_RATE_LIMIT_KEY = "servicecomb.identifierRateLimiting"; + + public IdentifierRateLimitProperties() { + super(MATCH_RATE_LIMIT_KEY); + } + + public IdentifierRateLimitProperties(String key) { + super(key); + } + + @Override + public Class getEntityClass() { + return IdentifierRateLimitingPolicy.class; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/InstanceBulkheadProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/InstanceBulkheadProperties.java new file mode 100644 index 00000000000..12627046745 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/InstanceBulkheadProperties.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.governance.properties; + +import org.apache.servicecomb.governance.policy.BulkheadPolicy; + +public class InstanceBulkheadProperties extends PolicyProperties { + public static final String MATCH_INSTANCE_BULKHEAD_KEY = "servicecomb.instanceBulkhead"; + + public InstanceBulkheadProperties() { + super(MATCH_INSTANCE_BULKHEAD_KEY); + } + + public InstanceBulkheadProperties(String key) { + super(key); + } + + @Override + public Class getEntityClass() { + return BulkheadPolicy.class; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/InstanceIsolationProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/InstanceIsolationProperties.java new file mode 100644 index 00000000000..e47193fd340 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/InstanceIsolationProperties.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.governance.properties; + +import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; + +public class InstanceIsolationProperties extends PolicyProperties { + public static final String MATCH_INSTANCE_ISOLATION_KEY = "servicecomb.instanceIsolation"; + + public InstanceIsolationProperties() { + super(MATCH_INSTANCE_ISOLATION_KEY); + } + + public InstanceIsolationProperties(String key) { + super(key); + } + + @Override + public Class getEntityClass() { + return CircuitBreakerPolicy.class; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/LoadBalanceProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/LoadBalanceProperties.java new file mode 100644 index 00000000000..892bbb8a580 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/LoadBalanceProperties.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.governance.properties; + +import org.apache.servicecomb.governance.policy.LoadBalancerPolicy; + +public class LoadBalanceProperties extends PolicyProperties { + public static final String MATCH_LOADBANLANCER_KEY = "servicecomb.loadbalance"; + + public LoadBalanceProperties() { + super(MATCH_LOADBANLANCER_KEY); + } + + public LoadBalanceProperties(String key) { + super(key); + } + + @Override + protected Class getEntityClass() { + return LoadBalancerPolicy.class; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/MapperProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/MapperProperties.java new file mode 100644 index 00000000000..5a20d4dd346 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/MapperProperties.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.governance.properties; + +import org.apache.servicecomb.governance.policy.MapperPolicy; + +public class MapperProperties extends PolicyProperties { + public static final String MATCH_MAPPER_KEY = "servicecomb.mapper"; + + public MapperProperties() { + super(MATCH_MAPPER_KEY); + } + + public MapperProperties(String key) { + super(key); + } + + @Override + protected Class getEntityClass() { + return MapperPolicy.class; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/MatchProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/MatchProperties.java new file mode 100644 index 00000000000..ca551e9c781 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/MatchProperties.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.governance.properties; + +import org.apache.servicecomb.governance.marker.TrafficMarker; + +public class MatchProperties extends GovernanceProperties { + public static final String MATCH_POLICY_KEY = "servicecomb.matchGroup"; + + public MatchProperties() { + super(MATCH_POLICY_KEY); + } + + @Override + public Class getEntityClass() { + return TrafficMarker.class; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/PolicyProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/PolicyProperties.java new file mode 100644 index 00000000000..4bd53d83fce --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/PolicyProperties.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.governance.properties; + +import org.apache.servicecomb.governance.policy.AbstractPolicy; + +public abstract class PolicyProperties extends GovernanceProperties { + protected PolicyProperties(String key) { + super(key); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/RateLimitProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/RateLimitProperties.java new file mode 100644 index 00000000000..c3c09434636 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/RateLimitProperties.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.governance.properties; + + +import org.apache.servicecomb.governance.policy.RateLimitingPolicy; + +public class RateLimitProperties extends PolicyProperties { + public static final String MATCH_RATE_LIMIT_KEY = "servicecomb.rateLimiting"; + + public RateLimitProperties() { + super(MATCH_RATE_LIMIT_KEY); + } + + public RateLimitProperties(String key) { + super(key); + } + + @Override + public Class getEntityClass() { + return RateLimitingPolicy.class; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/RetryProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/RetryProperties.java new file mode 100644 index 00000000000..50106c2a003 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/RetryProperties.java @@ -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. + */ +package org.apache.servicecomb.governance.properties; + +import org.apache.servicecomb.governance.policy.RetryPolicy; + +public class RetryProperties extends PolicyProperties { + public static final String MATCH_RETRY_KEY = "servicecomb.retry"; + + public RetryProperties() { + super(MATCH_RETRY_KEY); + } + + public RetryProperties(String key) { + super(key); + } + + @Override + public Class getEntityClass() { + return RetryPolicy.class; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/TimeLimiterProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/TimeLimiterProperties.java new file mode 100644 index 00000000000..daca14f57f4 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/TimeLimiterProperties.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.governance.properties; + + +import org.apache.servicecomb.governance.policy.TimeLimiterPolicy; + +public class TimeLimiterProperties extends PolicyProperties { + public static final String MATCH_TIMELIMITER_KEY = "servicecomb.timeLimiter"; + + public TimeLimiterProperties() { + super(MATCH_TIMELIMITER_KEY); + } + + public TimeLimiterProperties(String key) { + super(key); + } + + @Override + public Class getEntityClass() { + return TimeLimiterPolicy.class; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/service/GovernanceCache.java b/governance/src/main/java/org/apache/servicecomb/governance/service/GovernanceCache.java new file mode 100644 index 00000000000..35014272fd9 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/service/GovernanceCache.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.governance.service; + +import com.google.common.cache.Cache; + +import java.util.Objects; + +public interface GovernanceCache { + static GovernanceCache of(Cache cache) { + Objects.requireNonNull(cache, "Cache must not be null"); + return new GovernanceCacheImpl<>(cache); + } + + V getValueFromCache(K cacheKey); + + void putValueIntoCache(K cacheKey, V value); +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/service/GovernanceCacheImpl.java b/governance/src/main/java/org/apache/servicecomb/governance/service/GovernanceCacheImpl.java new file mode 100644 index 00000000000..a7dcfa5d4e7 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/service/GovernanceCacheImpl.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.governance.service; + +import com.google.common.cache.Cache; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GovernanceCacheImpl implements GovernanceCache { + + private static final Logger LOG = LoggerFactory.getLogger(GovernanceCacheImpl.class); + + private final Cache cache; + + public GovernanceCacheImpl(Cache cache) { + this.cache = cache; + } + + public V getValueFromCache(K cacheKey) { + try { + return cache.getIfPresent(cacheKey); + } catch (Exception exception) { + LOG.warn("Failed to get a value from Cache", exception); + return null; + } + } + + @Override + public void putValueIntoCache(K cacheKey, V value) { + try { + cache.put(cacheKey, value); + } catch (Exception exception) { + LOG.warn("Failed to put a value into Cache {}", exception); + } + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/service/MatchersService.java b/governance/src/main/java/org/apache/servicecomb/governance/service/MatchersService.java new file mode 100644 index 00000000000..1b0f0b42f9a --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/service/MatchersService.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.governance.service; + +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; + +public interface MatchersService { + boolean checkMatch(GovernanceRequestExtractor governanceRequest, String key); +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/service/MatchersServiceImpl.java b/governance/src/main/java/org/apache/servicecomb/governance/service/MatchersServiceImpl.java new file mode 100644 index 00000000000..cc851f16892 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/service/MatchersServiceImpl.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.governance.service; + +import java.util.Map; + +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.marker.RequestProcessor; +import org.apache.servicecomb.governance.marker.TrafficMarker; +import org.apache.servicecomb.governance.properties.MatchProperties; + +public class MatchersServiceImpl implements MatchersService { + private final RequestProcessor requestProcessor; + + private final MatchProperties matchProperties; + + public MatchersServiceImpl(RequestProcessor requestProcessor, MatchProperties matchProperties) { + this.requestProcessor = requestProcessor; + this.matchProperties = matchProperties; + } + + @Override + public boolean checkMatch(GovernanceRequestExtractor governanceRequest, String key) { + Map parsedEntity = matchProperties.getParsedEntity(); + + TrafficMarker trafficMarker = parsedEntity.get(key); + + if (trafficMarker == null) { + return false; + } + + return trafficMarker.checkMatch(governanceRequest, requestProcessor); + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/utils/CustomMatch.java b/governance/src/main/java/org/apache/servicecomb/governance/utils/CustomMatch.java new file mode 100644 index 00000000000..d2627e82af8 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/utils/CustomMatch.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.governance.utils; + +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; + +public interface CustomMatch { + boolean matchRequest(GovernanceRequestExtractor requestExtractor, String parameters); +} diff --git a/governance/src/main/java/org/apache/servicecomb/governance/utils/GovernanceUtils.java b/governance/src/main/java/org/apache/servicecomb/governance/utils/GovernanceUtils.java new file mode 100644 index 00000000000..28c4ed2a407 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/governance/utils/GovernanceUtils.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.governance.utils; + +public final class GovernanceUtils { + public static final String DIGIT_REGEX = "-{0,1}[0-9]{1,10}"; + + public static final String DIGIT_PREFIX = "PT"; + + public static final String STRATEGY_RANDOM_BACKOFF = "RandomBackoff"; +} diff --git a/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java b/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java new file mode 100644 index 00000000000..9aa881ee8df --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.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.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; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +@Configuration +public class RouterCommonConfiguration { + @Bean + public RouterRuleCache scbRouterRuleCache(Environment environment) { + return new RouterRuleCache(environment); + } + + @Bean + public RouterRuleMatcher scbRouterRuleMatcher(RouterRuleCache routerRuleCache, RequestProcessor requestProcessor) { + return new RouterRuleMatcher(routerRuleCache, requestProcessor); + } + + @Bean + 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 new file mode 100644 index 00000000000..ab742e8613d --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/router/RouterFilter.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.router; + +import java.util.List; + +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; +import org.apache.servicecomb.router.model.PolicyRuleItem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; + +public class RouterFilter { + + private static final Logger LOGGER = LoggerFactory.getLogger(RouterFilter.class); + + private final RouterRuleMatcher routerRuleMatcher; + + private final RouterRuleCache routerRuleCache; + + public RouterFilter(RouterRuleMatcher routerRuleMatcher, RouterRuleCache routerRuleCache) { + this.routerRuleMatcher = routerRuleMatcher; + this.routerRuleCache = routerRuleCache; + } + + public List getFilteredListOfServers(List list, + String targetServiceName, GovernanceRequestExtractor extractor, RouterDistributor distributor) { + if (CollectionUtils.isEmpty(list)) { + return list; + } + if (StringUtils.isEmpty(targetServiceName)) { + return list; + } + // 1.init and cache + if (!routerRuleCache.doInit(targetServiceName)) { + LOGGER.debug("route management init failed"); + return list; + } + // 2.match rule + PolicyRuleItem invokeRule = routerRuleMatcher.match(targetServiceName, extractor); + + if (invokeRule == null) { + LOGGER.debug("route management match rule failed"); + return list; + } + + LOGGER.debug("route management match rule success: {}", invokeRule); + + // 3.distribute select endpoint + List resultList = distributor.distribute(targetServiceName, list, invokeRule); + + LOGGER.debug("route management distribute rule success: {}", resultList); + + return 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 new file mode 100644 index 00000000000..7828c82d3a7 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/router/cache/RouterRuleCache.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.cache; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; +import org.apache.servicecomb.governance.event.GovernanceEventManager; +import org.apache.servicecomb.router.model.PolicyRuleItem; +import org.apache.servicecomb.router.model.ServiceInfoCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.LoaderOptions; +import org.yaml.snakeyaml.TypeDescription; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.representer.Representer; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.eventbus.Subscribe; + +public class RouterRuleCache { + private static final Logger LOGGER = LoggerFactory.getLogger(RouterRuleCache.class); + + public static final String ROUTE_RULE_PREFIX = "servicecomb.routeRule."; + + 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<>(); + + private final Object lock = new Object(); + + private final Representer representer = new Representer(new DumperOptions()); + + public RouterRuleCache(Environment environment) { + this.environment = environment; + representer.getPropertyUtils().setSkipMissingProperties(true); + GovernanceEventManager.register(this); + } + + /** + * cache and register callback + * + * return false when: 1. parsing error 2. rule is null + */ + public boolean doInit(String targetServiceName) { + if (!isServerContainRule(targetServiceName)) { + return false; + } + if (!serviceInfoCacheMap.containsKey(targetServiceName)) { + synchronized (lock) { + if (serviceInfoCacheMap.containsKey(targetServiceName)) { + return true; + } + return addAllRule(targetServiceName); + } + } + return true; + } + + @Subscribe + public void onConfigurationChangedEvent(GovernanceConfigurationChangedEvent event) { + for (String key : event.getChangedConfigurations()) { + 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 = environment.getProperty(String.format(ROUTE_RULE, targetServiceName), ""); + if (StringUtils.isEmpty(ruleStr)) { + ruleStr = environment.getProperty(GLOBAL_ROUTE_RULE_KEY, ""); + } + if (StringUtils.isEmpty(ruleStr)) { + return false; + } + List policyRuleItemList; + try { + Yaml entityParser = new Yaml( + new Constructor(new TypeDescription(PolicyRuleItem[].class, PolicyRuleItem[].class), new LoaderOptions()), + representer); + policyRuleItemList = Arrays + .asList(entityParser.loadAs(ruleStr, PolicyRuleItem[].class)); + } catch (Exception e) { + LOGGER.warn("Route management serialization for service {} failed: {}", targetServiceName, e.getMessage()); + return false; + } + if (CollectionUtils.isEmpty(policyRuleItemList)) { + LOGGER.warn("Route management serialization for service {} is empty", targetServiceName); + return false; + } + ServiceInfoCache serviceInfoCache = new ServiceInfoCache(policyRuleItemList); + serviceInfoCacheMap.put(targetServiceName, serviceInfoCache); + LOGGER.info("Route management serialization service {} rules success, content: {}", targetServiceName, + serviceInfoCache.getAllrule()); + return true; + } + + /** + * 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), "")) || + !StringUtils.isEmpty(environment.getProperty(GLOBAL_ROUTE_RULE_KEY, "")); + } + + public ConcurrentHashMap getServiceInfoCacheMap() { + return serviceInfoCacheMap; + } + + @VisibleForTesting + void refresh() { + serviceInfoCacheMap.clear(); + } + + public void refresh(String targetServiceName) { + serviceInfoCacheMap.remove(targetServiceName); + } +} 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 new file mode 100644 index 00000000000..d3905dfb133 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.CollectionUtils; + +public abstract class AbstractRouterDistributor implements + RouterDistributor { + + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRouterDistributor.class); + + private Function getVersion; + + private Function getServerName; + + private Function> getProperties; + + private RouterRuleCache routerRuleCache; + + @Autowired + public void setRouterRuleCache(RouterRuleCache routerRuleCache) { + this.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) { + invokeRule.check(); + + // unSetTags instance list + List unSetTagInstances = new ArrayList<>(); + + // record fallback router targItem instance + Map> fallbackVersionServerMap = new HashMap<>(); + + // get tag instance map, fallbackVersionServerMap, unSetTagInstances + Map> versionServerMap = getDistributList(targetServiceName, list, invokeRule, + unSetTagInstances, fallbackVersionServerMap); + + // weight calculation to obtain the next tags instance + TagItem targetTag = getFilteredServerTagItem(invokeRule, targetServiceName); + if (targetTag != null && versionServerMap.containsKey(targetTag)) { + return versionServerMap.get(targetTag); + } + + 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 getVersion, + Function getServerName, + Function> getProperties) { + this.getVersion = getVersion; + this.getServerName = getServerName; + this.getProperties = getProperties; + } + + 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 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, List unSetTagInstances, Map> fallbackVersionMap) { + Map> versionServerMap = new HashMap<>(); + for (INSTANCE instance : list) { + //get 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(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; + } + + 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(); + } + } + 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 new file mode 100644 index 00000000000..5f5babdec34 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/router/distribute/RouterDistributor.java @@ -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. + */ +package org.apache.servicecomb.router.distribute; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import org.apache.servicecomb.router.model.PolicyRuleItem; + +/** + * @author GuoYl123 + * @since 2019/10/17 + **/ +public interface RouterDistributor { + + void init(Function getVersion, + Function getServerName, + Function> getProperties); + + 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 new file mode 100644 index 00000000000..9647a853ea8 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/router/exception/RouterIllegalParamException.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.router.exception; + +/** + * @author GuoYl123 + * @since 2019/11/4 + **/ +public class RouterIllegalParamException extends RuntimeException { + + private static final long serialVersionUID = 4359709211352400087L; + + public RouterIllegalParamException(String message) { + super(message); + } + + public RouterIllegalParamException(String message, Throwable cause) { + super(message, cause); + } +} 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 new file mode 100644 index 00000000000..dd2ba78d7d3 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.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.router.match; + +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; + + private final RequestProcessor requestProcessor; + + public RouterRuleMatcher(RouterRuleCache routerRuleCache, RequestProcessor requestProcessor) { + this.routerRuleCache = routerRuleCache; + this.requestProcessor = requestProcessor; + } + + public PolicyRuleItem match(String serviceName, GovernanceRequestExtractor request) { + for (PolicyRuleItem rule : routerRuleCache.getServiceInfoCacheMap().get(serviceName) + .getAllrule()) { + if (rule.getMatch() == null || requestProcessor.match(request, rule.getMatch())) { + return rule; + } + } + return null; + } +} 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 new file mode 100644 index 00000000000..7a8afeda77f --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/router/model/HeaderRule.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.router.model; + +import org.apache.servicecomb.router.exception.RouterIllegalParamException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author GuoYl123 + * @since 2019/10/17 + **/ +public class HeaderRule { + + private static final Logger LOGGER = LoggerFactory.getLogger(HeaderRule.class); + + private String regex; + + private boolean caseInsensitive = true; + + private String exact; + + public HeaderRule() { + } + + public boolean match(String str) { + if (str == null) { + return false; + } + if (exact == null && regex == null) { + throw new RouterIllegalParamException( + "route management regex and exact can not br null at same time."); + } + if (caseInsensitive) { + str = str.toLowerCase(); + exact = exact == null ? null : exact.toLowerCase(); + regex = regex == null ? null : regex.toLowerCase(); + } + if (exact != null && !str.equals(exact)) { + return false; + } + try { + if (regex != null && !str.matches(regex)) { + return false; + } + } catch (Exception e) { + LOGGER.error("route management wrong regular expression format: {}", regex); + return false; + } + return true; + } + + public String getRegex() { + return regex; + } + + public void setRegex(String regex) { + this.regex = regex; + } + + public boolean isCaseInsensitive() { + return caseInsensitive; + } + + public void setCaseInsensitive(boolean caseInsensitive) { + this.caseInsensitive = caseInsensitive; + } + + public String getExact() { + return exact; + } + + public void setExact(String exact) { + this.exact = exact; + } + + @Override + public String toString() { + return "HeaderRule{" + + "regex='" + regex + '\'' + + ", caseInsensitive=" + caseInsensitive + + ", exact='" + exact + '\'' + + '}'; + } +} 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 new file mode 100644 index 00000000000..0700a2618d8 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.router.model; + +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 + * @since 2019/10/17 + **/ +public class PolicyRuleItem implements Comparable { + + private static final Logger LOGGER = LoggerFactory.getLogger(PolicyRuleItem.class); + + private Integer precedence; + + private Matcher 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 + * + */ + public void check() { + if (CollectionUtils.isEmpty(route)) { + throw new RouterIllegalParamException("canary rule list can not be null"); + } + int sum = 0; + for (RouteItem item : route) { + if (item.getWeight() == null) { + throw new RouterIllegalParamException("canary rule weight can not be null"); + } + sum += item.getWeight(); + } + if (sum > 100) { + LOGGER.warn("canary rule weight sum is more than 100"); + } else if (sum < 100) { + weightLess = true; + route.add(new RouteItem(100 - sum, null)); + } + } + + @Override + public int compareTo(PolicyRuleItem param) { + return Integer.compare(param.precedence, this.precedence); + } + + public Integer getPrecedence() { + return precedence; + } + + public void setPrecedence(Integer precedence) { + this.precedence = precedence; + } + + public Matcher getMatch() { + return match; + } + + public void setMatch(Matcher match) { + this.match = match; + } + + public List getRoute() { + return route; + } + + public void setRoute(List route) { + this.route = route; + } + + public Integer getTotal() { + return total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public boolean isWeightLess() { + return weightLess; + } + + 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{" + + "precedence=" + precedence + + ", match=" + match + + ", 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 new file mode 100644 index 00000000000..5f1d6e9f8df --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +/** + * @author GuoYl123 + * @since 2019/10/17 + **/ +public class RouteItem implements Comparable { + + private Integer weight; + /** + * for load balance + */ + private Integer currentWeight = 0; + + private Map tags; + + private TagItem tagitem; + + + public void initTagItem() { + if (tags != null) { + tagitem = new TagItem(tags); + } + } + + public void addCurrentWeight() { + currentWeight += weight; + } + + public void reduceCurrentWeight(int total) { + currentWeight -= total; + } + + public RouteItem() { + } + + public RouteItem(Integer weight, TagItem tags) { + this.weight = weight; + this.tagitem = tags; + } + + public Integer getWeight() { + return weight; + } + + public void setWeight(Integer weight) { + this.weight = weight; + } + + public Integer getCurrentWeight() { + return currentWeight; + } + + public void setCurrentWeight(Integer currentWeight) { + this.currentWeight = currentWeight; + } + + public Map getTags() { + return tags; + } + + public void setTags(Map tags) { + this.tags = tags; + } + + public TagItem getTagitem() { + return tagitem; + } + + public void setTagitem(TagItem tagitem) { + this.tagitem = tagitem; + } + + @Override + public int compareTo(RouteItem param) { + return Integer.compare(param.weight, this.weight); + } + + @Override + public String toString() { + return "RouteItem{" + + "weight=" + weight + + ", currentWeight=" + currentWeight + + ", tags=" + tags + + ", tagitem=" + tagitem + + '}'; + } +} 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 new file mode 100644 index 00000000000..232f1da6338 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/router/model/ServiceInfoCache.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.List; +import java.util.stream.Collectors; + +import org.springframework.util.CollectionUtils; + +/** + * @author GuoYl123 + * @since 2019/10/17 + **/ +public class ServiceInfoCache { + private final List allrule; + + /** + * for default version + */ + private TagItem latestVersionTag; + + public ServiceInfoCache(List policyRuleItemList) { + this.allrule = policyRuleItemList.stream().sorted().collect(Collectors.toList()); + + this.getAllrule().forEach(rule -> { + rule.getRoute().forEach(RouteItem::initTagItem); + if (!CollectionUtils.isEmpty(rule.getFallback())) { + rule.getFallback().forEach(RouteItem::initTagItem); + } + }); + } + + public TagItem getNextInvokeVersion(PolicyRuleItem policyRuleItem) { + List rule = policyRuleItem.getRoute(); + if (policyRuleItem.getTotal() == null) { + policyRuleItem.setTotal(rule.stream().mapToInt(RouteItem::getWeight).sum()); + } + 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()) { + maxIndex = i; + maxWeight = rule.get(i).getCurrentWeight(); + } + } + rule.get(maxIndex).reduceCurrentWeight(total); + return rule.get(maxIndex).getTagitem(); + } + + public List getAllrule() { + return allrule; + } + + public TagItem getLatestVersionTag() { + return latestVersionTag; + } + + public void setLatestVersionTag(TagItem latestVersionTag) { + this.latestVersionTag = latestVersionTag; + } + + @Override + public String toString() { + return "ServiceInfoCache{" + + "allrule=" + allrule + + ", latestVersionTag=" + latestVersionTag + + '}'; + } +} 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 new file mode 100644 index 00000000000..a7a20fab7ea --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/router/model/TagItem.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @author GuoYl123 + * @since 2019/10/17 + **/ +public class TagItem { + + private static final String VERSION = "version"; + + private String version; + + private Map param; + + public TagItem() { + } + + public TagItem(String version, Map param) { + this.version = version; + this.param = param; + } + + public TagItem(String version) { + this.version = version; + Map param = new HashMap<>(); + param.put(VERSION, version); + this.param = param; + } + + public TagItem(Map param) { + if (param.containsKey(VERSION)) { + this.version = param.get(VERSION); + } + this.param = param; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public Map getParam() { + return param; + } + + public void setParam(Map param) { + this.param = param; + } + + @Override + public int hashCode() { + return Objects.hash(getVersion(), getParam()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof TagItem)) { + return false; + } + TagItem tagItem = (TagItem) o; + return Objects.equals(getVersion(), tagItem.getVersion()) && + Objects.equals(getParam(), tagItem.getParam()); + } + + /** + * return match num + * + * @param item + * @return + */ + public int matchNum(TagItem item) { + int cnt = 0; + if (version != null && !version.equals(item.version)) { + return 0; + } + for (Map.Entry entry : param.entrySet()) { + if (item.getParam().containsKey(entry.getKey()) && + !item.getParam().get(entry.getKey()).equals(entry.getValue())) { + return 0; + } + cnt++; + } + return cnt; + } +} diff --git a/governance/src/main/java/org/apache/servicecomb/router/util/VersionCompareUtil.java b/governance/src/main/java/org/apache/servicecomb/router/util/VersionCompareUtil.java new file mode 100644 index 00000000000..1af7b521155 --- /dev/null +++ b/governance/src/main/java/org/apache/servicecomb/router/util/VersionCompareUtil.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.router.util; + +import org.apache.servicecomb.foundation.common.Version; + +public class VersionCompareUtil { + public static int compareVersion(String version1, String version2) { + return new Version(version1).compareTo(new Version(version2)); + } +} 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 new file mode 100644 index 00000000000..dedeba80053 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.util.Arrays; +import java.util.List; + +import org.apache.servicecomb.governance.handler.ext.AbstractFailurePredictor; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AbstractFailurePredictorTest { + class MyAbstractFailurePredictor extends AbstractFailurePredictor { + MyAbstractFailurePredictor() { + } + + @Override + protected String extractStatusCode(Object result) { + return (String) result; + } + + @Override + public boolean isFailedResult(List statusList, Throwable e) { + return super.isFailedResult(statusList, e); + } + } + + @Test + public void testCodeMatch() { + AbstractFailurePredictor predictor = new MyAbstractFailurePredictor(); + List statusList = Arrays.asList("500"); + Assertions.assertTrue(predictor.isFailedResult(statusList, "500")); + Assertions.assertFalse(predictor.isFailedResult(statusList, "502")); + Assertions.assertFalse(predictor.isFailedResult(statusList, "400")); + Assertions.assertFalse(predictor.isFailedResult(statusList, "444")); + + statusList = Arrays.asList("5x0"); + Assertions.assertTrue(predictor.isFailedResult(statusList, "500")); + Assertions.assertFalse(predictor.isFailedResult(statusList, "502")); + Assertions.assertFalse(predictor.isFailedResult(statusList, "400")); + Assertions.assertFalse(predictor.isFailedResult(statusList, "444")); + + statusList = Arrays.asList(null, "xx", "5x0"); + Assertions.assertTrue(predictor.isFailedResult(statusList, "500")); + Assertions.assertFalse(predictor.isFailedResult(statusList, "502")); + Assertions.assertFalse(predictor.isFailedResult(statusList, "400")); + Assertions.assertFalse(predictor.isFailedResult(statusList, "444")); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/BulkheadHandlerTest.java b/governance/src/test/java/org/apache/servicecomb/governance/BulkheadHandlerTest.java new file mode 100644 index 00000000000..59703d4ee7a --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/BulkheadHandlerTest.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.governance; + +import org.apache.servicecomb.governance.handler.BulkheadHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.policy.BulkheadPolicy; +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.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) +public class BulkheadHandlerTest { + private BulkheadHandler bulkheadHandler; + + @Autowired + public void setInstanceIsolationHandler(BulkheadHandler scbBulkheadHandler) { + this.bulkheadHandler = scbBulkheadHandler; + } + + @Test + public void testMatchPriorityPolicy() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/bulkhead"); + BulkheadPolicy policy = bulkheadHandler.matchPolicy(request); + Assertions.assertEquals("demo-bulkhead-priority", policy.getName()); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/CustomMatchTest.java b/governance/src/test/java/org/apache/servicecomb/governance/CustomMatchTest.java new file mode 100644 index 00000000000..a0e0c2d8ab9 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/CustomMatchTest.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.governance; + +import org.apache.servicecomb.governance.marker.CustomMatcher; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.marker.Matcher; +import org.apache.servicecomb.governance.marker.RequestProcessor; +import org.apache.servicecomb.governance.mockclasses.service.MockConfigurationForCustomMatcher; +import org.junit.Assert; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.junit.jupiter.api.Assertions; + +@SpringBootTest +@ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class, MockConfigurationForCustomMatcher.class}) +public class CustomMatchTest { + + private RequestProcessor requestProcessor; + + @Autowired + public void setRequestProcessor(RequestProcessor requestProcessor) { + this.requestProcessor = requestProcessor; + } + + private Matcher generateMatcher(String customMatcherHandler, String customMatcherParameters) { + CustomMatcher customMatcher = new CustomMatcher(); + customMatcher.setCustomMatcherHandler(customMatcherHandler); + customMatcher.setCustomMatcherParameters(customMatcherParameters); + Matcher matcher = new Matcher(); + matcher.setCustomMatcher(customMatcher); + return matcher; + } + + @Test + public void test_should_pass_when_value_class_empty() { + GovernanceRequest request = new GovernanceRequest(); + Matcher mockMatcher = generateMatcher("", ""); + Assert.assertTrue(this.requestProcessor.match(request, mockMatcher)); + mockMatcher = generateMatcher("", "bill"); + Assert.assertTrue(this.requestProcessor.match(request, mockMatcher)); + mockMatcher = generateMatcher("classWithAnnotation", ""); + Assert.assertTrue(this.requestProcessor.match(request, mockMatcher)); + } + + @Test + public void test_should_pass_when_value_class_match() { + GovernanceRequest request = new GovernanceRequest(); + Matcher mockMatcher = generateMatcher("classWithAnnotation", "bill"); + Assert.assertTrue(this.requestProcessor.match(request, mockMatcher)); + } + + @Test + public void test_should_throw_exception_when_class_not_found() { + GovernanceRequest request = new GovernanceRequest(); + try { + Matcher mockMatcher = generateMatcher("classWithAnnotationNotFound", "bill"); + this.requestProcessor.match(request, mockMatcher); + Assertions.fail("an exception is expected!"); + } catch (Exception e) { + Assert.assertTrue(e.getCause() instanceof ClassNotFoundException); + } + } + + @Test + public void test_should_pass_when_multiple_value() { + GovernanceRequest request = new GovernanceRequest(); + Matcher mockMatcher = generateMatcher("classWithAnnotation", "bill,bill2"); + Assert.assertTrue(this.requestProcessor.match(request, mockMatcher)); + } + + @Test + public void test_should_throw_exception_when_not_implements_interface() { + GovernanceRequest request = new GovernanceRequest(); + try { + Matcher mockMatcher = generateMatcher("classNotImplements", "bill,bill2"); + this.requestProcessor.match(request, mockMatcher); + Assertions.fail("an exception is expected!"); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains(RequestProcessor.errorMessageForNotImplements)); + } + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java b/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java new file mode 100644 index 00000000000..7733b8ed44f --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java @@ -0,0 +1,205 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.servicecomb.governance.handler.FaultInjectionHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.processor.injection.Fault; +import org.apache.servicecomb.governance.processor.injection.FaultInjectionDecorators; +import org.apache.servicecomb.governance.processor.injection.FaultInjectionDecorators.FaultInjectionDecorateCheckedSupplier; +import org.apache.servicecomb.governance.processor.injection.FaultInjectionException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) +public class FaultInjectionTest { + private FaultInjectionHandler faultInjectionHandler; + + private FaultInjectionHandler faultInjectionHandler2; + + @Autowired + public void setFaultInjectionHandler(FaultInjectionHandler scbFaultInjectionHandler, + @Qualifier("faultInjectionHandler2") FaultInjectionHandler faultInjectionHandler2) { + this.faultInjectionHandler = scbFaultInjectionHandler; + this.faultInjectionHandler2 = faultInjectionHandler2; + } + + public FaultInjectionTest() { + } + + @Test + public void test_delay_fault_injection_service_name_work() throws Throwable { + FaultInjectionDecorateCheckedSupplier ds = + FaultInjectionDecorators.ofCheckedSupplier(() -> "test"); + + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/faultInjectDelay"); + request.setServiceName("srcService"); + + Fault fault = faultInjectionHandler.getActuator(request); + ds.withFaultInjection(fault); + + Assertions.assertEquals("test", ds.get()); + + // flow control + CountDownLatch cd = new CountDownLatch(10); + AtomicBoolean expected = new AtomicBoolean(false); + AtomicBoolean notExpected = new AtomicBoolean(false); + for (int i = 0; i < 10; i++) { + new Thread(() -> { + try { + long startTime = System.currentTimeMillis(); + Object result = ds.get(); + if (!"test".equals(result)) { + notExpected.set(true); + } + // delayTime is 2S + if (System.currentTimeMillis() - startTime > 1000) { + expected.set(true); + } + } catch (Throwable e) { + notExpected.set(true); + } + cd.countDown(); + }).start(); + } + //timeout should be bigger than delayTime + cd.await(10, TimeUnit.SECONDS); + Assertions.assertFalse(notExpected.get()); + Assertions.assertTrue(expected.get()); + } + + @Test + public void test_abort_fault_injection_service_name_work() throws Throwable { + FaultInjectionDecorateCheckedSupplier ds = + FaultInjectionDecorators.ofCheckedSupplier(() -> "test"); + + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/faultInjectAbort"); + request.setServiceName("srcService"); + + Fault fault = faultInjectionHandler.getActuator(request); + ds.withFaultInjection(fault); + + Assertions.assertEquals("test", ds.get()); + + // flow control + CountDownLatch cd = new CountDownLatch(10); + AtomicBoolean expected = new AtomicBoolean(false); + AtomicBoolean notExpected = new AtomicBoolean(false); + for (int i = 0; i < 10; i++) { + new Thread(() -> { + try { + Object result = ds.get(); + if (!"test".equals(result)) { + notExpected.set(true); + } + } catch (FaultInjectionException e) { + expected.set(true); + } catch (Throwable e) { + notExpected.set(true); + } + cd.countDown(); + }).start(); + } + cd.await(1, TimeUnit.SECONDS); + Assertions.assertFalse(notExpected.get()); + Assertions.assertTrue(expected.get()); + } + + @Test + public void test_fallback_returnNull_work() throws Throwable { + FaultInjectionDecorateCheckedSupplier ds = + FaultInjectionDecorators.ofCheckedSupplier(() -> "test"); + + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/returnNull"); + request.setServiceName("returnNull"); + + Fault fault = faultInjectionHandler.getActuator(request); + ds.withFaultInjection(fault); + Assertions.assertEquals(null, ds.get()); + } + + @Test + public void test_fallback_ThrowException_work() throws Throwable { + FaultInjectionDecorateCheckedSupplier ds = + FaultInjectionDecorators.ofCheckedSupplier(() -> "test"); + + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/throwException"); + request.setServiceName("ThrowException"); + + Fault fault = faultInjectionHandler.getActuator(request); + ds.withFaultInjection(fault); + boolean expected = false; + try { + ds.get(); + } catch (FaultInjectionException e) { + if (e.getFaultResponse().getErrorCode() == 500) { + expected = true; + } + } + Assertions.assertEquals(true, expected); + } + + @Test + public void test_fallback_forceClosed_work() throws Throwable { + FaultInjectionDecorateCheckedSupplier ds = + FaultInjectionDecorators.ofCheckedSupplier(() -> "test"); + + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/forceClosed"); + request.setServiceName("forceClosed"); + + Fault fault = faultInjectionHandler.getActuator(request); + ds.withFaultInjection(fault); + Assertions.assertEquals("test", ds.get()); + } + + @Test + public void test_fallback_ThrowException_work_handler2() throws Throwable { + FaultInjectionDecorateCheckedSupplier ds = + FaultInjectionDecorators.ofCheckedSupplier(() -> "test"); + + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/throwException"); + request.setServiceName("ThrowException"); + + Fault fault = faultInjectionHandler2.getActuator(request); + ds.withFaultInjection(fault); + boolean expected = false; + try { + ds.get(); + } catch (FaultInjectionException e) { + if (e.getFaultResponse().getErrorCode() == 500) { + expected = true; + } + } + Assertions.assertEquals(true, expected); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/FlowControlTest.java b/governance/src/test/java/org/apache/servicecomb/governance/FlowControlTest.java new file mode 100644 index 00000000000..a4b751fd95d --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/FlowControlTest.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.servicecomb.governance.handler.RateLimitingHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +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.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +import io.github.resilience4j.decorators.Decorators; +import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RequestNotPermitted; + +@SpringBootTest +@ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) +public class FlowControlTest { + private RateLimitingHandler rateLimitingHandler; + + @Autowired + public void setRateLimitingHandler(RateLimitingHandler rateLimitingHandler) { + this.rateLimitingHandler = rateLimitingHandler; + } + + public FlowControlTest() { + } + + @Test + public void test_rate_limiting_work() throws Throwable { + DecorateCheckedSupplier ds = Decorators.ofCheckedSupplier(() -> "test"); + + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/hello"); + + RateLimiter rateLimiter = rateLimitingHandler.getActuator(request); + ds.withRateLimiter(rateLimiter); + + Assertions.assertEquals("test", ds.get()); + + // flow control + CountDownLatch cd = new CountDownLatch(10); + AtomicBoolean expected = new AtomicBoolean(false); + AtomicBoolean notExpected = new AtomicBoolean(false); + for (int i = 0; i < 10; i++) { + new Thread(() -> { + try { + Object result = ds.get(); + if (!"test".equals(result)) { + notExpected.set(true); + } + } catch (Throwable e) { + if (e instanceof RequestNotPermitted) { + expected.set(true); + } else { + notExpected.set(true); + } + } + cd.countDown(); + }).start(); + } + cd.await(1, TimeUnit.SECONDS); + Assertions.assertTrue(expected.get()); + Assertions.assertFalse(notExpected.get()); + } + + @Test + public void test_rate_limiting_service_name_work() throws Throwable { + DecorateCheckedSupplier ds = Decorators.ofCheckedSupplier(() -> "test"); + + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/helloServiceName"); + request.setServiceName("srcService"); + + RateLimiter rateLimiter = rateLimitingHandler.getActuator(request); + ds.withRateLimiter(rateLimiter); + + Assertions.assertEquals("test", ds.get()); + + // flow control + CountDownLatch cd = new CountDownLatch(10); + AtomicBoolean expected = new AtomicBoolean(false); + AtomicBoolean notExpected = new AtomicBoolean(false); + for (int i = 0; i < 10; i++) { + new Thread(() -> { + try { + Object result = ds.get(); + if (!"test".equals(result)) { + notExpected.set(true); + } + } catch (Throwable e) { + if (e instanceof RequestNotPermitted) { + expected.set(true); + } else { + notExpected.set(true); + } + } + cd.countDown(); + }).start(); + } + cd.await(1, TimeUnit.SECONDS); + Assertions.assertTrue(expected.get()); + Assertions.assertFalse(notExpected.get()); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/GovernanceCacheHandlerTest.java b/governance/src/test/java/org/apache/servicecomb/governance/GovernanceCacheHandlerTest.java new file mode 100644 index 00000000000..ac26c3f3bdd --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/GovernanceCacheHandlerTest.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.governance; + + +import org.apache.servicecomb.governance.handler.GovernanceCacheHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.policy.GovernanceCachePolicy; +import org.apache.servicecomb.governance.service.GovernanceCache; +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.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) +public class GovernanceCacheHandlerTest { + private GovernanceCacheHandler governanceCacheHandler; + + @Autowired + public void setInstanceIsolationHandler(@Autowired GovernanceCacheHandler governanceCacheHandler) { + this.governanceCacheHandler = governanceCacheHandler; + } + + @Test + public void testMatchPriorityPolicy() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/governanceCache"); + GovernanceCachePolicy policy = governanceCacheHandler.matchPolicy(request); + Assertions.assertEquals("demo-governanceCache", policy.getName()); + GovernanceCache governanceCache = governanceCacheHandler.getActuator(request); + governanceCache.putValueIntoCache("governance", "Cache"); + Object cache = governanceCache.getValueFromCache("governance"); + Assertions.assertEquals("Cache", cache); + governanceCache.putValueIntoCache("response", null); + Object response = governanceCache.getValueFromCache("response"); + Assertions.assertNull(response); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java b/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java new file mode 100644 index 00000000000..5b71a2ac3b8 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java @@ -0,0 +1,484 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.time.Duration; +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.Matcher; +import org.apache.servicecomb.governance.marker.TrafficMarker; +import org.apache.servicecomb.governance.policy.AbstractPolicy; +import org.apache.servicecomb.governance.policy.BulkheadPolicy; +import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; +import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; +import org.apache.servicecomb.governance.policy.GovernanceCachePolicy; +import org.apache.servicecomb.governance.policy.RateLimitingPolicy; +import org.apache.servicecomb.governance.policy.RetryPolicy; +import org.apache.servicecomb.governance.policy.TimeLimiterPolicy; +import org.apache.servicecomb.governance.processor.injection.FaultInjectionConst; +import org.apache.servicecomb.governance.properties.BulkheadProperties; +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.GovernanceProperties; +import org.apache.servicecomb.governance.properties.InstanceIsolationProperties; +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.junit.jupiter.api.AfterEach; +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.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(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) +public class GovernancePropertiesTest { + private List> propertiesList; + + private MatchProperties matchProperties; + + private BulkheadProperties bulkheadProperties; + + private CircuitBreakerProperties circuitBreakerProperties; + + private TimeLimiterProperties timeLimiterProperties; + + private GovernanceCacheProperties governanceCacheProperties; + + private InstanceIsolationProperties instanceIsolationProperties; + + private RateLimitProperties rateLimitProperties; + + private RetryProperties retryProperties; + + private FaultInjectionProperties faultInjectionProperties; + + private Environment environment; + + @Autowired + public void setPropertiesList(List> propertiesList) { + this.propertiesList = propertiesList; + } + + @Autowired + public void setMatchProperties(MatchProperties scbMatchProperties) { + this.matchProperties = scbMatchProperties; + } + + @Autowired + public void setBulkheadProperties(BulkheadProperties scbBulkheadProperties) { + this.bulkheadProperties = scbBulkheadProperties; + } + + @Autowired + public void setCircuitBreakerProperties(CircuitBreakerProperties scbCircuitBreakerProperties) { + this.circuitBreakerProperties = scbCircuitBreakerProperties; + } + + @Autowired + public void setTimeLimiterProperties(TimeLimiterProperties scbTimeLimiterProperties) { + this.timeLimiterProperties = scbTimeLimiterProperties; + } + + @Autowired + public void setGovernanceCacheProperties(GovernanceCacheProperties scbGovernanceCacheProperties) { + this.governanceCacheProperties = scbGovernanceCacheProperties; + } + + @Autowired + public void setRateLimitProperties(RateLimitProperties scbRateLimitProperties) { + this.rateLimitProperties = scbRateLimitProperties; + } + + @Autowired + public void setInstanceIsolationProperties(InstanceIsolationProperties scbInstanceIsolationProperties) { + this.instanceIsolationProperties = scbInstanceIsolationProperties; + } + + @Autowired + public void setRetryProperties(RetryProperties scbRetryProperties) { + this.retryProperties = scbRetryProperties; + } + + @Autowired + public void setFaultInjectionProperties( + FaultInjectionProperties scbFaultInjectionProperties) { + this.faultInjectionProperties = scbFaultInjectionProperties; + } + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + public void setDynamicValues(Map dynamicValues) { + this.dynamicValues = dynamicValues; + } + + public GovernancePropertiesTest() { + } + + private Map dynamicValues = new HashMap<>(); + + private static final float DELTA = 0.0000001f; + + @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]); + } + }); + } + + @AfterEach + public void tearDown() { + Set keys = dynamicValues.keySet(); + keys.forEach(k -> dynamicValues.put(k, null)); + GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); + } + + @Test + public void test_all_bean_is_loaded() { + Assertions.assertEquals(16, propertiesList.size()); + } + + @Test + public void test_match_properties_successfully_loaded() { + Map markers = matchProperties.getParsedEntity(); + TrafficMarker demoRateLimiting = markers.get("demo-rateLimiting"); + List matchers = demoRateLimiting.getMatches(); + Assertions.assertEquals(1, matchers.size()); + Matcher matcher = matchers.get(0); + Assertions.assertEquals("/hello", matcher.getApiPath().get("exact")); + + TrafficMarker demoBulkhead = markers.get("demo-bulkhead"); + matchers = demoBulkhead.getMatches(); + Assertions.assertEquals(2, matchers.size()); + matcher = matchers.get(0); + Assertions.assertEquals("/bulkhead", matcher.getApiPath().get("exact")); + Assertions.assertEquals("matchPath", matcher.getName()); + } + + @Test + public void test_match_properties_delete() { + Map markers = matchProperties.getParsedEntity(); + Assertions.assertEquals(null, markers.get("test")); + dynamicValues.put("servicecomb.matchGroup.test", "matches:\n" + + " - apiPath:\n" + + " exact: \"/hello2\"\n" + + " name: match0"); + GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); + markers = matchProperties.getParsedEntity(); + Assertions.assertEquals(1, markers.get("test").getMatches().size()); + tearDown(); + markers = matchProperties.getParsedEntity(); + Assertions.assertEquals(null, markers.get("test")); + } + + @Test + public void test_match_properties_changed() { + dynamicValues.put("servicecomb.matchGroup.demo-rateLimiting", "matches:\n" + + " - apiPath:\n" + + " exact: \"/hello2\"\n" + + " name: match0"); + dynamicValues.put("servicecomb.matchGroup.demo-rateLimiting2", "matches:\n" + + " - apiPath:\n" + + " exact: \"/hello2\"\n" + + " name: match0"); + + GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); + + Map markers = matchProperties.getParsedEntity(); + TrafficMarker demoRateLimiting = markers.get("demo-rateLimiting"); + List matchers = demoRateLimiting.getMatches(); + Assertions.assertEquals(1, matchers.size()); + Matcher matcher = matchers.get(0); + Assertions.assertEquals("/hello2", matcher.getApiPath().get("exact")); + + demoRateLimiting = markers.get("demo-rateLimiting2"); + matchers = demoRateLimiting.getMatches(); + Assertions.assertEquals(1, matchers.size()); + matcher = matchers.get(0); + Assertions.assertEquals("/hello2", matcher.getApiPath().get("exact")); + } + + @Test + public void test_bulkhead_properties_changed() { + dynamicValues.put("servicecomb.bulkhead.demo-bulkhead", "rules:\n" + + "maxConcurrentCalls: 2\n" + + "maxWaitDuration: 2000"); + dynamicValues.put("servicecomb.bulkhead.bulkhead1", "rules:\n" + + "maxConcurrentCalls: 3\n" + + "maxWaitDuration: 3000"); + + GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); + + Map policies = bulkheadProperties.getParsedEntity(); + Assertions.assertEquals(3, policies.size()); + BulkheadPolicy policy = policies.get("demo-bulkhead"); + Assertions.assertEquals(2, policy.getMaxConcurrentCalls()); + Assertions.assertEquals(2000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); + + policies = bulkheadProperties.getParsedEntity(); + Assertions.assertEquals(3, policies.size()); + policy = policies.get("bulkhead1"); + Assertions.assertEquals(3, policy.getMaxConcurrentCalls()); + Assertions.assertEquals(3000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); + Assertions.assertEquals("bulkhead1", policy.getName()); + } + + @Test + public void test_bulkhead_properties_bound() { + dynamicValues.put("servicecomb.bulkhead.test-bulkhead1", "rules:\n" + + "maxConcurrentCalls: 0\n" + + "maxWaitDuration: 2000"); + dynamicValues.put("servicecomb.bulkhead.test-bulkhead2", "rules:\n" + + "maxConcurrentCalls: 1000\n" + + "maxWaitDuration: 0"); + dynamicValues.put("servicecomb.bulkhead.test-bulkhead3", "rules:\n" + + "maxConcurrentCalls: 0\n" + + "maxWaitDuration: 2S"); + + GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); + + Map policies = bulkheadProperties.getParsedEntity(); + Assertions.assertEquals(5, policies.size()); + BulkheadPolicy policy = policies.get("test-bulkhead1"); + Assertions.assertEquals(0, policy.getMaxConcurrentCalls()); + Assertions.assertEquals(2000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); + + policy = policies.get("test-bulkhead2"); + Assertions.assertEquals(1000, policy.getMaxConcurrentCalls()); + Assertions.assertEquals(0, Duration.parse(policy.getMaxWaitDuration()).toMillis()); + Assertions.assertEquals("test-bulkhead2", policy.getName()); + + policy = policies.get("test-bulkhead3"); + Assertions.assertEquals(0, policy.getMaxConcurrentCalls()); + Assertions.assertEquals(2000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); + Assertions.assertEquals("test-bulkhead3", policy.getName()); + } + + @Test + public void test_bulkhead_properties_successfully_loaded() { + Map policies = bulkheadProperties.getParsedEntity(); + Assertions.assertEquals(2, policies.size()); + BulkheadPolicy policy = policies.get("demo-bulkhead"); + Assertions.assertEquals(1, policy.getMaxConcurrentCalls()); + Assertions.assertEquals(3000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); + } + + @Test + public void test_timelimiter_properties_successfully_loaded() { + Map policies = timeLimiterProperties.getParsedEntity(); + Assertions.assertEquals(2, policies.size()); + TimeLimiterPolicy timeLimiterPolicy = policies.get("demo-timeLimiter-other"); + Assertions.assertEquals(2000, Duration.parse(timeLimiterPolicy.getTimeoutDuration()).toMillis()); + Assertions.assertEquals(false, timeLimiterPolicy.isCancelRunningFuture()); + } + + @Test + public void test_timelimiter_properties_bound() { + dynamicValues.put("servicecomb.timeLimiter.name1", "rules:\n" + + "timeoutDuration: 5000\n" + + "cancelRunningFuture: false"); + + GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); + + Map policies = timeLimiterProperties.getParsedEntity(); + Assertions.assertEquals(3, policies.size()); + TimeLimiterPolicy policy = policies.get("name1"); + Assertions.assertEquals(false, policy.isCancelRunningFuture()); + Assertions.assertEquals(5000, Duration.parse(policy.getTimeoutDuration()).toMillis()); + } + + @Test + public void test_governanceCache_properties_successfully_loaded() { + Map policies = governanceCacheProperties.getParsedEntity(); + Assertions.assertEquals(2, policies.size()); + GovernanceCachePolicy governanceCachePolicy = policies.get("demo-governanceCache-other"); + Assertions.assertEquals(15, governanceCachePolicy.getConcurrencyLevel()); + Assertions.assertEquals(50000, governanceCachePolicy.getMaximumSize()); + Assertions.assertEquals(666666, Duration.parse(governanceCachePolicy.getTtl()).toMillis()); + } + + @Test + public void test_governanceCache_properties_bound() { + dynamicValues.put("servicecomb.cache.name1", "rules:\n" + + "ttl: 3000000\n" + + "maximumSize: 2000\n" + + "concurrencyLevel: 6"); + + GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); + + Map policies = governanceCacheProperties.getParsedEntity(); + Assertions.assertEquals(3, policies.size()); + GovernanceCachePolicy policy = policies.get("name1"); + Assertions.assertEquals(3000000, Duration.parse(policy.getTtl()).toMillis()); + Assertions.assertEquals(2000, policy.getMaximumSize()); + Assertions.assertEquals(6, policy.getConcurrencyLevel()); + } + + @Test + public void test_circuit_breaker_properties_successfully_loaded() { + Map policies = circuitBreakerProperties.getParsedEntity(); + Assertions.assertEquals(1, policies.size()); + CircuitBreakerPolicy policy = policies.get("demo-circuitBreaker"); + Assertions.assertEquals(2, policy.getMinimumNumberOfCalls()); + Assertions.assertEquals("2", policy.getSlidingWindowSize()); + } + + @Test + public void test_circuit_breaker_properties_Of_windows_size() { + dynamicValues.put("servicecomb.circuitBreaker.name1", "rules:\n" + + "slidingWindowType: count\n" + + "slidingWindowSize: 2"); + dynamicValues.put("servicecomb.circuitBreaker.name2", "rules:\n" + + "slidingWindowType: time\n" + + "slidingWindowSize: 2"); + dynamicValues.put("servicecomb.circuitBreaker.name3", "rules:\n" + + "slidingWindowType: test\n" + + "slidingWindowSize: 1M"); + + GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); + + Map policies = circuitBreakerProperties.getParsedEntity(); + Assertions.assertEquals(4, policies.size()); + CircuitBreakerPolicy policy = policies.get("name1"); + Assertions.assertEquals("count", policy.getSlidingWindowType()); + Assertions.assertEquals("2", policy.getSlidingWindowSize()); + + policy = policies.get("name2"); + Assertions.assertEquals("time", policy.getSlidingWindowType()); + Assertions.assertEquals("2", policy.getSlidingWindowSize()); + + policy = policies.get("name3"); + Assertions.assertEquals("60", policy.getSlidingWindowSize()); + } + + @Test + public void test_circuit_breaker_properties_Of_type() { + dynamicValues.put("servicecomb.circuitBreaker.type1", "rules:\n" + + "failureRateThreshold: 20\n" + + "slowCallRateThreshold: 100"); + dynamicValues.put("servicecomb.circuitBreaker.type2", "rules:\n" + + "failureRateThreshold: 20.33\n" + + "slowCallRateThreshold: 0.01"); + + GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); + + Map policies = circuitBreakerProperties.getParsedEntity(); + CircuitBreakerPolicy policy = policies.get("type1"); + Assertions.assertEquals(20.0f, policy.getFailureRateThreshold(), DELTA); + Assertions.assertEquals(100.0f, policy.getSlowCallRateThreshold(), DELTA); + + policy = policies.get("type2"); + Assertions.assertEquals(20.33f, policy.getFailureRateThreshold(), DELTA); + Assertions.assertEquals(0.01f, policy.getSlowCallRateThreshold(), DELTA); + } + + @Test + public void test_rate_limit_properties_successfully_loaded() { + Map policies = rateLimitProperties.getParsedEntity(); + Assertions.assertEquals(2, policies.size()); + RateLimitingPolicy policy = policies.get("demo-rateLimiting"); + Assertions.assertEquals(1, policy.getRate()); + } + + @Test + public void test_retry_properties_successfully_loaded() { + Map policies = retryProperties.getParsedEntity(); + Assertions.assertEquals(1, policies.size()); + RetryPolicy policy = policies.get("demo-retry"); + Assertions.assertEquals(3, policy.getMaxAttempts()); + } + + + @Test + public void test_properties_changed_to_duration() { + dynamicValues.put("servicecomb.bulkhead.test1", "rules:\n" + + "maxConcurrentCalls: 2\n" + + "maxWaitDuration: 2S"); + dynamicValues.put("servicecomb.bulkhead.test2", "rules:\n" + + "maxConcurrentCalls: 3\n" + + "maxWaitDuration: 1M"); + + GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet()))); + + Map policies = bulkheadProperties.getParsedEntity(); + Assertions.assertEquals(4, policies.size()); + BulkheadPolicy policy = policies.get("test1"); + Assertions.assertEquals(2000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); + + policies = bulkheadProperties.getParsedEntity(); + Assertions.assertEquals(4, policies.size()); + policy = policies.get("test2"); + Assertions.assertEquals(60000, Duration.parse(policy.getMaxWaitDuration()).toMillis()); + } + + @Test + public void test_instance_isolation_properties_successfully_loaded() { + Map policies = instanceIsolationProperties.getParsedEntity(); + Assertions.assertEquals(1, policies.size()); + CircuitBreakerPolicy policy = policies.get("demo-allOperation"); + Assertions.assertEquals(2, policy.getMinimumNumberOfCalls()); + Assertions.assertEquals("2", policy.getSlidingWindowSize()); + } + + @Test + public void test_fault_injection_properties_successfully_loaded() { + Map policies = faultInjectionProperties.getParsedEntity(); + Assertions.assertEquals(5, policies.size()); + FaultInjectionPolicy policy = policies.get("demo-faultInjectDelay"); + Assertions.assertEquals(FaultInjectionConst.TYPE_DELAY, policy.getType()); + Assertions.assertEquals(2000, policy.getDelayTimeToMillis()); + Assertions.assertEquals(100, policy.getPercentage()); + + policy = policies.get("demo-faultInjectAbort"); + Assertions.assertEquals(FaultInjectionConst.TYPE_ABORT, policy.getType()); + Assertions.assertEquals(50, policy.getPercentage()); + Assertions.assertEquals(500, policy.getErrorCode()); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/IdentifierRateLimitingHandlerTest.java b/governance/src/test/java/org/apache/servicecomb/governance/IdentifierRateLimitingHandlerTest.java new file mode 100644 index 00000000000..1aed8e4a302 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/IdentifierRateLimitingHandlerTest.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.servicecomb.governance.handler.IdentifierRateLimitingHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +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.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +import io.github.resilience4j.decorators.Decorators; +import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RequestNotPermitted; + +@SpringBootTest +@ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) +public class IdentifierRateLimitingHandlerTest { + private IdentifierRateLimitingHandler rateLimitingHandler; + + @Autowired + public void setRateLimitingHandler(IdentifierRateLimitingHandler rateLimitingHandler) { + this.rateLimitingHandler = rateLimitingHandler; + } + + public IdentifierRateLimitingHandlerTest() { + } + + @Test + public void test_rate_limiting_work() throws Throwable { + DecorateCheckedSupplier ds = Decorators.ofCheckedSupplier(() -> "test"); + + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/hello"); + Map headers = new HashMap<>(); + headers.put("test", "1234"); + request.setHeaders(headers); + + RateLimiter rateLimiter = rateLimitingHandler.getActuator(request); + ds.withRateLimiter(rateLimiter); + + Assertions.assertEquals("test", ds.get()); + + // flow control + CountDownLatch cd = new CountDownLatch(10); + AtomicBoolean expected = new AtomicBoolean(false); + AtomicBoolean notExpected = new AtomicBoolean(false); + for (int i = 0; i < 10; i++) { + new Thread(() -> { + try { + Object result = ds.get(); + if (!"test".equals(result)) { + notExpected.set(true); + } + } catch (Throwable e) { + if (e instanceof RequestNotPermitted) { + expected.set(true); + } else { + notExpected.set(true); + } + } + cd.countDown(); + }).start(); + } + cd.await(1, TimeUnit.SECONDS); + Assertions.assertTrue(expected.get()); + Assertions.assertFalse(notExpected.get()); + } + + @Test + public void test_rate_limiting_service_name_work() throws Throwable { + DecorateCheckedSupplier ds = Decorators.ofCheckedSupplier(() -> "test"); + + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/helloServiceName"); + request.setServiceName("srcService"); + Map headers = new HashMap<>(); + headers.put("test", "1234"); + request.setHeaders(headers); + + RateLimiter rateLimiter = rateLimitingHandler.getActuator(request); + ds.withRateLimiter(rateLimiter); + + Assertions.assertEquals("test", ds.get()); + + headers = new HashMap<>(); + headers.put("test", "12345"); + request.setHeaders(headers); + + RateLimiter rateLimiter2 = rateLimitingHandler.getActuator(request); + DecorateCheckedSupplier ds2 = Decorators.ofCheckedSupplier(() -> "test2"); + ds2.withRateLimiter(rateLimiter2); + Assertions.assertEquals("test2", ds2.get()); + + // flow control + CountDownLatch cd = new CountDownLatch(10); + AtomicBoolean expected = new AtomicBoolean(false); + AtomicBoolean notExpected = new AtomicBoolean(false); + for (int i = 0; i < 10; i++) { + new Thread(() -> { + try { + Object result = ds.get(); + if (!"test".equals(result)) { + notExpected.set(true); + } + } catch (Throwable e) { + if (e instanceof RequestNotPermitted) { + expected.set(true); + } else { + notExpected.set(true); + } + } + cd.countDown(); + }).start(); + } + cd.await(1, TimeUnit.SECONDS); + Assertions.assertTrue(expected.get()); + Assertions.assertFalse(notExpected.get()); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/InstanceBulkheadHandlerTest.java b/governance/src/test/java/org/apache/servicecomb/governance/InstanceBulkheadHandlerTest.java new file mode 100644 index 00000000000..de5213ef520 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/InstanceBulkheadHandlerTest.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.governance; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.governance.handler.InstanceBulkheadHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +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.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +import io.github.resilience4j.bulkhead.Bulkhead; +import io.github.resilience4j.bulkhead.BulkheadFullException; +import io.github.resilience4j.decorators.Decorators; +import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; + +@SpringBootTest +@ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) +public class InstanceBulkheadHandlerTest { + private InstanceBulkheadHandler instanceBulkheadHandler; + + @Autowired + public void setInstanceBulkheadHandler(InstanceBulkheadHandler instanceBulkheadHandler) { + this.instanceBulkheadHandler = instanceBulkheadHandler; + } + + @Test + public void test_instance_bulkhead_work() throws Throwable { + + // instance1 + DecorateCheckedSupplier dsInstance1 = Decorators.ofCheckedSupplier(() -> "wake"); + + GovernanceRequest requestInstance1 = new GovernanceRequest(); + requestInstance1.setInstanceId("instance01"); + requestInstance1.setServiceName("service01"); + requestInstance1.setApiPath("/test"); + + Bulkhead bulkheadInstance1 = instanceBulkheadHandler.getActuator(requestInstance1); + dsInstance1.withBulkhead(bulkheadInstance1); + + // instance2 + DecorateCheckedSupplier dsInstance2 = Decorators.ofCheckedSupplier(() -> { + Thread.sleep(1000); + return "sleep"; + }); + + GovernanceRequest requestInstance2 = new GovernanceRequest(); + requestInstance2.setInstanceId("instance02"); + requestInstance2.setServiceName("service01"); + requestInstance2.setApiPath("/test"); + + Bulkhead bulkheadInstance2 = instanceBulkheadHandler.getActuator(requestInstance2); + dsInstance2.withBulkhead(bulkheadInstance2); + + Executor executor = Executors.newFixedThreadPool(4); + AtomicInteger wakeCount = new AtomicInteger(0); + AtomicInteger sleepCount = new AtomicInteger(0); + AtomicInteger errorCount = new AtomicInteger(0); + AtomicInteger rejectCount = new AtomicInteger(0); + CountDownLatch countDownLatch = new CountDownLatch(100); + for (int i = 0; i < 100; i++) { + final int num = i; + executor.execute(() -> { + // 50% for each server + if (num % 2 == 0) { + runCommand(dsInstance1, wakeCount, sleepCount, errorCount, rejectCount, countDownLatch); + } else { + runCommand(dsInstance2, wakeCount, sleepCount, errorCount, rejectCount, countDownLatch); + } + }); + } + countDownLatch.await(100, TimeUnit.SECONDS); + Assertions.assertEquals(50, wakeCount.get()); + Assertions.assertEquals(2, sleepCount.get()); + Assertions.assertEquals(0, errorCount.get()); + Assertions.assertEquals(48, rejectCount.get()); + } + + private void runCommand(DecorateCheckedSupplier ds, AtomicInteger wakeCount, AtomicInteger sleepCount, + AtomicInteger errorCount, AtomicInteger rejectCount, CountDownLatch countDownLatch) { + try { + String result = ds.get(); + if ("wake".equals(result)) { + wakeCount.incrementAndGet(); + } else if ("sleep".equals(result)) { + sleepCount.incrementAndGet(); + } else { + errorCount.incrementAndGet(); + } + } catch (BulkheadFullException e) { + rejectCount.incrementAndGet(); + } catch (Throwable e) { + errorCount.incrementAndGet(); + } + countDownLatch.countDown(); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/InstanceIsolationTest.java b/governance/src/test/java/org/apache/servicecomb/governance/InstanceIsolationTest.java new file mode 100644 index 00000000000..0a6c8f23e14 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/InstanceIsolationTest.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.net.ConnectException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.governance.handler.InstanceIsolationHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +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.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +import io.github.resilience4j.circuitbreaker.CallNotPermittedException; +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.decorators.Decorators; +import io.github.resilience4j.decorators.Decorators.DecorateCheckedSupplier; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.prometheusmetrics.PrometheusMeterRegistry; + +@SpringBootTest +@ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) +public class InstanceIsolationTest { + private InstanceIsolationHandler instanceIsolationHandler; + + private MeterRegistry meterRegistry; + + @Autowired + public void setInstanceIsolationHandler(InstanceIsolationHandler instanceIsolationHandler, + MeterRegistry meterRegistry) { + this.instanceIsolationHandler = instanceIsolationHandler; + this.meterRegistry = meterRegistry; + } + + @Test + public void test_instance_isolation_work() throws Throwable { + AtomicInteger counter = new AtomicInteger(0); + + DecorateCheckedSupplier ds = Decorators.ofCheckedSupplier(() -> { + int run = counter.getAndIncrement(); + if (run == 0) { + return "test"; + } + if (run == 1) { + throw new ConnectException("test exception"); + } + return "test"; + }); + + DecorateCheckedSupplier ds2 = Decorators.ofCheckedSupplier(() -> "test"); + + GovernanceRequest request = new GovernanceRequest(); + request.setInstanceId("instance01"); + request.setServiceName("service01"); + request.setApiPath("/test"); + + CircuitBreaker circuitBreaker = instanceIsolationHandler.getActuator(request); + ds.withCircuitBreaker(circuitBreaker); + + // isolation from error + Assertions.assertEquals("test", ds.get()); + Assertions.assertThrows(ConnectException.class, ds::get); + + Assertions.assertThrows(CallNotPermittedException.class, ds::get); + Assertions.assertThrows(CallNotPermittedException.class, ds::get); + + Assertions.assertThrows(CallNotPermittedException.class, ds::get); + Assertions.assertThrows(CallNotPermittedException.class, ds::get); + Assertions.assertThrows(CallNotPermittedException.class, ds::get); + + // isolation do not influence other instances + GovernanceRequest request2 = new GovernanceRequest(); + request2.setInstanceId("instance02"); + request2.setServiceName("service01"); + request2.setApiPath("/test"); + + CircuitBreaker circuitBreaker2 = instanceIsolationHandler.getActuator(request2); + ds2.withCircuitBreaker(circuitBreaker2); + + Assertions.assertEquals("test", ds2.get()); + Assertions.assertEquals("test", ds2.get()); + Assertions.assertEquals("test", ds2.get()); + Assertions.assertEquals("test", ds2.get()); + + assertMetricsNotFinish(); + + // recover from isolation + Thread.sleep(1000); + + Assertions.assertEquals("test", ds.get()); + Assertions.assertEquals("test", ds.get()); + Assertions.assertEquals("test", ds2.get()); + Assertions.assertEquals("test", ds2.get()); + + assertMetricsFinish(); + } + + private void assertMetricsNotFinish() { + String result = ((PrometheusMeterRegistry) meterRegistry).scrape(); + Assertions.assertTrue(result.contains( + "servicecomb_instanceIsolation_state{name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance01\",state=\"open\"} 1.0")); + Assertions.assertTrue(result.contains( + "servicecomb_instanceIsolation_state{name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance02\",state=\"closed\"} 1.0")); + Assertions.assertTrue(result.contains( + "servicecomb_instanceIsolation_calls_seconds_count{kind=\"successful\",name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance01\"} 1")); + Assertions.assertTrue(result.contains( + "servicecomb_instanceIsolation_calls_seconds_count{kind=\"successful\",name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance02\"} 4")); + } + + private void assertMetricsFinish() { + String result = ((PrometheusMeterRegistry) meterRegistry).scrape(); + Assertions.assertTrue(result.contains( + "servicecomb_instanceIsolation_state{name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance01\",state=\"closed\"} 1.0")); + Assertions.assertTrue(result.contains( + "servicecomb_instanceIsolation_state{name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance02\",state=\"closed\"} 1.0")); + Assertions.assertTrue(result.contains( + "servicecomb_instanceIsolation_calls_seconds_count{kind=\"successful\",name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance01\"} 3")); + Assertions.assertTrue(result.contains( + "servicecomb_instanceIsolation_calls_seconds_count{kind=\"successful\",name=\"servicecomb.instanceIsolation.demo-allOperation.service01.instance02\"} 6")); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/LoadBalancerTest.java b/governance/src/test/java/org/apache/servicecomb/governance/LoadBalancerTest.java new file mode 100644 index 00000000000..9973cbc56c7 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/LoadBalancerTest.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.governance; + +import org.apache.servicecomb.governance.handler.LoadBalanceHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.processor.loadbanlance.LoadBalance; +import org.junit.Assert; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) +public class LoadBalancerTest { + private LoadBalanceHandler loadBalanceHandler; + + @Autowired + public void setLoadBalanceHandler(LoadBalanceHandler loadBalanceHandler) { + this.loadBalanceHandler = loadBalanceHandler; + } + + public LoadBalancerTest() { + + } + + @Test + public void test_loadbalance_random() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/loadrandom"); + request.setServiceName("loadrandom"); + LoadBalance loadBalance = loadBalanceHandler.getActuator(request); + Assert.assertEquals("Random", loadBalance.getRule()); + } + + @Test + public void test_loadbalance_roundRobin() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/loadroundRobin"); + request.setServiceName("loadroundRobin"); + LoadBalance loadBalance = loadBalanceHandler.getActuator(request); + Assert.assertEquals("RoundRobin", loadBalance.getRule()); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/MapperTest.java b/governance/src/test/java/org/apache/servicecomb/governance/MapperTest.java new file mode 100644 index 00000000000..4a4607fefc3 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/MapperTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.governance.handler.MapperHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.processor.mapping.Mapper; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) +public class MapperTest { + private MapperHandler mapperHandler; + + private MapperHandler mapperHandler2; + + @Autowired + public void setMapperHandler(MapperHandler scbMapperHandler, @Qualifier("mapperHandler2") MapperHandler mapperHandler2) { + this.mapperHandler = scbMapperHandler; + this.mapperHandler2 = mapperHandler2; + } + + @Test + public void test_mapper_work() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/mapper/v1"); + Mapper mapper = mapperHandler.getActuator(request); + Assertions.assertEquals(2, mapper.target().size()); + Assertions.assertEquals("127.0.0.1", mapper.target().get("host")); + Assertions.assertEquals("8080", mapper.target().get("port")); + } + + @Test + public void test_mapper_query_work() { + GovernanceRequest request = new GovernanceRequest(); + Map 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(); + request.setApiPath("/mapper/v1"); + Mapper mapper = mapperHandler2.getActuator(request); + Assertions.assertEquals(2, mapper.target().size()); + Assertions.assertEquals("127.0.0.1", mapper.target().get("host")); + Assertions.assertEquals("9090", mapper.target().get("port")); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/MockCircuitBreakerExtension.java b/governance/src/test/java/org/apache/servicecomb/governance/MockCircuitBreakerExtension.java new file mode 100644 index 00000000000..c66cac8fbfa --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/MockCircuitBreakerExtension.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.governance; + +import org.apache.servicecomb.governance.handler.ext.AbstractCircuitBreakerExtension; + +public class MockCircuitBreakerExtension extends AbstractCircuitBreakerExtension { + @Override + protected String extractStatusCode(Object result) { + return "200"; + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/MockConfiguration.java b/governance/src/test/java/org/apache/servicecomb/governance/MockConfiguration.java new file mode 100644 index 00000000000..85c04f39590 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/MockConfiguration.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import org.apache.servicecomb.governance.handler.BulkheadHandler; +import org.apache.servicecomb.governance.handler.CircuitBreakerHandler; +import org.apache.servicecomb.governance.handler.FaultInjectionHandler; +import org.apache.servicecomb.governance.handler.MapperHandler; +import org.apache.servicecomb.governance.handler.ext.AbstractCircuitBreakerExtension; +import org.apache.servicecomb.governance.properties.BulkheadProperties; +import org.apache.servicecomb.governance.properties.CircuitBreakerProperties; +import org.apache.servicecomb.governance.properties.FaultInjectionProperties; +import org.apache.servicecomb.governance.properties.MapperProperties; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.micrometer.prometheusmetrics.PrometheusConfig; +import io.micrometer.prometheusmetrics.PrometheusMeterRegistry; + + +@Configuration +public class MockConfiguration { + @Bean + public MockMicroserviceMeta mockMicroserviceMeta() { + return new MockMicroserviceMeta(); + } + + @Bean + public MockRetryExtension mockRetryExtension() { + return new MockRetryExtension(); + } + + @Bean + public MockCircuitBreakerExtension circuitBreakerExtension() { + return new MockCircuitBreakerExtension(); + } + + @Bean + public MockInstanceIsolationExtension instanceIsolationExtension() { + return new MockInstanceIsolationExtension(); + } + + @Bean + public PrometheusMeterRegistry meterRegistry() { + return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + } + + @Bean + public MapperProperties mapperProperties2() { + return new MapperProperties(MapperProperties.MATCH_MAPPER_KEY + "2"); + } + + @Bean + public MapperHandler mapperHandler2(@Qualifier("mapperProperties2") MapperProperties mapperProperties) { + return new MapperHandler(mapperProperties); + } + + @Bean + public BulkheadProperties bulkheadProperties2() { + return new BulkheadProperties(BulkheadProperties.MATCH_BULKHEAD_KEY + "2"); + } + + @Bean + public BulkheadHandler bulkheadHandler2(@Qualifier("bulkheadProperties2") BulkheadProperties bulkheadProperties) { + return new BulkheadHandler(bulkheadProperties); + } + + @Bean + public CircuitBreakerProperties circuitBreakerProperties2() { + return new CircuitBreakerProperties(CircuitBreakerProperties.MATCH_CIRCUITBREAKER_KEY + "2"); + } + + @Bean + public CircuitBreakerHandler circuitBreakerHandler2( + @Qualifier("circuitBreakerProperties2") CircuitBreakerProperties circuitBreakerProperties, + AbstractCircuitBreakerExtension circuitBreakerExtension) { + return new CircuitBreakerHandler(circuitBreakerProperties, circuitBreakerExtension); + } + + @Bean + public FaultInjectionProperties faultInjectionProperties2() { + return new FaultInjectionProperties(FaultInjectionProperties.MATCH_FAULT_INJECTION_KEY + "2"); + } + + @Bean + public FaultInjectionHandler faultInjectionHandler2( + @Qualifier("faultInjectionProperties2") FaultInjectionProperties faultInjectionProperties) { + return new FaultInjectionHandler(faultInjectionProperties); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/MockInstanceIsolationExtension.java b/governance/src/test/java/org/apache/servicecomb/governance/MockInstanceIsolationExtension.java new file mode 100644 index 00000000000..86f0ce99b37 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/MockInstanceIsolationExtension.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.governance; + +import org.apache.servicecomb.governance.handler.ext.AbstractInstanceIsolationExtension; + +public class MockInstanceIsolationExtension extends AbstractInstanceIsolationExtension { + @Override + protected String extractStatusCode(Object result) { + return "200"; + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/MockMicroserviceMeta.java b/governance/src/test/java/org/apache/servicecomb/governance/MockMicroserviceMeta.java new file mode 100644 index 00000000000..ca68e2b3a39 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/MockMicroserviceMeta.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.governance; + +public class MockMicroserviceMeta implements MicroserviceMeta { + @Override + public String getName() { + return "myself"; + } + + @Override + public String getVersion() { + return "1.0"; + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java b/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java new file mode 100644 index 00000000000..2badc9c1548 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.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.governance; + +import java.util.List; + +import org.apache.servicecomb.governance.handler.ext.AbstractRetryExtension; + + +public class MockRetryExtension extends AbstractRetryExtension { + @Override + public boolean isFailedResult(List statusList, Object result) { + return false; + } + + @Override + protected String extractStatusCode(Object result) { + return null; + } + + @Override + public boolean isFailedResult(List statusList, Throwable e) { + return false; + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/OperatorTest.java b/governance/src/test/java/org/apache/servicecomb/governance/OperatorTest.java new file mode 100644 index 00000000000..7c0670acb93 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/OperatorTest.java @@ -0,0 +1,297 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.time.Duration; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.marker.Matcher; +import org.apache.servicecomb.governance.marker.RequestProcessor; +import org.apache.servicecomb.governance.marker.operator.RawOperator; +import org.apache.servicecomb.governance.policy.RetryPolicy; +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.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +@SpringBootTest +@ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) +public class OperatorTest { + private RequestProcessor requestProcessor; + + @Autowired + public void setRequestProcessor(RequestProcessor requestProcessor) { + this.requestProcessor = requestProcessor; + } + + @Test + public void test_unknown_operator() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/test"); + Matcher matcher = new Matcher(); + RawOperator apiPath = new RawOperator(); + apiPath.put("unknown", "/test"); + matcher.setApiPath(apiPath); + Assertions.assertFalse(requestProcessor.match(request, matcher)); + } + + @Test + public void test_exact_api_path_match() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/bulkhead"); + Matcher matcher = new Matcher(); + RawOperator apiPath = new RawOperator(); + apiPath.put("exact", "/bulkhead"); + matcher.setApiPath(apiPath); + Assertions.assertTrue(requestProcessor.match(request, matcher)); + } + + @Test + public void test_prefix_api_path_match() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/bulkhead/hello"); + Matcher matcher = new Matcher(); + RawOperator apiPath = new RawOperator(); + apiPath.put("prefix", "/bulkhead"); + matcher.setApiPath(apiPath); + Assertions.assertTrue(requestProcessor.match(request, matcher)); + } + + @Test + public void test_prefix_api_path_not_match_null() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/bulkhead/hello"); + Matcher matcher = new Matcher(); + RawOperator apiPath = new RawOperator(); + apiPath.put("prefix", null); + matcher.setApiPath(apiPath); + Assertions.assertFalse(requestProcessor.match(request, matcher)); + } + + @Test + public void test_suffix_api_path_match() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/api/bulkhead"); + Matcher matcher = new Matcher(); + RawOperator apiPath = new RawOperator(); + apiPath.put("suffix", "/bulkhead"); + matcher.setApiPath(apiPath); + Assertions.assertTrue(requestProcessor.match(request, matcher)); + } + + @Test + public void test_suffix_api_path_not_match_null() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/api/bulkhead"); + Matcher matcher = new Matcher(); + RawOperator apiPath = new RawOperator(); + apiPath.put("suffix", null); + matcher.setApiPath(apiPath); + Assertions.assertFalse(requestProcessor.match(request, matcher)); + } + + @Test + public void test_exact_api_path_not_match() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/bulkhead/"); + Matcher matcher = new Matcher(); + RawOperator apiPath = new RawOperator(); + apiPath.put("exact", "/bulkhead"); + matcher.setApiPath(apiPath); + Assertions.assertFalse(requestProcessor.match(request, matcher)); + + request.setApiPath("/bulkhead"); + request.setApiPath(null); + Assertions.assertFalse(requestProcessor.match(request, matcher)); + + request.setApiPath("/bulkhead"); + apiPath.clear(); + matcher.setApiPath(apiPath); + Assertions.assertFalse(requestProcessor.match(request, matcher)); + } + + @Test + public void test_exact_api_path_match_header_match() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/bulkhead"); + request.setMethod("GET"); + Map reqHeaders = new HashMap<>(); + reqHeaders.put("header1", "value1"); + request.setHeaders(reqHeaders); + Matcher matcher = new Matcher(); + RawOperator apiPath = new RawOperator(); + apiPath.put("exact", "/bulkhead"); + matcher.setApiPath(apiPath); + matcher.setMethod(Arrays.asList("GET")); + Map headers = new HashMap<>(); + RawOperator header1 = new RawOperator(); + header1.put("exact", "value1"); + headers.put("header1", header1); + matcher.setHeaders(headers); + Assertions.assertTrue(requestProcessor.match(request, matcher)); + } + + @Test + public void test_exact_api_path_match_header_not_match() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/bulkhead"); + request.setMethod("GET"); + Map reqHeaders = new HashMap<>(); + reqHeaders.put("header1", "value2"); + request.setHeaders(reqHeaders); + Matcher matcher = new Matcher(); + RawOperator apiPath = new RawOperator(); + apiPath.put("exact", "/bulkhead"); + matcher.setApiPath(apiPath); + matcher.setMethod(Arrays.asList("GET")); + Map headers = new HashMap<>(); + RawOperator header1 = new RawOperator(); + header1.put("exact", "value1"); + headers.put("header1", header1); + matcher.setHeaders(headers); + Assertions.assertFalse(requestProcessor.match(request, matcher)); + + reqHeaders.clear(); + request.setHeaders(reqHeaders); + Assertions.assertFalse(requestProcessor.match(request, matcher)); + } + + @Test + public void test_header_low_case() { + GovernanceRequest request = new GovernanceRequest(); + Map reqHeaders = new HashMap<>(); + reqHeaders.put("hEadeR", "100"); + request.setHeaders(reqHeaders); + Matcher matcher = new Matcher(); + Map headers = new HashMap<>(); + RawOperator header1 = new RawOperator(); + header1.put("compare", ">10"); + headers.put("HeAder", header1); + matcher.setHeaders(headers); + Assertions.assertTrue(requestProcessor.match(request, matcher)); + } + + @Test + public void test_compare_header_match() { + GovernanceRequest request = new GovernanceRequest(); + Map reqHeaders = new HashMap<>(); + reqHeaders.put("header1", "100"); + request.setHeaders(reqHeaders); + Matcher matcher = new Matcher(); + Map headers = new HashMap<>(); + + RawOperator header1 = new RawOperator(); + header1.put("compare", ">10"); + headers.put("header1", header1); + matcher.setHeaders(headers); + Assertions.assertTrue(requestProcessor.match(request, matcher)); + + header1 = new RawOperator(); + header1.put("compare", ">=10"); + headers.put("header1", header1); + matcher.setHeaders(headers); + Assertions.assertTrue(requestProcessor.match(request, matcher)); + + header1 = new RawOperator(); + header1.put("compare", "<1000"); + headers.put("header1", header1); + matcher.setHeaders(headers); + Assertions.assertTrue(requestProcessor.match(request, matcher)); + + header1 = new RawOperator(); + header1.put("compare", "<=1000"); + headers.put("header1", header1); + matcher.setHeaders(headers); + Assertions.assertTrue(requestProcessor.match(request, matcher)); + + header1 = new RawOperator(); + header1.put("compare", "=100"); + headers.put("header1", header1); + matcher.setHeaders(headers); + Assertions.assertTrue(requestProcessor.match(request, matcher)); + } + + @Test + public void test_compare_header_not_match() { + GovernanceRequest request = new GovernanceRequest(); + Map reqHeaders = new HashMap<>(); + reqHeaders.put("header1", "100"); + request.setHeaders(reqHeaders); + Matcher matcher = new Matcher(); + Map headers = new HashMap<>(); + + RawOperator header1 = new RawOperator(); + header1.put("compare", ">1000"); + headers.put("header1", header1); + matcher.setHeaders(headers); + Assertions.assertFalse(requestProcessor.match(request, matcher)); + + header1 = new RawOperator(); + header1.put("compare", ">=1000"); + headers.put("header1", header1); + matcher.setHeaders(headers); + Assertions.assertFalse(requestProcessor.match(request, matcher)); + + header1 = new RawOperator(); + header1.put("compare", "<10"); + headers.put("header1", header1); + matcher.setHeaders(headers); + Assertions.assertFalse(requestProcessor.match(request, matcher)); + + header1 = new RawOperator(); + header1.put("compare", "<=10"); + headers.put("header1", header1); + matcher.setHeaders(headers); + Assertions.assertFalse(requestProcessor.match(request, matcher)); + + header1 = new RawOperator(); + header1.put("compare", "=200"); + headers.put("header1", header1); + matcher.setHeaders(headers); + Assertions.assertFalse(requestProcessor.match(request, matcher)); + } + + @Test + public void test_time_changed_to_duration() { + RetryPolicy retryPolicy = new RetryPolicy(); + String result; + result = retryPolicy.stringOfDuration("100", Duration.ofMillis(10)); + Assertions.assertEquals("PT0.1S", result); + Assertions.assertEquals(100, Duration.parse(result).toMillis()); + + result = retryPolicy.stringOfDuration("3S", Duration.ofMillis(10)); + Assertions.assertEquals("PT3S", result); + Assertions.assertEquals(3000, Duration.parse(result).toMillis()); + + result = retryPolicy.stringOfDuration("1M", Duration.ofMillis(10)); + Assertions.assertEquals("PT1M", result); + Assertions.assertEquals(60000, Duration.parse(result).toMillis()); + + result = retryPolicy.stringOfDuration("1H", Duration.ofMillis(10)); + Assertions.assertEquals("PT1H", result); + Assertions.assertEquals(3600000, Duration.parse(result).toMillis()); + + result = retryPolicy.stringOfDuration("3", Duration.ofMillis(10)); + Assertions.assertEquals("PT0.003S", result); + Assertions.assertEquals(3, Duration.parse(result).toMillis()); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java b/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java new file mode 100644 index 00000000000..073cfc44534 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import org.apache.servicecomb.governance.handler.Disposable; +import org.apache.servicecomb.governance.handler.RetryHandler; +import org.apache.servicecomb.governance.handler.ext.AbstractRetryExtension; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.policy.RetryPolicy; +import org.apache.servicecomb.governance.properties.RetryProperties; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import io.github.resilience4j.retry.MaxRetriesExceededException; +import io.github.resilience4j.retry.Retry; + +public class RetryHandlerTest { + @Test + 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(), Mockito.any())).thenReturn(true); + + RetryPolicy retryPolicy = new RetryPolicy(); + retryPolicy.setName("test"); + retryPolicy.setFailAfterMaxAttempts(false); + RetryHandler retryHandler = new RetryHandler(retryProperties, retryExtension); + + Disposable retry = retryHandler.createProcessor(retryPolicy.getName(), governanceRequest, retryPolicy); + Assertions.assertThrows(IllegalStateException.class, () -> retry.get().executeCheckedSupplier(() -> { + throw new IllegalStateException(); + })); + } + + @Test + 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(), Mockito.any())).thenReturn(true); + + RetryPolicy retryPolicy = new RetryPolicy(); + retryPolicy.setName("test"); + retryPolicy.setFailAfterMaxAttempts(true); + RetryHandler retryHandler = new RetryHandler(retryProperties, retryExtension); + + Disposable retry = retryHandler.createProcessor(retryPolicy.getName(), governanceRequest, retryPolicy); + Assertions.assertThrows(IllegalStateException.class, () -> retry.get().executeCheckedSupplier(() -> { + throw new IllegalStateException(); + })); + } + + @Test + 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(), (Object) Mockito.any())).thenReturn(true); + + RetryPolicy retryPolicy = new RetryPolicy(); + retryPolicy.setName("test"); + retryPolicy.setFailAfterMaxAttempts(true); + RetryHandler retryHandler = new RetryHandler(retryProperties, retryExtension); + + Disposable retry = retryHandler.createProcessor(retryPolicy.getName(), governanceRequest, retryPolicy); + Assertions.assertThrows(MaxRetriesExceededException.class, + () -> retry.get().executeCheckedSupplier(() -> -1)); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/TimeLimiterHandlerTest.java b/governance/src/test/java/org/apache/servicecomb/governance/TimeLimiterHandlerTest.java new file mode 100644 index 00000000000..742c7118417 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/TimeLimiterHandlerTest.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.governance; + +import java.time.Duration; + +import org.apache.servicecomb.governance.handler.TimeLimiterHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.policy.TimeLimiterPolicy; +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.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +import io.github.resilience4j.timelimiter.TimeLimiter; + +@SpringBootTest +@ContextConfiguration(classes = {GovernanceCommonConfiguration.class, MockConfiguration.class}) +public class TimeLimiterHandlerTest { + private TimeLimiterHandler timeLimiterHandler; + + @Autowired + public void setInstanceIsolationHandler(TimeLimiterHandler timeLimiterHandler) { + this.timeLimiterHandler = timeLimiterHandler; + } + + @Test + public void testMatchPriorityPolicy() { + GovernanceRequest request = new GovernanceRequest(); + request.setApiPath("/timeLimiter"); + TimeLimiterPolicy policy = timeLimiterHandler.matchPolicy(request); + Assertions.assertEquals("demo-timeLimiter", policy.getName()); + TimeLimiter timeLimiter = timeLimiterHandler.getActuator(request); + Duration timeoutDuration = timeLimiter.getTimeLimiterConfig().getTimeoutDuration(); + Assertions.assertEquals(2000, timeoutDuration.toMillis()); + } +} 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 new file mode 100644 index 00000000000..41157a9b079 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/handler/ext/RetryExtensionTest.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.handler.ext; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.NoRouteToHostException; +import java.net.SocketTimeoutException; +import java.util.Arrays; +import java.util.List; + + +import org.junit.jupiter.api.Assertions; +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 io.vertx.core.VertxException; + +@SpringBootTest +@ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) +public class RetryExtensionTest { + + @Test + public void test_status_code_to_contains() { + List statusList = Arrays.asList("502", "503"); + boolean result = AbstractRetryExtension.statusCodeContains(statusList, "502"); + Assertions.assertTrue(result); + + result = AbstractRetryExtension.statusCodeContains(statusList, "504"); + Assertions.assertFalse(result); + + statusList = Arrays.asList("5xx", "4x4", "4x", "x32", "xx6"); + result = AbstractRetryExtension.statusCodeContains(statusList, "502"); + Assertions.assertTrue(result); + + result = AbstractRetryExtension.statusCodeContains(statusList, "504"); + Assertions.assertTrue(result); + + statusList = Arrays.asList("4x4", "x32", "xx6"); + result = AbstractRetryExtension.statusCodeContains(statusList, "402"); + Assertions.assertFalse(result); + + result = AbstractRetryExtension.statusCodeContains(statusList, "404"); + Assertions.assertTrue(result); + + result = AbstractRetryExtension.statusCodeContains(statusList, "332"); + Assertions.assertTrue(result); + + result = AbstractRetryExtension.statusCodeContains(statusList, "446"); + Assertions.assertTrue(result); + + statusList = Arrays.asList("4x", "x3x", "x5"); + result = AbstractRetryExtension.statusCodeContains(statusList, "446"); + Assertions.assertFalse(result); + + result = AbstractRetryExtension.statusCodeContains(statusList, "455"); + Assertions.assertFalse(result); + + result = AbstractRetryExtension.statusCodeContains(statusList, "434"); + Assertions.assertTrue(result); + } + + @Test + public void testRetryWithConnectionException() { + Exception target = new ConnectException("connection refused"); + Exception root = new Exception(target); + boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); + Assertions.assertTrue(canRetry); + } + + @Test + public void testRetryWithSocketTimeout() { + Exception target = new SocketTimeoutException("Read timed out"); + Exception root = new Exception(target); + boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); + Assertions.assertTrue(canRetry); + } + + @Test + public void testRetryWithIOException() { + Exception target = new IOException("Connection reset by peer"); + Exception root = new Exception(target); + boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); + Assertions.assertTrue(canRetry); + + target = new IOException("Target not exist"); + root = new Exception(target); + canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); + Assertions.assertFalse(canRetry); + } + + @Test + public void testRetryVertxException() { + Exception target = new VertxException("Connection was closed"); + Exception root = new Exception(target); + boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); + Assertions.assertTrue(canRetry); + + target = new IOException(""); + root = new Exception(target); + canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); + Assertions.assertFalse(canRetry); + } + + @Test + public void testRetryNoRouteToHostException() { + Exception target = new NoRouteToHostException("Host is unreachable"); + Exception root = new Exception(target); + boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); + Assertions.assertTrue(canRetry); + + target = new NoRouteToHostException("No route to host"); + root = new Exception(target); + canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); + Assertions.assertTrue(canRetry); + } + + @Test + public void testRetryEqualTen() { + Exception target = new ConnectException("connection refused"); + for (int i = 0; i < 8; i++) { + target = new Exception("Level" + i, target); + } + Exception root = new Exception(target); + boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); + Assertions.assertTrue(canRetry); + } + + @Test + public void testRetryOverTen() { + Exception target = new ConnectException("connection refused"); + for (int i = 0; i < 9; i++) { + target = new Exception("Level" + i, target); + } + Exception root = new Exception(target); + boolean canRetry = FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, root); + Assertions.assertFalse(canRetry); + } +} 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/ClassWithAnnotation.java b/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassWithAnnotation.java new file mode 100644 index 00000000000..263d442f40b --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassWithAnnotation.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.governance.mockclasses; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.mockclasses.service.MockProfileClassUserService; +import org.apache.servicecomb.governance.utils.CustomMatch; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ClassWithAnnotation implements CustomMatch { + @Autowired + MockProfileClassUserService service; + + @Override + public boolean matchRequest(GovernanceRequestExtractor requestExtractor, String parameters) { + String profileValue = service.getUser(); + return Stream.of(parameters.split(",")).collect(Collectors.toSet()).contains(profileValue); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/CustomMatchDemo.java b/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/CustomMatchDemo.java new file mode 100644 index 00000000000..dba57a48dd5 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/CustomMatchDemo.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.governance.mockclasses; + +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.utils.CustomMatch; + +public class CustomMatchDemo implements CustomMatch { + + private CustomMatchDemo() { + + } + + @Override + public boolean matchRequest(GovernanceRequestExtractor requestExtractor, String parameters) { + return true; + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/service/MockConfigurationForCustomMatcher.java b/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/service/MockConfigurationForCustomMatcher.java new file mode 100644 index 00000000000..7668ddc8e28 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/service/MockConfigurationForCustomMatcher.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.governance.mockclasses.service; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan("org.apache.servicecomb.governance.mockclasses") +public class MockConfigurationForCustomMatcher { +} diff --git a/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/service/MockProfileClassUserService.java b/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/service/MockProfileClassUserService.java new file mode 100644 index 00000000000..31aa5479cef --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/service/MockProfileClassUserService.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.governance.mockclasses.service; + +import org.springframework.stereotype.Service; + +@Service +public class MockProfileClassUserService { + public String getUser() { + return "bill"; + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/router/ExampleDistributor.java b/governance/src/test/java/org/apache/servicecomb/router/ExampleDistributor.java new file mode 100644 index 00000000000..42fdfad0367 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/router/ExampleDistributor.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.router; + +import org.apache.servicecomb.router.distribute.AbstractRouterDistributor; +import org.springframework.stereotype.Component; + +@Component +public class ExampleDistributor extends AbstractRouterDistributor { + public ExampleDistributor() { + 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 new file mode 100644 index 00000000000..cc8cc9ce96c --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java @@ -0,0 +1,279 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 RouterDistributorDynamicConfig2Test { + 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: 2\n" + + " route:\n" + + " - weight: 100\n" + + " tags:\n" + + " version: 2.0\n" + + " - precedence: 1\n" + + " match:\n" + + " headers:\n" + + " appId:\n" + + " regex: 01\n" + + " caseInsensitive: false\n" + + " userId:\n" + + " exact: 01\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; + } + + 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(CONFIG_KEY, RULE_STRING); + + postConfigurationChangedEvent(); + } + + private void postConfigurationChangedEvent() { + Set changedKeys = new HashSet<>(); + changedKeys.add(CONFIG_KEY); + GovernanceConfigurationChangedEvent newEvent = new GovernanceConfigurationChangedEvent(changedKeys); + GovernanceEventManager.post(newEvent); + } + + @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); + ins1.setVersion("2.0"); + ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); + ins2.setVersion("1.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 testCaseSensitiveNotMatch() { + String rule = "" + + " - precedence: 1\n" + + " route:\n" + + " - weight: 100\n" + + " tags:\n" + + " version: 2.0\n" + + " - precedence: 2\n" + + " match:\n" + + " headers:\n" + + " userId:\n" + + " exact: user01\n" + + " caseInsensitive: false\n" + + " route:\n" + + " - weight: 100\n" + + " tags:\n" + + " version: 1.0\n"; + dynamicValues.put(CONFIG_KEY, rule); + postConfigurationChangedEvent(); + + 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); + ins1.setVersion("2.0"); + ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); + ins2.setVersion("1.0"); + serverList.add(ins1); + serverList.add(ins2); + + 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()); + } + + @Test + public void testNoneMatch() { + String rule = "" + + " - precedence: 1\n" + + " match:\n" + + " headers:\n" + + " userId:\n" + + " regex: user02\n" + + " route:\n" + + " - weight: 100\n" + + " tags:\n" + + " version: 2.0\n" + + " - precedence: 2\n" + + " match:\n" + + " headers:\n" + + " userId:\n" + + " exact: user01\n" + + " caseInsensitive: false\n" + + " route:\n" + + " - weight: 100\n" + + " tags:\n" + + " version: 1.0\n"; + dynamicValues.put(CONFIG_KEY, rule); + postConfigurationChangedEvent(); + + 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); + ins1.setVersion("2.0"); + ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); + ins2.setVersion("1.0"); + serverList.add(ins1); + serverList.add(ins2); + + List resultServerList = mainFilter(serverList, governanceRequest); + Assertions.assertEquals(2, resultServerList.size()); + } + + @Test + public void testOneMatchButNoInstance() { + String rule = "" + + " - precedence: 1\n" + + " match:\n" + + " headers:\n" + + " appId:\n" + + " regex: app02\n" + + " route:\n" + + " - weight: 100\n" + + " tags:\n" + + " version: 2.0\n" + + " - precedence: 2\n" + + " match:\n" + + " headers:\n" + + " userId:\n" + + " exact: user01\n" + + " caseInsensitive: false\n" + + " route:\n" + + " - weight: 100\n" + + " tags:\n" + + " version: 1.0\n"; + dynamicValues.put(CONFIG_KEY, rule); + postConfigurationChangedEvent(); + + 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); + ins1.setVersion("1.0"); + ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); + ins2.setVersion("1.0"); + serverList.add(ins1); + serverList.add(ins2); + + List resultServerList = mainFilter(serverList, governanceRequest); + Assertions.assertEquals(2, resultServerList.size()); + } + + private List mainFilter(List serverList, GovernanceRequestExtractor requestExtractor) { + return routerFilter + .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 new file mode 100644 index 00000000000..4418a30829b --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 RouterDistributorDynamicConfigTest { + private static final String TARGET_SERVICE_NAME = "test_server"; + + private static final String RULE_STRING = "" + + " - precedence: 2 #优先级\n" + + " match: #匹配策略\n" + + " headers: #header匹配\n" + + " appId:\n" + + " exact: \"01\"\n" + + " userId:\n" + + " exact: \"01\"\n" + + " route: #路由规则\n" + + " - weight: 50\n" + + " tags:\n" + + " version: 1.1\n" + + " - 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: 3\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 RouterDistributorDynamicConfigTest() { + } + + 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.ROUTE_RULE_PREFIX + TARGET_SERVICE_NAME, RULE_STRING); + + Set changedKeys = new HashSet<>(); + changedKeys.add(RouterRuleCache.ROUTE_RULE_PREFIX + TARGET_SERVICE_NAME); + GovernanceConfigurationChangedEvent newEvent = new GovernanceConfigurationChangedEvent(changedKeys); + GovernanceEventManager.post(newEvent); + } + + + @Test + public void testHeaderIsEmpty() { + List list = getMockList(); + List serverList = mainFilter(list, new GovernanceRequest()); + Assertions.assertEquals(2, serverList.size()); + } + + @Test + 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, governanceRequest); + Assertions.assertEquals(1, serverList.size()); + Assertions.assertEquals("01", serverList.get(0).getId()); + } + + @Test + public void testVersionMatch() { + Map headers = new HashMap<>(); + headers.put("userId", "01"); + headers.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); + List serverList = mainFilter(getMockList(), governanceRequest); + Assertions.assertEquals(1, serverList.size()); + Assertions.assertEquals("02", serverList.get(0).getId()); + } + + @Test + public void testMatchPrecedenceLower() { + Map headers = new HashMap<>(); + headers.put("userId", "02"); + headers.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); + List serverList = mainFilter(getMockList(), governanceRequest); + Assertions.assertEquals(1, serverList.size()); + Assertions.assertEquals("01", serverList.get(0).getId()); + } + + @Test + 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); + ins1.setVersion("2.0"); + ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); + ins2.addTags("app", "a"); + ins2.setVersion("1.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 getMockList() { + List serverList = new ArrayList<>(); + ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); + ins1.setVersion("2.0"); + ServiceIns ins2 = new ServiceIns("02", TARGET_SERVICE_NAME); + ins2.addTags("app", "a"); + serverList.add(ins1); + serverList.add(ins2); + return serverList; + } + + private List mainFilter(List serverList, + GovernanceRequestExtractor governanceRequestExtractor) { + return routerFilter + .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 new file mode 100644 index 00000000000..54ebcb05823 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +@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; + + @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 serverNum2 = 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())) { + serverNum2++; + } + } + } + boolean flag = false; + if (Math.round(serverNum2 * 1.0 / serverNum1) == 4) { + flag = true; + } + Assertions.assertTrue(flag); + } + + List initServiceList() { + ServiceIns serviceIns1 = new ServiceIns("01", "test_server1"); + ServiceIns serviceIns2 = new ServiceIns("02", "test_server1"); + 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/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/java/org/apache/servicecomb/router/ServiceIns.java b/governance/src/test/java/org/apache/servicecomb/router/ServiceIns.java new file mode 100644 index 00000000000..7600d09d7ca --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/router/ServiceIns.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.router; + +import java.util.HashMap; +import java.util.Map; + +public class ServiceIns { + String version = "1.1"; + + String serverName; + + Map tags = new HashMap<>(); + + private final String id; + + public ServiceIns(String id, String serverName) { + this.id = id; + this.serverName = serverName; + } + + public String getId() { + return id; + } + + public String getVersion() { + return version; + } + + public String getServerName() { + return serverName; + } + + public Map getTags() { + return tags; + } + + public void setVersion(String version) { + this.version = version; + } + + public void addTags(String key, String v) { + tags.put(key, v); + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/router/VersionCompareUtilTest.java b/governance/src/test/java/org/apache/servicecomb/router/VersionCompareUtilTest.java new file mode 100644 index 00000000000..d4438a956a1 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/router/VersionCompareUtilTest.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.router; + +import org.apache.servicecomb.router.util.VersionCompareUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class VersionCompareUtilTest { + + @Test + public void testVersion() { + Assertions.assertTrue(VersionCompareUtil.compareVersion("0.0.1", "0.0.0") > 0); + Assertions.assertEquals(0, VersionCompareUtil.compareVersion("0.0.0", "0.0.0")); + Assertions.assertTrue(VersionCompareUtil.compareVersion("0.0.0", "0.0.1") < 0); + Assertions.assertEquals(0, VersionCompareUtil.compareVersion("0.0.0", "0.0.0.0")); + Assertions.assertTrue(VersionCompareUtil.compareVersion("0.0.1", "0.0.0.0") > 0); + Assertions.assertTrue(VersionCompareUtil.compareVersion("0.0.1", "0.0.0.0") > 0); + } +} diff --git a/governance/src/test/resources/META-INF/spring/bean.xml b/governance/src/test/resources/META-INF/spring/bean.xml new file mode 100644 index 00000000000..5ff6c6e05cc --- /dev/null +++ b/governance/src/test/resources/META-INF/spring/bean.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/governance/src/test/resources/application.yaml b/governance/src/test/resources/application.yaml new file mode 100644 index 00000000000..ce981ac1ea2 --- /dev/null +++ b/governance/src/test/resources/application.yaml @@ -0,0 +1,298 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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: + matchGroup: + demo-timeLimiter: | + matches: + - apiPath: + prefix: "/timeLimiter" + demo-governanceCache: | + matches: + - apiPath: + prefix: "/governanceCache" + demo-loadbalance-random: | + matches: + - serviceName: "loadrandom" + demo-loadbalance-roundRobin: | + matches: + - serviceName: "loadroundRobin" + demo-fallback-returnNull: | + matches: + - serviceName: "returnNull" + demo-fallback-ThrowException: | + matches: + - serviceName: "ThrowException" + demo-faultInject-forceClosed: | + matches: + - serviceName: "forceClosed" + demo-allOperation: | + matches: + - apiPath: + prefix: "/" + demo-rateLimiting: | + matches: + - apiPath: + exact: "/hello" + demo-rateLimiting-servicename: | + matches: + - apiPath: + exact: "/helloServiceName" + serviceName: "srcService" + wrong-name-inogred: | + wrong: some + demo-retry: | + matches: + - apiPath: + exact: "/retry" + demo-circuitBreaker: | + matches: + - apiPath: + exact: "/circuitBreaker" + demo-bulkhead: | + matches: + - name: matchPath + apiPath: + exact: "/bulkhead" + - name: matchHeader + headers: + x-token: + exact: token + demo-bulkhead-priority: | + matches: + - apiPath: + exact: "/bulkhead" + demo-bulkhead-other: | + matches: + - apiPath: + exact: "/bulkhead" + services: other:1.0 + demo-faultInjectDelay: | + matches: + - apiPath: + contains: "/faultInjectDelay" + demo-faultInjectAbort: | + matches: + - apiPath: + contains: "/faultInjectAbort" + demo-mapper: | + matches: + - apiPath: + prefix: "/mapper" + demo-mapper-query: | + matches: + - queries: + name: + exact: "bob" + rateLimiting: + demo-rateLimiting: | + rate: 1 + demo-rateLimiting-servicename: | + rate: 1 + wrongIgnored: | + rate: 0 + identifierRateLimiting: + demo-rateLimiting: | + rate: 1 + identifier: test + demo-rateLimiting-servicename: | + rate: 1 + identifier: test + timeLimiter: + demo-timeLimiter: | + timeoutDuration: 2000 + cancelRunningFuture: true + demo-timeLimiter-other: | + timeoutDuration: 2000 + cancelRunningFuture: false + cache: + demo-governanceCache: | + ttl: 36000000 + maximumSize: 50000 + concurrencyLevel: 10 + demo-governanceCache-other: | + ttl: 666666 + maximumSize: 50000 + concurrencyLevel: 15 + retry: + demo-retry: | + maxAttempts: 3 + wrongIgnored: | + maxAttempts: -1 + circuitBreaker: + demo-circuitBreaker: | + minimumNumberOfCalls: 2 + slidingWindowSize: 2 + slidingWindowType: COUNT_BASED + services: myself:1.0,other + wrongIgnored: | + rules: + match: demo-circuitBreaker.xx + minimumNumberOfCalls: -1 + bulkhead: + demo-bulkhead: | + order: 1 + maxConcurrentCalls: 1 + maxWaitDuration: 3000 + services: myself:1.0 + demo-bulkhead-priority: | + order: 0 + maxConcurrentCalls: 1 + maxWaitDuration: 3000 + services: myself:1.0 + demo-bulkhead-other: | + maxConcurrentCalls: 1 + maxWaitDuration: 3000 + services: other:1.0 + wrongIgnored: | + rules: + match: demo-bulkhead.xx + maxWaitDuration: -1 + instanceIsolation: + demo-allOperation: | + minimumNumberOfCalls: 2 + slidingWindowSize: 2 + slidingWindowType: COUNT_BASED + waitDurationInOpenState: 1000 + instanceBulkhead: + demo-allOperation: | + maxConcurrentCalls: 2 + maxWaitDuration: 10 + faultInjection: + demo-fallback-ThrowException: | + type: abort + percentage: 100 + demo-fallback-returnNull: | + fallbackType: ReturnNull + type: abort + percentage: 100 + demo-faultInject-forceClosed: | + forceClosed: true + fallbackType: ReturnNull + demo-faultInjectDelay: | + delayTime: 2S + type: delay + percentage: 100 + demo-faultInjectAbort: | + type: abort + percentage: 50 + errorCode: 500 + wrongIgnored: | + delayTime: -1 + type: ERROR + faultInjection2: + demo-fallback-ThrowException: | + type: abort + percentage: 100 + loadbalance: + demo-loadbalance-random: | + rule: Random + demo-loadbalance-roundRobin: | + rule: RoundRobin + mapper: + demo-mapper: | + target: + host: 127.0.0.1 + port: 8080 + demo-mapper-query: | + target: + user-id: $Q{name} + mapper2: + demo-mapper: | + target: + host: 127.0.0.1 + port: 9090 + routeRule: + test_server1: | # 服务名 + - precedence: 1 # 优先级,数字越大,优先级越高。 + match: # 请求匹配规则。0..N个,不配置表示匹配。 + headers: # header 匹配 + region: # 如果配置了多个 header,那么所有的 header 规则都必须和请求匹配 + exact: 'providerRegion' + type: + regex: gray_[a-z]+ # java 正则表达式匹配 + route: # 路由规则 + - weight: 20 # 权重值 + tags: + version: 1.0.0 # 实例标记。满足标记条件的实例放到这一组。 + - weight: 80 # 权重值 + tags: + version: 2.0.0 # 实例标记。满足标记条件的实例放到这一组。 + + - precedence: 2 + route: + - weight: 20 + tags: + x-group: red + - 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-bizkeeper/pom.xml b/handlers/handler-bizkeeper/pom.xml deleted file mode 100644 index 1951316b473..00000000000 --- a/handlers/handler-bizkeeper/pom.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - 4.0.0 - - io.servicecomb - handlers - 0.3.0-SNAPSHOT - - - handler-bizkeeper - - - UTF-8 - - - - - io.servicecomb - java-chassis-core - - - - com.netflix.hystrix - hystrix-core - - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - - - - diff --git a/handlers/handler-bizkeeper/readme.MD b/handlers/handler-bizkeeper/readme.MD deleted file mode 100644 index 698fc80167e..00000000000 --- a/handlers/handler-bizkeeper/readme.MD +++ /dev/null @@ -1,3 +0,0 @@ -参考: -hystrix: https://github.com/Netflix/Hystrix/wiki -design: http://code.huawei.com/CSE/CSE/wikis/design \ No newline at end of file diff --git a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperCommand.java b/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperCommand.java deleted file mode 100644 index b575fba0f2f..00000000000 --- a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperCommand.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.hystrix.HystrixObservableCommand; -import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; - -import io.servicecomb.core.Invocation; -import io.servicecomb.swagger.invocation.Response; -import rx.Observable; - -/** - * 接管调用链的处理流程,处理完成后,将结果交给调用链。额外还提供了请求缓存的功能和容错/降级处理能力。 - * - */ -public abstract class BizkeeperCommand extends HystrixObservableCommand { - private static final Logger LOG = LoggerFactory.getLogger(BizkeeperCommand.class); - - private final Invocation invocation; - - private final String type; - - protected BizkeeperCommand(String type, Invocation invocation, HystrixObservableCommand.Setter setter) { - super(setter); - this.type = type; - this.invocation = invocation; - } - - @Override - protected String getCacheKey() { - if (HystrixRequestContext.isCurrentThreadInitialized()) { - StringBuilder sb = new StringBuilder(); - sb.append(this.getCommandGroup().name()); - sb.append("-"); - sb.append(this.getCommandKey().name()); - return sb.toString(); - } else { - return super.getCacheKey(); - } - } - - @Override - protected Observable resumeWithFallback() { - Observable observable = Observable.create(f -> { - LOG.info("fallback called."); - try { - if (Configuration.FALLBACKPOLICY_POLICY_RETURN - .equals(Configuration.INSTANCE.getFallbackPolicyPolicy(type, - invocation.getMicroserviceName(), - invocation.getOperationMeta().getMicroserviceQualifiedName()))) { - f.onNext(Response.succResp(null)); - } else { - f.onNext(Response.failResp(invocation.getInvocationType(), - BizkeeperExceptionUtils - .createBizkeeperException(BizkeeperExceptionUtils.CSE_HANDLER_BK_FALLBACK, - null, - invocation.getOperationMeta().getMicroserviceQualifiedName()))); - } - f.onCompleted(); - } catch (Exception e) { - LOG.warn("fallbacke failed due to:" + e.getMessage()); - throw e; - } - }); - return observable; - } - - @Override - protected Observable construct() { - Observable observable = Observable.create(f -> { - try { - invocation.next(resp -> { - if (isFailedResponse(resp)) { - // e should implements toString - LOG.warn("bizkeeper command failed due to:" + resp.getResult()); - f.onError(resp.getResult()); - } else { - f.onNext(resp); - f.onCompleted(); - } - }); - } catch (Exception e) { - LOG.warn("bizkeeper command execute failed due to:" + e.getClass().getName()); - f.onError(e); - } - }); - return observable; - } - - protected abstract boolean isFailedResponse(Response resp); -} diff --git a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperExceptionUtils.java b/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperExceptionUtils.java deleted file mode 100644 index 595ca84c720..00000000000 --- a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperExceptionUtils.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import io.servicecomb.core.exception.CseException; -import io.servicecomb.core.exception.ExceptionUtils; - -public class BizkeeperExceptionUtils extends ExceptionUtils { - public static final String CSE_HANDLER_BK_FALLBACK = "cse.bizkeeper.fallback"; - - static { - ERROR_DESC_MGR.register(CSE_HANDLER_BK_FALLBACK, - "This is a fallback call from circuit breaker. " - + "\n You can add fallback logic by catching this exception. " + "\n info: operation=%s."); - } - - public static CseException createBizkeeperException(String code, Throwable cause, Object... args) { - String msg = String.format(ERROR_DESC_MGR.ensureFindValue(code), args); - CseException exception = new CseException(code, msg, cause); - return exception; - } -} diff --git a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperHandler.java b/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperHandler.java deleted file mode 100644 index 8985bfe3512..00000000000 --- a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperHandler.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.hystrix.HystrixCommandProperties; -import com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy; -import com.netflix.hystrix.HystrixInvokable; -import com.netflix.hystrix.HystrixObservable; -import com.netflix.hystrix.strategy.HystrixPlugins; -import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.handler.impl.AbstractHandler; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.Response; -import rx.Observable; - -/** - * 提供createBizkeeperCommand抽象接口来创建不同的处理链实例。 - * - */ -public abstract class BizkeeperHandler extends AbstractHandler { - private static final Logger LOG = LoggerFactory.getLogger(BizkeeperHandler.class); - - protected final String groupname; - - private static final int WINDOW_IN_MILLISECONDS = 10000; - - private static final int WINDOW_BUCKETS = 10; - - private static final int SNAPSHOT_INTERVAL = 1000; - - static { - try { - HystrixPlugins.getInstance().registerPropertiesStrategy(HystrixPropertiesStrategyExt.getInstance()); - } catch (IllegalStateException e) { - LOG.warn("Hystrix properties already registered. Dynamic configuration may not work.", e); - } - try { - HystrixPlugins.getInstance().registerCommandExecutionHook(new HystrixCommandExecutionHook() { - public Exception onExecutionError(HystrixInvokable commandInstance, Exception e) { - LOG.warn("bizkeeper execution error", e); - return e; //by default, just pass through - } - }); - } catch (IllegalStateException e) { - LOG.warn("HystrixCommandExecutionHook already registered.", e); - } - } - - private BizkeeperHandlerDelegate delegate; - - public BizkeeperHandler(String groupname) { - this.groupname = groupname; - delegate = new BizkeeperHandlerDelegate(this); - } - - protected abstract BizkeeperCommand createBizkeeperCommand(Invocation invocation); - - @Override - public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception { - HystrixObservable command = delegate.createBizkeeperCommand(invocation); - - Observable observable = command.toObservable(); - observable.subscribe(response -> { - asyncResp.complete(response); - }, error -> { - LOG.warn("catch error in bizkeeper:" + error.getMessage()); - asyncResp.fail(invocation.getInvocationType(), error); - }, () -> { - - }); - } - - protected void setCommonProperties(Invocation invocation, HystrixCommandProperties.Setter setter) { - setter.withExecutionTimeoutInMilliseconds(Configuration.INSTANCE - .getIsolationTimeoutInMilliseconds(groupname, - invocation.getMicroserviceName(), - invocation.getOperationMeta().getMicroserviceQualifiedName())) - .withExecutionIsolationSemaphoreMaxConcurrentRequests(Configuration.INSTANCE - .getIsolationMaxConcurrentRequests(groupname, - invocation.getMicroserviceName(), - invocation.getOperationMeta() - .getMicroserviceQualifiedName())) - .withExecutionTimeoutEnabled( - Configuration.INSTANCE.getIsolationTimeoutEnabled(groupname, - invocation.getMicroserviceName(), - invocation.getOperationMeta() - .getMicroserviceQualifiedName())) - .withCircuitBreakerEnabled(Configuration.INSTANCE.isCircuitBreakerEnabled(groupname, - invocation.getMicroserviceName(), - invocation.getOperationMeta() - .getMicroserviceQualifiedName())) - .withCircuitBreakerForceOpen(Configuration.INSTANCE.isCircuitBreakerForceOpen(groupname, - invocation.getMicroserviceName(), - invocation.getOperationMeta() - .getMicroserviceQualifiedName())) - .withCircuitBreakerForceClosed(Configuration.INSTANCE.isCircuitBreakerForceClosed(groupname, - invocation.getMicroserviceName(), - invocation.getOperationMeta() - .getMicroserviceQualifiedName())) - .withCircuitBreakerSleepWindowInMilliseconds( - Configuration.INSTANCE.getCircuitBreakerSleepWindowInMilliseconds(groupname, - invocation.getMicroserviceName(), - invocation.getOperationMeta() - .getMicroserviceQualifiedName())) - .withCircuitBreakerRequestVolumeThreshold( - Configuration.INSTANCE.getCircuitBreakerRequestVolumeThreshold(groupname, - invocation.getMicroserviceName(), - invocation.getOperationMeta() - .getMicroserviceQualifiedName())) - .withCircuitBreakerErrorThresholdPercentage( - Configuration.INSTANCE.getCircuitBreakerErrorThresholdPercentage(groupname, - invocation.getMicroserviceName(), - invocation.getOperationMeta() - .getMicroserviceQualifiedName())) - .withFallbackEnabled( - Configuration.INSTANCE.isFallbackEnabled(groupname, - invocation.getMicroserviceName(), - invocation.getOperationMeta().getMicroserviceQualifiedName())) - .withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE) - .withMetricsRollingPercentileEnabled(false) - .withMetricsRollingStatisticalWindowInMilliseconds(WINDOW_IN_MILLISECONDS) - .withMetricsRollingStatisticalWindowBuckets(WINDOW_BUCKETS) - .withMetricsHealthSnapshotIntervalInMilliseconds(SNAPSHOT_INTERVAL) - .withFallbackIsolationSemaphoreMaxConcurrentRequests(Configuration.INSTANCE - .getFallbackMaxConcurrentRequests(groupname, - invocation.getMicroserviceName(), - invocation.getOperationMeta().getMicroserviceQualifiedName())); - } -} diff --git a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperHandlerDelegate.java b/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperHandlerDelegate.java deleted file mode 100644 index 012fc6aafaa..00000000000 --- a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperHandlerDelegate.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import com.netflix.hystrix.HystrixObservable; - -import io.servicecomb.core.Invocation; -import io.servicecomb.swagger.invocation.Response; -import rx.Observable; -import rx.Subscription; -import rx.functions.Action0; -import rx.subjects.ReplaySubject; - -public class BizkeeperHandlerDelegate { - - private BizkeeperHandler handler; - - public BizkeeperHandlerDelegate(BizkeeperHandler handler) { - this.handler = handler; - } - - protected HystrixObservable createBizkeeperCommand(Invocation invocation) { - if (Configuration.INSTANCE.isFallbackForce(handler.groupname, - invocation.getMicroserviceName(), - invocation.getOperationMeta().getMicroserviceQualifiedName())) { - return forceFallbackCommand(invocation); - } - return handler.createBizkeeperCommand(invocation); - } - - protected HystrixObservable forceFallbackCommand(Invocation invocation) { - return new HystrixObservable() { - @Override - public Observable observe() { - ReplaySubject subject = ReplaySubject.create(); - final Subscription sourceSubscription = toObservable().subscribe(subject); - return subject.doOnUnsubscribe(new Action0() { - @Override - public void call() { - sourceSubscription.unsubscribe(); - } - }); - } - - @Override - public Observable toObservable() { - return Observable.create(f -> { - try { - if (Configuration.FALLBACKPOLICY_POLICY_RETURN - .equals(Configuration.INSTANCE.getFallbackPolicyPolicy(handler.groupname, - invocation.getMicroserviceName(), - invocation.getOperationMeta().getMicroserviceQualifiedName()))) { - f.onNext(Response.succResp(null)); - } else { - f.onNext(Response.failResp(invocation.getInvocationType(), - BizkeeperExceptionUtils - .createBizkeeperException(BizkeeperExceptionUtils.CSE_HANDLER_BK_FALLBACK, - null, - invocation.getOperationMeta().getMicroserviceQualifiedName()))); - } - } catch (Exception e) { - f.onError(e); - } - }); - } - }; - } -} diff --git a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperRequestContext.java b/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperRequestContext.java deleted file mode 100644 index e88c0d554f3..00000000000 --- a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/BizkeeperRequestContext.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; - -/** - * 使用缓存功能的时候,需要首先使用BizkeeperRequestContext来初始化上下文 - * - */ -public final class BizkeeperRequestContext { - private HystrixRequestContext context; - - private BizkeeperRequestContext(HystrixRequestContext context) { - this.context = context; - } - - public static BizkeeperRequestContext initializeContext() { - return new BizkeeperRequestContext(HystrixRequestContext.initializeContext()); - } - - public void shutdown() { - this.context.shutdown(); - } -} diff --git a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/CommandKey.java b/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/CommandKey.java deleted file mode 100644 index 980604adfe5..00000000000 --- a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/CommandKey.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import com.netflix.hystrix.HystrixCommandGroupKey; -import com.netflix.hystrix.HystrixCommandKey; - -import io.servicecomb.core.Invocation; - -/** - * 创建对应的Key值 - * - */ -public final class CommandKey { - private CommandKey() { - } - - public static HystrixCommandGroupKey toHystrixCommandGroupKey(String type, Invocation invocation) { - return HystrixCommandGroupKey.Factory - .asKey(type + "." + invocation.getOperationMeta().getMicroserviceQualifiedName()); - } - - public static HystrixCommandKey toHystrixCommandKey(String type, Invocation invocation) { - return HystrixCommandKey.Factory - .asKey(type + "." + invocation.getOperationMeta().getMicroserviceQualifiedName()); - } -} diff --git a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/Configuration.java b/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/Configuration.java deleted file mode 100644 index 72c59ca0784..00000000000 --- a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/Configuration.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import com.netflix.config.DynamicPropertyFactory; - -public final class Configuration { - //isolation - private static final String ISOLATION = "cse.isolation."; - - private static final String ISOLATION_TIMEOUTINMILLISECONDS = "timeoutInMilliseconds"; - - private static final String ISOLATION_TIMEOUTINMILLISECONDS_OLD = - ".businessKeeper.command.execution.isolation.thread.timeoutInMilliseconds"; - - private static final String ISOLATION_TIMEOUTENABLED = "timeout.enabled"; - - private static final String ISOLATION_MAXCONCURRENTREQUESTS = "maxConcurrentRequests"; - - //circuit breaker - private static final String CIRCUIT_BREAKER = "cse.circuitBreaker."; - - private static final String CIRCUIT_BREAKER_ENABLED = "enabled"; - - private static final String CIRCUIT_BREAKER_FORCEOPEN = "forceOpen"; - - private static final String CIRCUIT_BREAKER_FORCECLOSED = "forceClosed"; - - private static final String CIRCUIT_BREAKER_SLEEPWINDOWINMILLISECONDS = "sleepWindowInMilliseconds"; - - private static final String CIRCUIT_BREAKER_REQUESTVOLUMETHRESHOLD = "requestVolumeThreshold"; - - private static final String CIRCUIT_BREAKER_ERRORTHRESHOLDPERCENTAGE = "errorThresholdPercentage"; - - // fallback - // following items only supports consumer - private static final String FALLBACK = "cse.fallback."; - - private static final String FALLBACK_ENABLED = "enabled"; - - private static final String FALLBACK_FORCE = "force"; - - private static final String FALLBACK_MAXCONCURRENTREQUESTS = "maxConcurrentRequests"; - - // fallbackpolicy - private static final String FALLBACKPOLICY = "cse.fallbackpolicy."; - - private static final String FALLBACKPOLICY_POLICY = "policy"; - - public static final String FALLBACKPOLICY_POLICY_THROW = "throwexception"; - - public static final String FALLBACKPOLICY_POLICY_RETURN = "returnnull"; - - private static final int DEFAULT_ISOLATION_TIMEOUT = 30000; - - private static final int DEFAULT_MAX_CONCURRENT_REQUESTS = 10; - - private static final int DEFAULT_SLEEP_WINDOW = 15000; - - private static final int DEFAULT_VOLUME_THRESHOLD = 20; - - private static final int DEFAULT_THRESHOLD_PERCENTAGE = 50; - - public static final Configuration INSTANCE = new Configuration(); - - private Configuration() { - - } - - public int getIsolationTimeoutInMilliseconds(String type, String microserviceName, - String qualifiedOperationName) { - int timeout; - String p = getProperty("30000", - ISOLATION + type + "." + qualifiedOperationName + "." + ISOLATION_TIMEOUTINMILLISECONDS, - ISOLATION + type + "." + microserviceName + "." + ISOLATION_TIMEOUTINMILLISECONDS, - ISOLATION + type + "." + ISOLATION_TIMEOUTINMILLISECONDS, - // 2.0 compatible - type + "." + microserviceName + ISOLATION_TIMEOUTINMILLISECONDS_OLD, - type + ".default" + ISOLATION_TIMEOUTINMILLISECONDS_OLD); - try { - timeout = Integer.parseInt(p); - } catch (NumberFormatException e) { - return DEFAULT_ISOLATION_TIMEOUT; - } - if (timeout > 0) { - return timeout; - } - return DEFAULT_ISOLATION_TIMEOUT; - } - - public boolean getIsolationTimeoutEnabled(String type, String microserviceName, - String qualifiedOperationName) { - String p = getProperty("false", - ISOLATION + type + "." + qualifiedOperationName + "." + ISOLATION_TIMEOUTENABLED, - ISOLATION + type + "." + microserviceName + "." + ISOLATION_TIMEOUTENABLED, - ISOLATION + type + "." + ISOLATION_TIMEOUTENABLED); - return Boolean.parseBoolean(p); - } - - public int getIsolationMaxConcurrentRequests(String type, String microserviceName, - String qualifiedOperationName) { - int concurrentRequests; - String p = getProperty("10", - ISOLATION + type + "." + qualifiedOperationName + "." + ISOLATION_MAXCONCURRENTREQUESTS, - ISOLATION + type + "." + microserviceName + "." + ISOLATION_MAXCONCURRENTREQUESTS, - ISOLATION + type + "." + ISOLATION_MAXCONCURRENTREQUESTS); - try { - concurrentRequests = Integer.parseInt(p); - } catch (NumberFormatException e) { - return DEFAULT_MAX_CONCURRENT_REQUESTS; - } - if (concurrentRequests > 0) { - return concurrentRequests; - } - return DEFAULT_MAX_CONCURRENT_REQUESTS; - } - - public boolean isCircuitBreakerEnabled(String type, String microserviceName, String qualifiedOperationName) { - String p = getProperty("true", - CIRCUIT_BREAKER + type + "." + qualifiedOperationName + "." + CIRCUIT_BREAKER_ENABLED, - CIRCUIT_BREAKER + type + "." + microserviceName + "." + CIRCUIT_BREAKER_ENABLED, - CIRCUIT_BREAKER + type + "." + CIRCUIT_BREAKER_ENABLED); - return Boolean.parseBoolean(p); - } - - public boolean isCircuitBreakerForceOpen(String type, String microserviceName, String qualifiedOperationName) { - String p = getProperty("false", - CIRCUIT_BREAKER + type + "." + qualifiedOperationName + "." + CIRCUIT_BREAKER_FORCEOPEN, - CIRCUIT_BREAKER + type + "." + microserviceName + "." + CIRCUIT_BREAKER_FORCEOPEN, - CIRCUIT_BREAKER + type + "." + CIRCUIT_BREAKER_FORCEOPEN); - return Boolean.parseBoolean(p); - } - - public boolean isCircuitBreakerForceClosed(String type, String microserviceName, String qualifiedOperationName) { - String p = getProperty("false", - CIRCUIT_BREAKER + type + "." + qualifiedOperationName + "." + CIRCUIT_BREAKER_FORCECLOSED, - CIRCUIT_BREAKER + type + "." + microserviceName + "." + CIRCUIT_BREAKER_FORCECLOSED, - CIRCUIT_BREAKER + type + "." + CIRCUIT_BREAKER_FORCECLOSED); - return Boolean.parseBoolean(p); - } - - public int getCircuitBreakerSleepWindowInMilliseconds(String type, String microserviceName, - String qualifiedOperationName) { - String p = getProperty("15000", - CIRCUIT_BREAKER + type + "." + qualifiedOperationName + "." - + CIRCUIT_BREAKER_SLEEPWINDOWINMILLISECONDS, - CIRCUIT_BREAKER + type + "." + microserviceName + "." + CIRCUIT_BREAKER_SLEEPWINDOWINMILLISECONDS, - CIRCUIT_BREAKER + type + "." + CIRCUIT_BREAKER_SLEEPWINDOWINMILLISECONDS); - try { - return Integer.parseInt(p); - } catch (NumberFormatException e) { - return DEFAULT_SLEEP_WINDOW; - } - } - - public int getCircuitBreakerRequestVolumeThreshold(String type, String microserviceName, - String qualifiedOperationName) { - String p = getProperty("20", - CIRCUIT_BREAKER + type + "." + qualifiedOperationName + "." - + CIRCUIT_BREAKER_REQUESTVOLUMETHRESHOLD, - CIRCUIT_BREAKER + type + "." + microserviceName + "." + CIRCUIT_BREAKER_REQUESTVOLUMETHRESHOLD, - CIRCUIT_BREAKER + type + "." + CIRCUIT_BREAKER_REQUESTVOLUMETHRESHOLD); - try { - return Integer.parseInt(p); - } catch (NumberFormatException e) { - return DEFAULT_VOLUME_THRESHOLD; - } - } - - public int getCircuitBreakerErrorThresholdPercentage(String type, String microserviceName, - String qualifiedOperationName) { - String p = getProperty("50", - CIRCUIT_BREAKER + type + "." + qualifiedOperationName + "." - + CIRCUIT_BREAKER_ERRORTHRESHOLDPERCENTAGE, - CIRCUIT_BREAKER + type + "." + microserviceName + "." + CIRCUIT_BREAKER_ERRORTHRESHOLDPERCENTAGE, - CIRCUIT_BREAKER + type + "." + CIRCUIT_BREAKER_ERRORTHRESHOLDPERCENTAGE); - try { - return Integer.parseInt(p); - } catch (NumberFormatException e) { - return DEFAULT_THRESHOLD_PERCENTAGE; - } - } - - public boolean isFallbackEnabled(String type, String microserviceName, String qualifiedOperationName) { - String p = getProperty("true", - FALLBACK + type + "." + qualifiedOperationName + "." + FALLBACK_ENABLED, - FALLBACK + type + "." + microserviceName + "." + FALLBACK_ENABLED, - FALLBACK + type + "." + FALLBACK_ENABLED); - return Boolean.parseBoolean(p); - } - - public boolean isFallbackForce(String type, String microserviceName, String qualifiedOperationName) { - String p = getProperty("false", - FALLBACK + type + "." + qualifiedOperationName + "." + FALLBACK_FORCE, - FALLBACK + type + "." + microserviceName + "." + FALLBACK_FORCE, - FALLBACK + type + "." + FALLBACK_FORCE); - return Boolean.parseBoolean(p); - } - - public int getFallbackMaxConcurrentRequests(String type, String microserviceName, String qualifiedOperationName) { - String p = getProperty("10", - FALLBACK + type + "." + qualifiedOperationName + "." + FALLBACK_MAXCONCURRENTREQUESTS, - FALLBACK + type + "." + microserviceName + "." + FALLBACK_MAXCONCURRENTREQUESTS, - FALLBACK + type + "." + FALLBACK_MAXCONCURRENTREQUESTS); - try { - return Integer.parseInt(p); - } catch (NumberFormatException e) { - return DEFAULT_MAX_CONCURRENT_REQUESTS; - } - } - - public String getFallbackPolicyPolicy(String type, String microserviceName, String qualifiedOperationName) { - String p = getProperty(null, - FALLBACKPOLICY + type + "." + qualifiedOperationName + "." + FALLBACKPOLICY_POLICY, - FALLBACKPOLICY + type + "." + microserviceName + "." + FALLBACKPOLICY_POLICY, - FALLBACKPOLICY + type + "." + FALLBACKPOLICY_POLICY); - if (FALLBACKPOLICY_POLICY_RETURN.equals(p)) { - return FALLBACKPOLICY_POLICY_RETURN; - } else { - return FALLBACKPOLICY_POLICY_THROW; - } - } - - private String getProperty(String defaultValue, String... keys) { - String property = null; - for (String key : keys) { - property = DynamicPropertyFactory.getInstance().getStringProperty(key, null).get(); - if (property != null) { - break; - } - } - - if (property != null) { - return property; - } else { - return defaultValue; - } - } -} diff --git a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/ConsumerBizkeeperCommand.java b/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/ConsumerBizkeeperCommand.java deleted file mode 100644 index 92a7fd47616..00000000000 --- a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/ConsumerBizkeeperCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import io.servicecomb.core.Invocation; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.ExceptionFactory; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -public class ConsumerBizkeeperCommand extends BizkeeperCommand { - protected ConsumerBizkeeperCommand(String type, Invocation invocation, - com.netflix.hystrix.HystrixObservableCommand.Setter setter) { - super(type, invocation, setter); - } - - @Override - protected boolean isFailedResponse(Response resp) { - if (resp.isFailed()) { - if (InvocationException.class.isInstance(resp.getResult())) { - InvocationException e = (InvocationException) resp.getResult(); - return e.getStatusCode() == ExceptionFactory.CONSUMER_INNER_STATUS_CODE; - } else { - return true; - } - } else { - return false; - } - } -} diff --git a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/ConsumerBizkeeperHandler.java b/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/ConsumerBizkeeperHandler.java deleted file mode 100644 index d37ee1e5afd..00000000000 --- a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/ConsumerBizkeeperHandler.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import com.netflix.hystrix.HystrixCommandProperties; -import com.netflix.hystrix.HystrixObservableCommand; - -import io.servicecomb.core.Invocation; - -/** - * 客户端调用链处理流程 - * - */ -public class ConsumerBizkeeperHandler extends BizkeeperHandler { - private static final String COMMAND_GROUP = "Consumer"; - - public ConsumerBizkeeperHandler() { - super(COMMAND_GROUP); - } - - @Override - protected BizkeeperCommand createBizkeeperCommand(Invocation invocation) { - HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter() - .withRequestCacheEnabled(true) - .withRequestLogEnabled(false); - setCommonProperties(invocation, setter); - - BizkeeperCommand command = new ConsumerBizkeeperCommand(groupname, invocation, - HystrixObservableCommand.Setter - .withGroupKey(CommandKey.toHystrixCommandGroupKey(groupname, invocation)) - .andCommandKey(CommandKey.toHystrixCommandKey(groupname, invocation)) - .andCommandPropertiesDefaults(setter)); - return command; - } -} diff --git a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/HystrixCommandPropertiesExt.java b/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/HystrixCommandPropertiesExt.java deleted file mode 100644 index 865f5dd7759..00000000000 --- a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/HystrixCommandPropertiesExt.java +++ /dev/null @@ -1,531 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import static com.netflix.hystrix.strategy.properties.HystrixPropertiesChainedProperty.forBoolean; -import static com.netflix.hystrix.strategy.properties.HystrixPropertiesChainedProperty.forInteger; -import static com.netflix.hystrix.strategy.properties.HystrixPropertiesChainedProperty.forString; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.hystrix.HystrixCommandKey; -import com.netflix.hystrix.HystrixCommandProperties; -import com.netflix.hystrix.strategy.properties.HystrixDynamicProperty; -import com.netflix.hystrix.strategy.properties.HystrixProperty; - -public class HystrixCommandPropertiesExt extends HystrixCommandProperties { - - private static final Logger LOGGER = LoggerFactory.getLogger(HystrixCommandProperties.class); - - /* defaults */ - /* package */ - // default => statisticalWindow: 10000 = 10 seconds (and default of 10 - // buckets so each bucket is 1 second) - static final Integer DEFAULT_METRICSROLLINGSTATISTICALWINDOW = 10000; - - // default => statisticalWindowBuckets: 10 = 10 buckets in a 10 second - // window so each bucket is 1 second - private static final Integer DEFAULT_METRICSROLLINGSTATISTICALWINDOWBUCKETS = 10; - - // default => statisticalWindowVolumeThreshold: 20 requests in 10 seconds - // must occur before statistics matter - private static final Integer DEFAULT_CIRCUITBREAKERREQUESTVOLUMETHRESHOLD = 20; - - // default => sleepWindow: 5000 = 5 seconds that we will sleep before trying - // again after tripping the circuit - private static final Integer DEFAULT_CIRCUITBREAKERSLEEPWINDOWINMILLISECONDS = 5000; - - // default => errorThresholdPercentage = 50 = if 50%+ of requests in 10 - // seconds are failures or latent then we will trip the circuit - private static final Integer DEFAULT_CIRCUITBREAKERERRORTHRESHOLDPERCENTAGE = 50; - - // default => forceCircuitOpen = false (we want to allow traffic) - private static final Boolean DEFAULT_CIRCUITBREAKERFORCEOPEN = false; - - /* package */ - // default => ignoreErrors = false - static final Boolean DEFAULT_CIRCUITBREAKERFORCECLOSED = false; - - // default => executionTimeoutInMilliseconds: 1000 = 1 second - private static final Integer DEFAULT_EXECUTIONTIMEOUTINMILLISECONDS = 1000; - - private static final Boolean DEFAULT_EXECUTIONTIMEOUTENABLED = true; - - private static final ExecutionIsolationStrategy DEFAULT_ISOLATIONSTRATEGY = ExecutionIsolationStrategy.THREAD; - - private static final Boolean DEFAULT_EXECUTIONISOLATIONTHREADINTERRUPTONTIMEOUT = true; - - private static final Boolean DEFAULT_METRICSROLLINGPERCENTILEENABLED = true; - - private static final Boolean DEFAULT_REQUESTCACHEENABLED = true; - - private static final Integer DEFAULT_FALLBACKISOLATIONSEMAPHOREMAXCONCURRENTREQUESTS = 10; - - private static final Boolean DEFAULT_FALLBACKENABLED = true; - - private static final Integer DEFAULT_EXECUTIONISOLATIONSEMAPHOREMAXCONCURRENTREQUESTS = 10; - - private static final Boolean DEFAULT_REQUESTLOGENABLED = true; - - private static final Boolean DEFAULT_CIRCUITBREAKERENABLED = true; - - // default to 1 minute for RollingPercentile - private static final Integer DEFAULT_METRICSROLLINGPERCENTILEWINDOW = 60000; - - // default to 6 buckets (10 seconds each in 60 second window) - private static final Integer DEFAULT_METRICSROLLINGPERCENTILEWINDOWBUCKETS = 6; - - // default to 100 values max per bucket - private static final Integer DEFAULT_METRICSROLLINGPERCENTILEBUCKETSIZE = 100; - - // default to 500ms as max frequency between allowing snapshots of health - // (error percentage etc) - private static final Integer DEFAULT_METRICSHEALTHSNAPSHOTINTERVALINMILLISECONDS = 500; - - private static final int COMMAND_KEY_LENGTH = 3; - - @SuppressWarnings("unused") - private final HystrixCommandKey key; - - // number of requests that must be made within a statisticalWindow before - // open/close decisions are made using stats - private final HystrixProperty circuitBreakerRequestVolumeThreshold; - - // milliseconds after tripping circuit before allowing retry - private final HystrixProperty circuitBreakerSleepWindowInMilliseconds; - - // Whether circuit breaker should be enabled. - private final HystrixProperty circuitBreakerEnabled; - - // % of 'marks' that must be failed to trip the circuit - private final HystrixProperty circuitBreakerErrorThresholdPercentage; - - // a property to allow forcing the circuit open (stopping all requests) - private final HystrixProperty circuitBreakerForceOpen; - - // a property to allow ignoring errors and therefore never trip 'open' (ie. - // allow all traffic through) - private final HystrixProperty circuitBreakerForceClosed; - - // Whether a command should be executed in a separate thread or not. - private final HystrixProperty executionIsolationStrategy; - - // Timeout value in milliseconds for a command - private final HystrixProperty executionTimeoutInMilliseconds; - - // Whether timeout should be triggered - private final HystrixProperty executionTimeoutEnabled; - - // What thread-pool this command should run in (if running on a separate - // thread). - private final HystrixProperty executionIsolationThreadPoolKeyOverride; - - // Number of permits for execution semaphore - private final HystrixProperty executionIsolationSemaphoreMaxConcurrentRequests; - - // Number of permits for fallback semaphore - private final HystrixProperty fallbackIsolationSemaphoreMaxConcurrentRequests; - - // Whether fallback should be attempted. - private final HystrixProperty fallbackEnabled; - - // Whether an underlying Future/Thread (when runInSeparateThread == true) - // should be interrupted after a timeout - private final HystrixProperty executionIsolationThreadInterruptOnTimeout; - - // milliseconds back that will be tracked - private final HystrixProperty metricsRollingStatisticalWindowInMilliseconds; - - // number of buckets in the statisticalWindow - private final HystrixProperty metricsRollingStatisticalWindowBuckets; - - // Whether monitoring should be enabled (SLA and Tracers). - private final HystrixProperty metricsRollingPercentileEnabled; - - // number of milliseconds that will be tracked in RollingPercentile - private final HystrixProperty metricsRollingPercentileWindowInMilliseconds; - - // number of buckets percentileWindow will be divided into - private final HystrixProperty metricsRollingPercentileWindowBuckets; - - // how many values will be stored in each percentileWindowBucket - private final HystrixProperty metricsRollingPercentileBucketSize; - - // time between health snapshots - private final HystrixProperty metricsHealthSnapshotIntervalInMilliseconds; - - // whether command request logging is enabled. - private final HystrixProperty requestLogEnabled; - - // Whether request caching is enabled. - private final HystrixProperty requestCacheEnabled; - - protected HystrixCommandPropertiesExt(HystrixCommandKey key) { - this(key, HystrixCommandProperties.Setter(), "cse"); - } - - protected HystrixCommandPropertiesExt(HystrixCommandKey key, HystrixCommandProperties.Setter builder) { - this(key, builder, "cse"); - } - - protected HystrixCommandPropertiesExt(HystrixCommandKey key, HystrixCommandProperties.Setter builder, - String propertyPrefix) { - super(key, builder, propertyPrefix); - this.key = key; - this.circuitBreakerEnabled = getProperty(propertyPrefix, - "circuitBreaker", - key, - "enabled", - builder.getCircuitBreakerEnabled(), - DEFAULT_CIRCUITBREAKERENABLED); - this.circuitBreakerRequestVolumeThreshold = getProperty(propertyPrefix, - "circuitBreaker", - key, - "requestVolumeThreshold", - builder.getCircuitBreakerRequestVolumeThreshold(), - DEFAULT_CIRCUITBREAKERREQUESTVOLUMETHRESHOLD); - this.circuitBreakerSleepWindowInMilliseconds = getProperty(propertyPrefix, - "circuitBreaker", - key, - "sleepWindowInMilliseconds", - builder.getCircuitBreakerSleepWindowInMilliseconds(), - DEFAULT_CIRCUITBREAKERSLEEPWINDOWINMILLISECONDS); - this.circuitBreakerErrorThresholdPercentage = getProperty(propertyPrefix, - "circuitBreaker", - key, - "errorThresholdPercentage", - builder.getCircuitBreakerErrorThresholdPercentage(), - DEFAULT_CIRCUITBREAKERERRORTHRESHOLDPERCENTAGE); - this.circuitBreakerForceOpen = getProperty(propertyPrefix, - "circuitBreaker", - key, - "forceOpen", - builder.getCircuitBreakerForceOpen(), - DEFAULT_CIRCUITBREAKERFORCEOPEN); - this.circuitBreakerForceClosed = getProperty(propertyPrefix, - "circuitBreaker", - key, - "forceClosed", - builder.getCircuitBreakerForceClosed(), - DEFAULT_CIRCUITBREAKERFORCECLOSED); - this.executionIsolationStrategy = getProperty(propertyPrefix, - "isolation", - key, - "strategy", - builder.getExecutionIsolationStrategy(), - DEFAULT_ISOLATIONSTRATEGY); - this.executionTimeoutInMilliseconds = getProperty(propertyPrefix, - "isolation", - key, - "timeoutInMilliseconds", - builder.getExecutionTimeoutInMilliseconds(), - DEFAULT_EXECUTIONTIMEOUTINMILLISECONDS); - this.executionTimeoutEnabled = getProperty(propertyPrefix, - "isolation", - key, - "timeout.enabled", - builder.getExecutionTimeoutEnabled(), - DEFAULT_EXECUTIONTIMEOUTENABLED); - this.executionIsolationThreadInterruptOnTimeout = getProperty(propertyPrefix, - "isolation", - key, - "interruptOnTimeout", - builder.getExecutionIsolationThreadInterruptOnTimeout(), - DEFAULT_EXECUTIONISOLATIONTHREADINTERRUPTONTIMEOUT); - this.executionIsolationSemaphoreMaxConcurrentRequests = getProperty(propertyPrefix, - "isolation", - key, - "maxConcurrentRequests", - builder.getExecutionIsolationSemaphoreMaxConcurrentRequests(), - DEFAULT_EXECUTIONISOLATIONSEMAPHOREMAXCONCURRENTREQUESTS); - this.fallbackIsolationSemaphoreMaxConcurrentRequests = getProperty(propertyPrefix, - "fallback", - key, - "maxConcurrentRequests", - builder.getFallbackIsolationSemaphoreMaxConcurrentRequests(), - DEFAULT_FALLBACKISOLATIONSEMAPHOREMAXCONCURRENTREQUESTS); - this.fallbackEnabled = getProperty(propertyPrefix, - "fallback", - key, - "enabled", - builder.getFallbackEnabled(), - DEFAULT_FALLBACKENABLED); - this.metricsRollingStatisticalWindowInMilliseconds = getProperty(propertyPrefix, - "metrics", - key, - "rollingStats.timeInMilliseconds", - builder.getMetricsRollingStatisticalWindowInMilliseconds(), - DEFAULT_METRICSROLLINGSTATISTICALWINDOW); - this.metricsRollingStatisticalWindowBuckets = getProperty(propertyPrefix, - "metrics", - key, - "rollingStats.numBuckets", - builder.getMetricsRollingStatisticalWindowBuckets(), - DEFAULT_METRICSROLLINGSTATISTICALWINDOWBUCKETS); - this.metricsRollingPercentileEnabled = getProperty(propertyPrefix, - "metrics", - key, - "rollingPercentile.enabled", - builder.getMetricsRollingPercentileEnabled(), - DEFAULT_METRICSROLLINGPERCENTILEENABLED); - this.metricsRollingPercentileWindowInMilliseconds = getProperty(propertyPrefix, - "metrics", - key, - "rollingPercentile.timeInMilliseconds", - builder.getMetricsRollingPercentileWindowInMilliseconds(), - DEFAULT_METRICSROLLINGPERCENTILEWINDOW); - this.metricsRollingPercentileWindowBuckets = getProperty(propertyPrefix, - "metrics", - key, - "rollingPercentile.numBuckets", - builder.getMetricsRollingPercentileWindowBuckets(), - DEFAULT_METRICSROLLINGPERCENTILEWINDOWBUCKETS); - this.metricsRollingPercentileBucketSize = getProperty(propertyPrefix, - "metrics", - key, - "rollingPercentile.bucketSize", - builder.getMetricsRollingPercentileBucketSize(), - DEFAULT_METRICSROLLINGPERCENTILEBUCKETSIZE); - this.metricsHealthSnapshotIntervalInMilliseconds = getProperty(propertyPrefix, - "metrics", - key, - "healthSnapshot.intervalInMilliseconds", - builder.getMetricsHealthSnapshotIntervalInMilliseconds(), - DEFAULT_METRICSHEALTHSNAPSHOTINTERVALINMILLISECONDS); - this.requestCacheEnabled = getProperty(propertyPrefix, - "requestCache", - key, - "enabled", - builder.getRequestCacheEnabled(), - DEFAULT_REQUESTCACHEENABLED); - this.requestLogEnabled = getProperty(propertyPrefix, - "requestLog", - key, - "enabled", - builder.getRequestLogEnabled(), - DEFAULT_REQUESTLOGENABLED); - - // threadpool doesn't have a global override, only instance level makes - // sense - this.executionIsolationThreadPoolKeyOverride = forString() - .add(propertyPrefix + ".command." + key.name() + ".threadPoolKeyOverride", null) - .build(); - } - - public HystrixProperty circuitBreakerEnabled() { - return circuitBreakerEnabled; - } - - public HystrixProperty circuitBreakerErrorThresholdPercentage() { - return circuitBreakerErrorThresholdPercentage; - } - - public HystrixProperty circuitBreakerForceClosed() { - return circuitBreakerForceClosed; - } - - public HystrixProperty circuitBreakerForceOpen() { - return circuitBreakerForceOpen; - } - - public HystrixProperty circuitBreakerRequestVolumeThreshold() { - return circuitBreakerRequestVolumeThreshold; - } - - public HystrixProperty circuitBreakerSleepWindowInMilliseconds() { - return circuitBreakerSleepWindowInMilliseconds; - } - - public HystrixProperty executionIsolationSemaphoreMaxConcurrentRequests() { - return executionIsolationSemaphoreMaxConcurrentRequests; - } - - public HystrixProperty executionIsolationStrategy() { - return executionIsolationStrategy; - } - - public HystrixProperty executionIsolationThreadInterruptOnTimeout() { - return executionIsolationThreadInterruptOnTimeout; - } - - public HystrixProperty executionIsolationThreadPoolKeyOverride() { - return executionIsolationThreadPoolKeyOverride; - } - - @Deprecated // prefer {@link #executionTimeoutInMilliseconds} - public HystrixProperty executionIsolationThreadTimeoutInMilliseconds() { - return executionTimeoutInMilliseconds; - } - - public HystrixProperty executionTimeoutInMilliseconds() { - return executionIsolationThreadTimeoutInMilliseconds(); - } - - public HystrixProperty executionTimeoutEnabled() { - return executionTimeoutEnabled; - } - - public HystrixProperty fallbackIsolationSemaphoreMaxConcurrentRequests() { - return fallbackIsolationSemaphoreMaxConcurrentRequests; - } - - public HystrixProperty fallbackEnabled() { - return fallbackEnabled; - } - - public HystrixProperty metricsHealthSnapshotIntervalInMilliseconds() { - return metricsHealthSnapshotIntervalInMilliseconds; - } - - public HystrixProperty metricsRollingPercentileBucketSize() { - return metricsRollingPercentileBucketSize; - } - - public HystrixProperty metricsRollingPercentileEnabled() { - return metricsRollingPercentileEnabled; - } - - public HystrixProperty metricsRollingPercentileWindow() { - return metricsRollingPercentileWindowInMilliseconds; - } - - public HystrixProperty metricsRollingPercentileWindowInMilliseconds() { - return metricsRollingPercentileWindowInMilliseconds; - } - - public HystrixProperty metricsRollingPercentileWindowBuckets() { - return metricsRollingPercentileWindowBuckets; - } - - public HystrixProperty metricsRollingStatisticalWindowInMilliseconds() { - return metricsRollingStatisticalWindowInMilliseconds; - } - - public HystrixProperty metricsRollingStatisticalWindowBuckets() { - return metricsRollingStatisticalWindowBuckets; - } - - public HystrixProperty requestCacheEnabled() { - return requestCacheEnabled; - } - - public HystrixProperty requestLogEnabled() { - return requestLogEnabled; - } - - private HystrixProperty getProperty(String propertyPrefix, String command, - HystrixCommandKey key, String instanceProperty, ExecutionIsolationStrategy builderOverrideValue, - ExecutionIsolationStrategy defaultValue) { - return new ExecutionIsolationStrategyHystrixProperty(builderOverrideValue, key, propertyPrefix, command, - defaultValue, instanceProperty); - } - - private static final class ExecutionIsolationStrategyHystrixProperty - implements HystrixProperty { - private final HystrixDynamicProperty property; - - private volatile ExecutionIsolationStrategy value; - - private final ExecutionIsolationStrategy defaultValue; - - private ExecutionIsolationStrategyHystrixProperty(ExecutionIsolationStrategy builderOverrideValue, - HystrixCommandKey key, String propertyPrefix, String command, ExecutionIsolationStrategy defaultValue, - String instanceProperty) { - this.defaultValue = defaultValue; - String overrideValue = null; - if (builderOverrideValue != null) { - overrideValue = builderOverrideValue.name(); - } - property = forString() - .add(propertyPrefix + "." + command + "." + key.name() + "." + instanceProperty, null) - .add(propertyPrefix + "." + command + "." + serviceKey(key.name()) + "." + instanceProperty, - overrideValue) - .add(propertyPrefix + "." + command + "." + typeKey(key.name()) + "." + instanceProperty, - defaultValue.name()) - .build(); - - // initialize the enum value from the property - parseProperty(); - - // use a callback to handle changes so we only handle the parse cost - // on updates rather than every fetch - property.addCallback(new Runnable() { - - @Override - public void run() { - // when the property value changes we'll update the value - parseProperty(); - } - }); - } - - @Override - public ExecutionIsolationStrategy get() { - return value; - } - - private void parseProperty() { - try { - value = ExecutionIsolationStrategy.valueOf(property.get()); - } catch (Exception e) { - LOGGER.error("Unable to derive ExecutionIsolationStrategy from property value: " + property.get(), e); - // use the default value - value = defaultValue; - } - } - } - - private static String serviceKey(String key) { - String[] keyparts = key.split("\\.", COMMAND_KEY_LENGTH); - if (keyparts.length == COMMAND_KEY_LENGTH) { - return keyparts[0] + "." + keyparts[1]; - } - return key; - } - - private static String typeKey(String key) { - int index = key.indexOf("."); - if (index > 0) { - return key.substring(0, index); - } - return key; - } - - private HystrixProperty getProperty(String propertyPrefix, String command, HystrixCommandKey key, - String instanceProperty, Integer builderOverrideValue, - Integer defaultValue) { - return forInteger() - .add(propertyPrefix + "." + command + "." + key.name() + "." + instanceProperty, null) - .add(propertyPrefix + "." + command + "." + serviceKey(key.name()) + "." + instanceProperty, - null) - .add(propertyPrefix + "." + command + "." + typeKey(key.name()) + "." + instanceProperty, - builderOverrideValue == null ? defaultValue : builderOverrideValue) - .build(); - } - - private HystrixProperty getProperty(String propertyPrefix, String command, HystrixCommandKey key, - String instanceProperty, Boolean builderOverrideValue, Boolean defaultValue) { - return forBoolean() - .add(propertyPrefix + "." + command + "." + key.name() + "." + instanceProperty, null) - .add(propertyPrefix + "." + command + "." + serviceKey(key.name()) + "." + instanceProperty, - null) - .add(propertyPrefix + "." + command + "." + typeKey(key.name()) + "." + instanceProperty, - builderOverrideValue == null ? defaultValue : builderOverrideValue) - .build(); - } -} diff --git a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/HystrixPropertiesStrategyExt.java b/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/HystrixPropertiesStrategyExt.java deleted file mode 100644 index 55afd7c8e32..00000000000 --- a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/HystrixPropertiesStrategyExt.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import com.netflix.hystrix.HystrixCommandKey; -import com.netflix.hystrix.HystrixCommandProperties; -import com.netflix.hystrix.HystrixCommandProperties.Setter; -import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy; - -/** - * 定制HystrixPropertiesStrategy。 配置需要支持动态生效和调整, 默认的hystrix实现会根据CommonKey进行缓存。 - * CircuitBreake需要统计并记录请求数据,需要缓存并保证每个commandKey对应一个Breaker;单纯清理CommandKey无法动态生效。 - * 参考:HystrixCircuitBreaker.Factory.getInstance(commandKey, groupKey, - * properties, metrics) - * - */ -public final class HystrixPropertiesStrategyExt extends HystrixPropertiesStrategy { - - private static final HystrixPropertiesStrategyExt INSTANCE = new HystrixPropertiesStrategyExt(); - - private final Map commandPropertiesMap = new ConcurrentHashMap<>(); - - private HystrixPropertiesStrategyExt() { - } - - public static HystrixPropertiesStrategyExt getInstance() { - return INSTANCE; - } - - @Override - public HystrixCommandProperties getCommandProperties(HystrixCommandKey commandKey, Setter builder) { - HystrixCommandProperties commandProperties = commandPropertiesMap.get(commandKey.name()); - if (commandProperties == null) { - commandProperties = new HystrixCommandPropertiesExt(commandKey, builder); - commandPropertiesMap.putIfAbsent(commandKey.name(), commandProperties); - } - return commandProperties; - } -} diff --git a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/ProviderBizkeeperCommand.java b/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/ProviderBizkeeperCommand.java deleted file mode 100644 index e9336f9ee57..00000000000 --- a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/ProviderBizkeeperCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import io.servicecomb.core.Invocation; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.ExceptionFactory; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -public class ProviderBizkeeperCommand extends BizkeeperCommand { - protected ProviderBizkeeperCommand(String type, Invocation invocation, - com.netflix.hystrix.HystrixObservableCommand.Setter setter) { - super(type, invocation, setter); - } - - @Override - protected boolean isFailedResponse(Response resp) { - if (resp.isFailed()) { - if (InvocationException.class.isInstance(resp.getResult())) { - InvocationException e = (InvocationException) resp.getResult(); - return e.getStatusCode() == ExceptionFactory.PRODUCER_INNER_STATUS_CODE; - } else { - return true; - } - } else { - return false; - } - } -} diff --git a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/ProviderBizkeeperHanlder.java b/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/ProviderBizkeeperHanlder.java deleted file mode 100644 index f1b7c3722d4..00000000000 --- a/handlers/handler-bizkeeper/src/main/java/io/servicecomb/bizkeeper/ProviderBizkeeperHanlder.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import com.netflix.hystrix.HystrixCommandProperties; -import com.netflix.hystrix.HystrixObservableCommand; - -import io.servicecomb.core.Invocation; - -/** - * 服务端调用链处理流程 - * - */ -public class ProviderBizkeeperHanlder extends BizkeeperHandler { - private static final String COMMAND_GROUP = "Provider"; - - public ProviderBizkeeperHanlder() { - super(COMMAND_GROUP); - } - - @Override - protected BizkeeperCommand createBizkeeperCommand(Invocation invocation) { - HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter() - .withRequestCacheEnabled(false) - .withRequestLogEnabled(false); - setCommonProperties(invocation, setter); - - BizkeeperCommand command = new ProviderBizkeeperCommand(groupname, invocation, - HystrixObservableCommand.Setter - .withGroupKey(CommandKey.toHystrixCommandGroupKey(groupname, invocation)) - .andCommandKey(CommandKey.toHystrixCommandKey(groupname, invocation)) - .andCommandPropertiesDefaults(setter)); - return command; - } -} diff --git a/handlers/handler-bizkeeper/src/main/resources/config/cse.handler.xml b/handlers/handler-bizkeeper/src/main/resources/config/cse.handler.xml deleted file mode 100644 index b1f051d6dcf..00000000000 --- a/handlers/handler-bizkeeper/src/main/resources/config/cse.handler.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestBizkeeperCommand.java b/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestBizkeeperCommand.java deleted file mode 100644 index 3d1dc99fc22..00000000000 --- a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestBizkeeperCommand.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.hystrix.HystrixCommandProperties; -import com.netflix.hystrix.HystrixObservableCommand; -import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.InvocationException; -import rx.Observable; - -public class TestBizkeeperCommand { - - @Test - public void testGetCacheKeyProvider() { - - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()).thenReturn("test1"); - - HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter() - .withRequestCacheEnabled(true) - .withRequestLogEnabled(false); - - BizkeeperCommand bizkeeperCommand = new ProviderBizkeeperCommand("groupname", invocation, - HystrixObservableCommand.Setter - .withGroupKey(CommandKey.toHystrixCommandGroupKey("groupname", invocation)) - .andCommandKey(CommandKey.toHystrixCommandKey("groupname", invocation)) - .andCommandPropertiesDefaults(setter)); - - String str = bizkeeperCommand.getCacheKey(); - Assert.assertNull(str); - - Response resp = Mockito.mock(Response.class); - Mockito.when(resp.isFailed()).thenReturn(false); - Assert.assertEquals(false, bizkeeperCommand.isFailedResponse(resp)); - Mockito.when(resp.isFailed()).thenReturn(true); - InvocationException excp = Mockito.mock(InvocationException.class); - Mockito.when(resp.getResult()).thenReturn(excp); - Mockito.when(excp.getStatusCode()).thenReturn(400); - Assert.assertEquals(false, bizkeeperCommand.isFailedResponse(resp)); - Mockito.when(resp.getResult()).thenReturn(excp); - Mockito.when(excp.getStatusCode()).thenReturn(590); - Assert.assertEquals(true, bizkeeperCommand.isFailedResponse(resp)); - } - - @Test - public void testResumeWithFallbackProvider() { - - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()).thenReturn("test1"); - - HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter() - .withRequestCacheEnabled(true) - .withRequestLogEnabled(false); - - BizkeeperCommand bizkeeperCommand = new ProviderBizkeeperCommand("groupname", invocation, - HystrixObservableCommand.Setter - .withGroupKey(CommandKey.toHystrixCommandGroupKey("groupname", invocation)) - .andCommandKey(CommandKey.toHystrixCommandKey("groupname", invocation)) - .andCommandPropertiesDefaults(setter)); - - Observable observe = bizkeeperCommand.resumeWithFallback(); - Assert.assertNotNull(observe); - } - - @Test - public void testConstructProvider() { - - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()).thenReturn("test1"); - - HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter() - .withRequestCacheEnabled(true) - .withRequestLogEnabled(false); - - BizkeeperCommand bizkeeperCommand = new ProviderBizkeeperCommand("groupname", invocation, - HystrixObservableCommand.Setter - .withGroupKey(CommandKey.toHystrixCommandGroupKey("groupname", invocation)) - .andCommandKey(CommandKey.toHystrixCommandKey("groupname", invocation)) - .andCommandPropertiesDefaults(setter)); - - Observable response = bizkeeperCommand.construct(); - Assert.assertNotNull(response); - } - - @Test - public void testGetCacheKeyWithContextInitializedProvider() { - - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()).thenReturn("test1"); - - HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter() - .withRequestCacheEnabled(true) - .withRequestLogEnabled(false); - - BizkeeperCommand bizkeeperCommand = new ProviderBizkeeperCommand("groupname", invocation, - HystrixObservableCommand.Setter - .withGroupKey(CommandKey.toHystrixCommandGroupKey("groupname", invocation)) - .andCommandKey(CommandKey.toHystrixCommandKey("groupname", invocation)) - .andCommandPropertiesDefaults(setter)); - - HystrixRequestContext.initializeContext(); - String cacheKey = bizkeeperCommand.getCacheKey(); - Assert.assertNotNull(cacheKey); - } - - @Test - public void testGetCacheKeyConsumer() { - - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()).thenReturn("test1"); - - HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter() - .withRequestCacheEnabled(true) - .withRequestLogEnabled(false); - - BizkeeperCommand bizkeeperCommand = new ConsumerBizkeeperCommand("groupname", invocation, - HystrixObservableCommand.Setter - .withGroupKey(CommandKey.toHystrixCommandGroupKey("groupname", invocation)) - .andCommandKey(CommandKey.toHystrixCommandKey("groupname", invocation)) - .andCommandPropertiesDefaults(setter)); - - String str = bizkeeperCommand.getCacheKey(); - Assert.assertNull(str); - - Response resp = Mockito.mock(Response.class); - Mockito.when(resp.isFailed()).thenReturn(false); - Assert.assertEquals(false, bizkeeperCommand.isFailedResponse(resp)); - Mockito.when(resp.isFailed()).thenReturn(true); - InvocationException excp = Mockito.mock(InvocationException.class); - Mockito.when(resp.getResult()).thenReturn(excp); - Mockito.when(excp.getStatusCode()).thenReturn(400); - Assert.assertEquals(false, bizkeeperCommand.isFailedResponse(resp)); - Mockito.when(resp.getResult()).thenReturn(excp); - Mockito.when(excp.getStatusCode()).thenReturn(490); - Assert.assertEquals(true, bizkeeperCommand.isFailedResponse(resp)); - } - - @Test - public void testResumeWithFallbackConsumer() { - - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()).thenReturn("test1"); - - HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter() - .withRequestCacheEnabled(true) - .withRequestLogEnabled(false); - - BizkeeperCommand bizkeeperCommand = new ConsumerBizkeeperCommand("groupname", invocation, - HystrixObservableCommand.Setter - .withGroupKey(CommandKey.toHystrixCommandGroupKey("groupname", invocation)) - .andCommandKey(CommandKey.toHystrixCommandKey("groupname", invocation)) - .andCommandPropertiesDefaults(setter)); - - Observable observe = bizkeeperCommand.resumeWithFallback(); - Assert.assertNotNull(observe); - } - - @Test - public void testConstructConsumer() { - - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()).thenReturn("test1"); - - HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter() - .withRequestCacheEnabled(true) - .withRequestLogEnabled(false); - - BizkeeperCommand bizkeeperCommand = new ConsumerBizkeeperCommand("groupname", invocation, - HystrixObservableCommand.Setter - .withGroupKey(CommandKey.toHystrixCommandGroupKey("groupname", invocation)) - .andCommandKey(CommandKey.toHystrixCommandKey("groupname", invocation)) - .andCommandPropertiesDefaults(setter)); - - Observable response = bizkeeperCommand.construct(); - Assert.assertNotNull(response); - } - - @Test - public void testGetCacheKeyWithContextInitializedConsumer() { - - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()).thenReturn("test1"); - - HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter() - .withRequestCacheEnabled(true) - .withRequestLogEnabled(false); - - BizkeeperCommand bizkeeperCommand = new ConsumerBizkeeperCommand("groupname", invocation, - HystrixObservableCommand.Setter - .withGroupKey(CommandKey.toHystrixCommandGroupKey("groupname", invocation)) - .andCommandKey(CommandKey.toHystrixCommandKey("groupname", invocation)) - .andCommandPropertiesDefaults(setter)); - - HystrixRequestContext.initializeContext(); - String cacheKey = bizkeeperCommand.getCacheKey(); - Assert.assertNotNull(cacheKey); - } -} diff --git a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestBizkeeperExceptionUtils.java b/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestBizkeeperExceptionUtils.java deleted file mode 100644 index 96d8c6042c9..00000000000 --- a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestBizkeeperExceptionUtils.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.exception.CseException; - -public class TestBizkeeperExceptionUtils { - - @Test - public void testCreateBizkeeperException() { - Assert.assertEquals("cse.bizkeeper.fallback", BizkeeperExceptionUtils.CSE_HANDLER_BK_FALLBACK); - CseException cseException = BizkeeperExceptionUtils.createBizkeeperException("cse.bizkeeper.fallback", - new Throwable(), - "ARGS"); - Assert.assertNotNull(cseException); - } -} diff --git a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestBizkeeperHandler.java b/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestBizkeeperHandler.java deleted file mode 100644 index 48859c3556a..00000000000 --- a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestBizkeeperHandler.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.hystrix.HystrixCommandProperties; -import com.netflix.hystrix.HystrixObservableCommand; -import com.netflix.hystrix.strategy.HystrixPlugins; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.swagger.invocation.AsyncResponse; - -public class TestBizkeeperHandler extends BizkeeperHandler { - - BizkeeperHandler bizkeeperHandler = null; - - private static final String GROUP_NAME = "Group_Name"; - - Invocation invocation = null; - - AsyncResponse asyncResp = null; - - public TestBizkeeperHandler() { - super(GROUP_NAME); - } - - @Before - public void setUp() throws Exception { - bizkeeperHandler = new TestBizkeeperHandler(); - invocation = Mockito.mock(Invocation.class); - asyncResp = Mockito.mock(AsyncResponse.class); - } - - @After - public void tearDown() throws Exception { - bizkeeperHandler = null; - invocation = null; - asyncResp = null; - } - - @Test - public void testHandleWithException() { - boolean validAssert; - HystrixPlugins.reset(); - try { - validAssert = true; - bizkeeperHandler.handle(invocation, asyncResp); - } catch (Exception e) { - validAssert = false; - } - Assert.assertFalse(validAssert); - } - - @Test - public void testHandle() { - boolean validAssert; - - try { - Assert.assertNotNull(bizkeeperHandler); - Mockito.when(invocation.getMicroserviceName()).thenReturn("test1"); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()).thenReturn("test1"); - validAssert = true; - - bizkeeperHandler.handle(invocation, asyncResp); - } catch (Exception exce) { - validAssert = false; - } - Assert.assertTrue(validAssert); - } - - @Test - public void testSetCommonProperties() { - boolean validAssert; - try { - validAssert = true; - HystrixPlugins.reset(); - HystrixCommandProperties.Setter setter = Mockito.mock(HystrixCommandProperties.Setter.class); - bizkeeperHandler.setCommonProperties(invocation, setter); - } catch (Exception e) { - validAssert = false; - } - Assert.assertFalse(validAssert); - } - - @Override - protected BizkeeperCommand createBizkeeperCommand(Invocation invocation) { - HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter() - .withRequestCacheEnabled(true) - .withRequestLogEnabled(false); - - BizkeeperCommand bizkeeperCommand = new ConsumerBizkeeperCommand("groupname", invocation, - HystrixObservableCommand.Setter - .withGroupKey(CommandKey.toHystrixCommandGroupKey("groupname", invocation)) - .andCommandKey(CommandKey.toHystrixCommandKey("groupname", invocation)) - .andCommandPropertiesDefaults(setter)); - return bizkeeperCommand; - } - - @Test - public void testHandleForceThrowException() throws Exception { - Mockito.when(invocation.getMicroserviceName()).thenReturn("testHandleForceThrowException"); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()) - .thenReturn("testHandleForceThrowException"); - System.setProperty("cse.fallback.Group_Name.testHandleForceThrowException.force", "true"); - System.setProperty("cse.fallbackpolicy.Group_Name.testHandleForceThrowException.policy", "throwexception"); - bizkeeperHandler.handle(invocation, f -> { - Assert.assertTrue(f.isFailed()); - }); - } - - @Test - public void testHandleForceReturnnull() throws Exception { - Mockito.when(invocation.getMicroserviceName()).thenReturn("testHandleForceReturnnull"); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()) - .thenReturn("testHandleForceReturnnull"); - System.setProperty("cse.fallback.Group_Name.testHandleForceReturnnull.force", "true"); - System.setProperty("cse.fallbackpolicy.Group_Name.testHandleForceReturnnull.policy", "returnnull"); - bizkeeperHandler.handle(invocation, f -> { - Assert.assertTrue(f.isSuccessed()); - Assert.assertEquals(null, (Object) f.getResult()); - }); - } -} diff --git a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestBizkeeperRequestContext.java b/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestBizkeeperRequestContext.java deleted file mode 100644 index a5c9b8a8452..00000000000 --- a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestBizkeeperRequestContext.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import org.junit.Assert; -import org.junit.Test; - -/** - * - * - */ -public class TestBizkeeperRequestContext { - - @Test - public void testInitializeContext() { - - BizkeeperRequestContext bizkeeperRequestContext = BizkeeperRequestContext.initializeContext(); - Assert.assertNotNull(bizkeeperRequestContext); - } - - @Test - public void testShutdown() { - BizkeeperRequestContext bizkeeperRequestContext = BizkeeperRequestContext.initializeContext(); - boolean validAssert; - - try { - bizkeeperRequestContext.shutdown(); - validAssert = true; - } catch (Exception e) { - validAssert = false; - } - - Assert.assertTrue(validAssert); - } -} diff --git a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestCommandKey.java b/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestCommandKey.java deleted file mode 100644 index a6e9a5e7e4a..00000000000 --- a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestCommandKey.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.hystrix.HystrixCommandGroupKey; -import com.netflix.hystrix.HystrixCommandKey; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; - -/** - * - * - */ -public class TestCommandKey { - - @Test - public void testToHystrixCommandGroupKey() { - Invocation invocation = Mockito.mock(Invocation.class); - - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()).thenReturn("test1"); - Assert.assertNotNull(invocation); - HystrixCommandGroupKey hystrixCommandGroupKey = CommandKey.toHystrixCommandGroupKey("groupname", invocation); - Assert.assertNotNull(hystrixCommandGroupKey); - } - - @Test - public void testToHystrixCommandKey() { - - Invocation invocation = Mockito.mock(Invocation.class); - - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()).thenReturn("test1"); - Assert.assertNotNull(invocation); - HystrixCommandKey hystrixCommandGroupKey = CommandKey.toHystrixCommandKey("groupname", invocation); - Assert.assertNotNull(hystrixCommandGroupKey); - } -} diff --git a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestConfiguration.java b/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestConfiguration.java deleted file mode 100644 index 041b17c23c1..00000000000 --- a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestConfiguration.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; - -public class TestConfiguration { - @Test - public void testConfiguration() { - - assertNotNull(Configuration.INSTANCE); - assertEquals("returnnull", Configuration.FALLBACKPOLICY_POLICY_RETURN); - assertEquals("throwexception", Configuration.FALLBACKPOLICY_POLICY_THROW); - - Configuration c = Configuration.INSTANCE; - Invocation invocation = Mockito.mock(Invocation.class); - String test2 = invocation.getMicroserviceName(); - - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceName()).thenReturn("testqualify"); - - int res = c.getIsolationTimeoutInMilliseconds("groupname", test2, "testqualify"); - assertEquals(30000, res); - boolean b1 = c.getIsolationTimeoutEnabled("groupname", test2, "testqualify"); - assertFalse(b1); - int res1 = c.getIsolationMaxConcurrentRequests("groupname", test2, "testqualify"); - assertEquals(10, res1); - boolean b2 = c.isCircuitBreakerEnabled("groupname", test2, "testqualify"); - assertTrue(b2); - String str = c.getFallbackPolicyPolicy("groupname", test2, "testqualify"); - assertEquals("throwexception", str); - } -} diff --git a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestConsumerBizkeeperHandler.java b/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestConsumerBizkeeperHandler.java deleted file mode 100644 index 48ae1844cd9..00000000000 --- a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestConsumerBizkeeperHandler.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.hystrix.strategy.HystrixPlugins; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; - -/** - * - * - */ -public class TestConsumerBizkeeperHandler { - - @Test - public void testCreateBizkeeperCommand() { - HystrixPlugins.reset(); - ConsumerBizkeeperHandler consumerBizkeeperHandler = new ConsumerBizkeeperHandler(); - - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()).thenReturn("test1"); - - CommandKey.toHystrixCommandGroupKey("groupname", invocation); - CommandKey.toHystrixCommandKey("groupname", invocation); - BizkeeperCommand command = consumerBizkeeperHandler.createBizkeeperCommand(invocation); - Assert.assertNotNull(command); - } -} diff --git a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestHystrixPropertiesStrategyExt.java b/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestHystrixPropertiesStrategyExt.java deleted file mode 100644 index 280e61b6660..00000000000 --- a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestHystrixPropertiesStrategyExt.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import static org.junit.Assert.assertNotNull; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.hystrix.HystrixCommandKey; -import com.netflix.hystrix.HystrixCommandProperties; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; - -/** - * - */ -public class TestHystrixPropertiesStrategyExt { - - @Test - public void testGetCommandPropertiesCacheKey() { - - assertNotNull(HystrixPropertiesStrategyExt.getInstance()); - - HystrixPropertiesStrategyExt hps = HystrixPropertiesStrategyExt.getInstance(); - HystrixCommandKey commandKey = Mockito.mock(HystrixCommandKey.class); - - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceName()).thenReturn("testqualify"); - - HystrixCommandProperties.Setter setter = HystrixCommandProperties.Setter() - .withRequestCacheEnabled(true) - .withRequestLogEnabled(false) - .withFallbackIsolationSemaphoreMaxConcurrentRequests( - Configuration.INSTANCE.getFallbackMaxConcurrentRequests("groupname", - "testing", - invocation.getOperationMeta().getMicroserviceQualifiedName())); - - String str1 = hps.getCommandPropertiesCacheKey(commandKey, setter); - Assert.assertNull(str1); - } - - @Test - public void testgetCommandProperties() { - HystrixCommandKey commandKey = Mockito.mock(HystrixCommandKey.class); - Mockito.when(commandKey.name()) - .thenReturn("provider.HystrixPropertiesStrategyExtTest.testgetCommandProperties"); - HystrixCommandProperties commandPro = HystrixPropertiesStrategyExt.getInstance() - .getCommandProperties(commandKey, HystrixCommandProperties.Setter()); - Assert.assertTrue(commandPro.circuitBreakerEnabled().get()); - Assert.assertEquals(Integer.valueOf(50), commandPro.circuitBreakerErrorThresholdPercentage().get()); - Assert.assertFalse(commandPro.circuitBreakerForceClosed().get()); - Assert.assertFalse(commandPro.circuitBreakerForceOpen().get()); - Assert.assertEquals(Integer.valueOf(20), commandPro.circuitBreakerRequestVolumeThreshold().get()); - Assert.assertEquals(Integer.valueOf(5000), commandPro.circuitBreakerSleepWindowInMilliseconds().get()); - Assert.assertEquals(Integer.valueOf(10), commandPro.executionIsolationSemaphoreMaxConcurrentRequests().get()); - Assert.assertTrue(commandPro.executionIsolationThreadInterruptOnTimeout().get()); - Assert.assertEquals(null, commandPro.executionIsolationThreadPoolKeyOverride().get()); - Assert.assertEquals(Integer.valueOf(1000), commandPro.executionTimeoutInMilliseconds().get()); - Assert.assertTrue(commandPro.executionTimeoutEnabled().get()); - Assert.assertEquals(Integer.valueOf(10), commandPro.fallbackIsolationSemaphoreMaxConcurrentRequests().get()); - Assert.assertTrue(commandPro.fallbackEnabled().get()); - } -} diff --git a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestProviderBizkeeperHandler.java b/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestProviderBizkeeperHandler.java deleted file mode 100644 index f876de77e6c..00000000000 --- a/handlers/handler-bizkeeper/src/test/java/io/servicecomb/bizkeeper/TestProviderBizkeeperHandler.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.bizkeeper; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.hystrix.strategy.HystrixPlugins; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; - -/** - * - * - */ -public class TestProviderBizkeeperHandler { - - @Test - public void testCreateBizkeeperCommand() { - HystrixPlugins.reset(); - ProviderBizkeeperHanlder providerBizkeeperHanlder = new ProviderBizkeeperHanlder(); - - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(Mockito.mock(OperationMeta.class)); - Mockito.when(invocation.getOperationMeta().getMicroserviceQualifiedName()).thenReturn("test1"); - - CommandKey.toHystrixCommandGroupKey("groupname", invocation); - CommandKey.toHystrixCommandKey("groupname", invocation); - BizkeeperCommand command = providerBizkeeperHanlder.createBizkeeperCommand(invocation); - Assert.assertNotNull(command); - } -} diff --git a/handlers/handler-bizkeeper/src/test/resources/log4j.properties b/handlers/handler-bizkeeper/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/handlers/handler-bizkeeper/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/handlers/handler-fault-injection/pom.xml b/handlers/handler-fault-injection/pom.xml new file mode 100755 index 00000000000..825c63c2270 --- /dev/null +++ b/handlers/handler-fault-injection/pom.xml @@ -0,0 +1,56 @@ + + + + 4.0.0 + + org.apache.servicecomb + handlers + 3.4.0-SNAPSHOT + + handler-fault-injection + Java Chassis::Handlers::Fault Injection + + + org.apache.servicecomb + java-chassis-core + + + + 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 + test + + + 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 new file mode 100644 index 00000000000..e4eef91ef2f --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerAbortFaultFilter.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.faultinjection; + +import java.util.concurrent.CompletableFuture; + +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.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 extends AbstractFilter implements ConsumerFilter, EdgeFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerAbortFaultFilter.class); + + private static final String SUCCESS_RESPONSE = "success"; + + public static final String ABORTED_ERROR_MSG = "aborted by fault inject"; + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1020; + } + + @Override + public String getName() { + return "consumer-abort-fault"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + if (!shouldAbort(invocation)) { + return nextNode.onFilter(invocation); + } + + // get the config values related to abort percentage. + int errorCode = FaultInjectionUtil.getFaultInjectionConfig(invocation, "abort.httpStatus"); + if (errorCode == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { + LOGGER.debug("Fault injection: Abort error code is not configured"); + return CompletableFuture.completedFuture(Response.succResp(SUCCESS_RESPONSE)); + } + + // if request need to be abort then return failure with given error code + CommonExceptionData errorData = new CommonExceptionData(ABORTED_ERROR_MSG); + return CompletableFuture.failedFuture(new InvocationException(errorCode, ABORTED_ERROR_MSG, errorData)); + } + + private boolean shouldAbort(Invocation invocation) { + // get the config values related to abort. + int abortPercent = FaultInjectionUtil.getFaultInjectionConfig(invocation, "abort.percent"); + if (abortPercent == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { + LOGGER.debug("Fault injection: Abort percentage is not configured"); + return false; + } + + // check fault abort condition. + return FaultInjectionUtil.isFaultNeedToInject(abortPercent); + } +} 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 new file mode 100644 index 00000000000..e8c0f6ffd67 --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerDelayFaultFilter.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.faultinjection; + +import java.util.concurrent.CompletableFuture; + +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.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.Context; +import io.vertx.core.Vertx; + +public class ConsumerDelayFaultFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerDelayFaultFilter.class); + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1030; + } + + @Override + public String getName() { + return "consumer-delay-fault"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + if (!shouldDelay(invocation)) { + return nextNode.onFilter(invocation); + } + + LOGGER.debug("Fault injection: delay is added for the request by fault inject handler"); + long delay = FaultInjectionUtil.getFaultInjectionConfig(invocation, + "delay.fixedDelay"); + if (delay == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { + LOGGER.debug("Fault injection: delay is not configured"); + return nextNode.onFilter(invocation); + } + + return executeDelay(invocation, nextNode, delay); + } + + private CompletableFuture executeDelay(Invocation invocation, FilterNode nextNode, + long delay) { + Context currentContext = Vertx.currentContext(); + if (currentContext != null && currentContext.isEventLoopContext()) { + CompletableFuture result = new CompletableFuture<>(); + currentContext.owner().setTimer(delay, timeID -> nextNode.onFilter(invocation).whenComplete((r, e) -> { + if (e == null) { + result.complete(r); + } else { + result.completeExceptionally(e); + } + } + )); + return result; + } + + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + LOGGER.info("Interrupted exception is received"); + } + return nextNode.onFilter(invocation); + } + + private boolean shouldDelay(Invocation invocation) { + int delayPercent = FaultInjectionUtil.getFaultInjectionConfig(invocation, + "delay.percent"); + if (delayPercent == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) { + LOGGER.debug("Fault injection: delay percentage is not configured"); + return false; + } + + // check fault delay condition. + return FaultInjectionUtil.isFaultNeedToInject(delayPercent); + } +} 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 new file mode 100644 index 00000000000..600ed5dc9f6 --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfiguration.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.faultinjection; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnProperty(value = FaultInjectionConfiguration.FAULT_INJECTION_ENABLED, + havingValue = "true") +public class FaultInjectionConfiguration { + public static final String FAULT_INJECTION_PREFIX = "servicecomb.faultInjection"; + + public static final String FAULT_INJECTION_ENABLED = FAULT_INJECTION_PREFIX + ".enabled"; + + @Bean + public ConsumerAbortFaultFilter scbConsumerAbortFaultFilter() { + return new ConsumerAbortFaultFilter(); + } + + @Bean + public ConsumerDelayFaultFilter scbConsumerDelayFaultFilter() { + return new ConsumerDelayFaultFilter(); + } +} diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConst.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConst.java new file mode 100644 index 00000000000..be34df98400 --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConst.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.faultinjection; + +/** + * Handles the all constant values for fault injection. + */ +public class FaultInjectionConst { + + public static final int FAULT_INJECTION_DEFAULT_VALUE = -1; + + public static final int FAULT_INJECTION_ERROR = -1; + + public static final String CONSUMER_FAULTINJECTION = "servicecomb.governance.Consumer."; + + public static final String CONSUMER_FAULTINJECTION_GLOBAL = "servicecomb.governance.Consumer._global."; + + public static final String CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS = "policy.fault.protocols."; +} 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 new file mode 100755 index 00000000000..67c3ab86939 --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.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.faultinjection; + +import static org.apache.servicecomb.faultinjection.FaultInjectionConst.CONSUMER_FAULTINJECTION; +import static org.apache.servicecomb.faultinjection.FaultInjectionConst.CONSUMER_FAULTINJECTION_GLOBAL; +import static org.apache.servicecomb.faultinjection.FaultInjectionConst.CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS; +import static org.apache.servicecomb.faultinjection.FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE; + +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; +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; + +/** + * Handles the count for all request based key[transport + microservice qualified name]. + */ +public class FaultInjectionUtil { + private FaultInjectionUtil() { + } + + // key is config parameter + private static final Map configCenterValue = new ConcurrentHashMapEx<>(); + + /** + * Returns the map of config parameter key and values. + * @return value of config parameter map + */ + public static Map getConfigCenterMap() { + return configCenterValue; + } + + /** + * Sets the value for given config parameter. + * @param key + * @param value + */ + public static void setConfigCenterValue(String key, AtomicInteger value) { + configCenterValue.put(key, value); + } + + /** + * Handles the reading fault injection configuration. + * + * @param invocation + * invocation of request + * @param key + * configuration key + * @return configuration value + */ + public static int getFaultInjectionConfig(Invocation invocation, String key) { + int value = 0; + String config; + + // get the config base on priority. operationName-->schema-->service-->global + String operationName = invocation.getOperationName(); + String schema = invocation.getSchemaId(); + String serviceName = invocation.getMicroserviceName(); + + config = CONSUMER_FAULTINJECTION + serviceName + ".schemas." + schema + ".operations." + operationName + "." + + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + invocation.getTransport().getName() + "." + key; + + value = getConfigValue(config); + if ((value != FAULT_INJECTION_DEFAULT_VALUE)) { + return value; + } + + config = CONSUMER_FAULTINJECTION + serviceName + ".schemas." + schema + "." + + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + invocation.getTransport().getName() + "." + key; + + value = getConfigValue(config); + if ((value != FAULT_INJECTION_DEFAULT_VALUE)) { + return value; + } + + config = CONSUMER_FAULTINJECTION + serviceName + "." + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + + invocation.getTransport().getName() + "." + key; + value = getConfigValue(config); + if ((value != FAULT_INJECTION_DEFAULT_VALUE)) { + return value; + } + + config = CONSUMER_FAULTINJECTION_GLOBAL + CONSUMER_FAULTINJECTION_POLICY_PROTOCOLS + + invocation.getTransport().getName() + "." + key; + + value = getConfigValue(config); + return value; + } + + /** + * Get the configuration value + * @param config config parameter + * @return int value + */ + private static int getConfigValue(String config) { + int value = 0; + //first need to check in config center map which has high priority. + Map cfgMap = FaultInjectionUtil.getConfigCenterMap(); + + if (cfgMap.containsKey(config)) { + return cfgMap.get(config).get(); + } + + value = LegacyPropertyFactory.getIntProperty(config, FAULT_INJECTION_DEFAULT_VALUE); + return value; + } + + /** + * It will check the delay/abort condition based on percentage. + * + * @return true: delay/abort is needed. false: delay/abort is not needed. + */ + public static boolean isFaultNeedToInject(int percentage) { + if (percentage > 0) { + return ThreadLocalRandom.current().nextInt(100) < percentage; + } + return false; + } + + @VisibleForTesting + static Map getConfigCenterValue() { + return configCenterValue; + } +} diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultParam.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultParam.java new file mode 100644 index 00000000000..23530f13f58 --- /dev/null +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultParam.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.faultinjection; + +import io.vertx.core.Vertx; + +/** + * Fault injection parameters which decides the fault injection condition. + */ +public class FaultParam { + private Vertx vertx; + + FaultParam() { + + } + + public Vertx getVertx() { + return vertx; + } + + public void setVertx(Vertx vertx) { + this.vertx = vertx; + } +} 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 new file mode 100644 index 00000000000..d44fd849b2f --- /dev/null +++ b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestConsumerAbortFaultFilter.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.faultinjection; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import org.apache.servicecomb.core.Invocation; +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.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.junit.jupiter.api.AfterAll; +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 TestConsumerAbortFaultFilter { + private Invocation invocation; + + private Environment environment; + + @BeforeEach + public void before() { + 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); + Transport transport = Mockito.mock(Transport.class); + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName12"); + Mockito.when(invocation.getTransport()).thenReturn(transport); + Mockito.when(transport.getName()).thenReturn("rest"); + Mockito.when(invocation.getOperationName()).thenReturn("sayBye4"); + Mockito.when(invocation.getSchemaId()).thenReturn("testSchema4"); + Mockito.when(invocation.getMicroserviceName()).thenReturn("carts6"); + } + + @AfterEach + public void after() { + } + + @AfterAll + public static void classTeardown() { + VertxUtils.blockCloseVertxByName("faultinjectionTest"); + } + + @Test + public void injectFaultError() { + 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(); + + FilterNode filterNode = Mockito.mock(FilterNode.class); + + Holder resultHolder = new Holder<>(); + CompletableFuture result = abortFault.onFilter(invocation, filterNode); + result.whenComplete((r, e) -> { + if (e != null) { + resultHolder.value = (InvocationException) e; + } + }); + Assertions.assertThrows(ExecutionException.class, () -> result.get()); + Assertions.assertEquals(421, resultHolder.value.getStatusCode()); + Assertions.assertEquals("aborted by fault inject", resultHolder.value.getReasonPhrase()); + Assertions.assertEquals("CommonExceptionData [message=aborted by fault inject]", + resultHolder.value.getErrorData().toString()); + } + + @Test + public void injectFaultNoError() throws Exception { + ConsumerAbortFaultFilter abortFault = new ConsumerAbortFaultFilter(); + FilterNode filterNode = Mockito.mock(FilterNode.class); + Mockito.when(filterNode.onFilter(invocation)) + .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); + CompletableFuture result = abortFault.onFilter(invocation, filterNode); + Assertions.assertEquals("success", result.get().getResult()); + } + + @Test + public void injectFaultNoPercentageConfig() throws Exception { + ConsumerAbortFaultFilter abortFault = new ConsumerAbortFaultFilter(); + FilterNode filterNode = Mockito.mock(FilterNode.class); + Mockito.when(filterNode.onFilter(invocation)) + .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); + CompletableFuture result = abortFault.onFilter(invocation, filterNode); + Assertions.assertEquals("success", result.get().getResult()); + } + + @Test + public void injectFaultNoErrorCodeConfig() throws Exception { + ConsumerAbortFaultFilter abortFault = new ConsumerAbortFaultFilter(); + FilterNode filterNode = Mockito.mock(FilterNode.class); + Mockito.when(filterNode.onFilter(invocation)) + .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); + CompletableFuture result = abortFault.onFilter(invocation, filterNode); + Assertions.assertEquals("success", result.get().getResult()); + } +} 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 new file mode 100644 index 00000000000..381d3d01447 --- /dev/null +++ b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestConsumerDelayFaultFilter.java @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.core.filter.FilterNode; +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; +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 TestConsumerDelayFaultFilter { + private Invocation invocation; + + private Environment environment; + + @BeforeEach + public void before() { + 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); + Transport transport = Mockito.mock(Transport.class); + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName12"); + Mockito.when(invocation.getTransport()).thenReturn(transport); + Mockito.when(transport.getName()).thenReturn("rest"); + Mockito.when(invocation.getOperationName()).thenReturn("sayBye4"); + Mockito.when(invocation.getSchemaId()).thenReturn("testSchema4"); + Mockito.when(invocation.getMicroserviceName()).thenReturn("carts6"); + } + + @AfterEach + public void after() { + } + + @AfterAll + public static void classTeardown() { + VertxUtils.blockCloseVertxByName("faultinjectionTest"); + } + + @Test + public void injectFaultVertxDelay() throws Exception { + 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); + Mockito.when(filterNode.onFilter(invocation)) + .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); + CompletableFuture result = delayFault.onFilter(invocation, filterNode); + Assertions.assertEquals("success", result.get().getResult()); + } + + @Test + public void injectFaultSystemDelay() throws Exception { + 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); + Mockito.when(filterNode.onFilter(invocation)) + .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); + CompletableFuture result = delayFault.onFilter(invocation, filterNode); + Assertions.assertEquals("success", result.get().getResult()); + } + + @Test + public void injectFaultNotDelay() throws Exception { + 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); + Mockito.when(filterNode.onFilter(invocation)) + .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); + CompletableFuture result = delayFault.onFilter(invocation, filterNode); + Assertions.assertEquals("success", result.get().getResult()); + } + + @Test + public void injectFaultNoPercentageConfig() throws Exception { + ConsumerDelayFaultFilter delayFault = new ConsumerDelayFaultFilter(); + FilterNode filterNode = Mockito.mock(FilterNode.class); + Mockito.when(filterNode.onFilter(invocation)) + .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); + CompletableFuture result = delayFault.onFilter(invocation, filterNode); + Assertions.assertEquals("success", result.get().getResult()); + } + + @Test + public void injectFaultNoDelayMsConfig() throws Exception { + 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); + Mockito.when(filterNode.onFilter(invocation)) + .thenReturn(CompletableFuture.completedFuture(Response.succResp("success"))); + CompletableFuture result = delayFault.onFilter(invocation, filterNode); + Assertions.assertEquals("success", result.get().getResult()); + } +} diff --git a/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectUtil.java b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectUtil.java new file mode 100644 index 00000000000..451d7205eca --- /dev/null +++ b/handlers/handler-fault-injection/src/test/java/org/apache/servicecomb/faultinjection/TestFaultInjectUtil.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.faultinjection; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * Tests the fault inject util functionality. + */ +public class TestFaultInjectUtil { + @Test + public void testFaultInjectUtil() { + FaultInjectionUtil.setConfigCenterValue("sayHi", new AtomicInteger(123)); + int value = FaultInjectionUtil.getConfigCenterMap().get("sayHi").get(); + Assertions.assertEquals(123, value); + } +} diff --git a/handlers/handler-flowcontrol-qps/pom.xml b/handlers/handler-flowcontrol-qps/pom.xml index 30b85b42c20..eb5d63a0993 100644 --- a/handlers/handler-flowcontrol-qps/pom.xml +++ b/handlers/handler-flowcontrol-qps/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - handlers - 0.3.0-SNAPSHOT - - handler-flowcontrol-qps - - - io.servicecomb - java-chassis-core - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + 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 + handlers + 3.4.0-SNAPSHOT + + handler-flowcontrol-qps + Java Chassis::Handlers::Flow Control QPS + + + org.apache.servicecomb + java-chassis-core + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.servicecomb + foundation-test-scaffolding + test + + diff --git a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/Config.java b/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/Config.java deleted file mode 100644 index 5b291eedbd5..00000000000 --- a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/Config.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.qps; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.config.DynamicBooleanProperty; -import com.netflix.config.DynamicPropertyFactory; - -public final class Config { - private static final Logger LOGGER = LoggerFactory.getLogger(Config.class); - - public static final String CONSUMER_LIMIT_KEY_PREFIX = "cse.flowcontrol.Consumer.qps.limit."; - - public static final String PROVIDER_LIMIT_KEY_PREFIX = "cse.flowcontrol.Provider.qps.limit."; - - public static final String PROVIDER_LIMIT_KEY_GLOBAL = - "cse.flowcontrol.Provider.qps.global.limit"; - - public static final String CONSUMER_ENABLED = "cse.flowcontrol.Consumer.qps.enabled"; - - public static final String PROVIDER_ENABLED = "cse.flowcontrol.Provider.qps.enabled"; - - public static final Config INSTANCE = new Config(); - - private final DynamicBooleanProperty consumerEanbled = - DynamicPropertyFactory.getInstance().getBooleanProperty(CONSUMER_ENABLED, true); - - private final DynamicBooleanProperty providerEanbled = - DynamicPropertyFactory.getInstance().getBooleanProperty(PROVIDER_ENABLED, true); - - private Config() { - consumerEanbled.addCallback(() -> { - boolean newValue = consumerEanbled.get(); - LOGGER.info("{} changed from {} to {}", CONSUMER_ENABLED, consumerEanbled, newValue); - }); - - providerEanbled.addCallback(() -> { - boolean newValue = providerEanbled.get(); - LOGGER.info("{} changed from {} to {}", PROVIDER_ENABLED, providerEanbled, newValue); - }); - } - - public boolean isConsumerEnabled() { - return consumerEanbled.get(); - } - - public boolean isProviderEnabled() { - return providerEanbled.get(); - } -} diff --git a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/ConsumerQpsControllerManager.java b/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/ConsumerQpsControllerManager.java deleted file mode 100644 index 2d5a1961484..00000000000 --- a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/ConsumerQpsControllerManager.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.qps; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.config.DynamicProperty; - -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.foundation.common.AbstractObjectManager; - -/** - * 以microservice.schema.operation为key - * 如果配置只到microservice级别,那么该microservice中所有的key都关联到同一个qpsController实例 - * 然后schema、operation级别有独立配置的,单独关联自己的qpsController实例 - * - * schema级独立的qpsController统计时,并不会导致microservice级别的统计也改变,operation级别规则也相同 - * 即:统计只在qpsController实例内部生效,不会产生实例间的关联 - * - */ -public class ConsumerQpsControllerManager extends AbstractObjectManager { - private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerQpsControllerManager.class); - // 最终使用的QpsController实例,实际都是从下面的map中引用出来的,不会独立创建 - - // 3种类型的key都保存在这里,不存在冲突 - // microservice - // microservice.schema - // microservice.schema.operation - private Map qpsControllerMap = new ConcurrentHashMap<>(); - - // 避免重复watch - // 只会在create流程中调用,是有锁保护的,不必考虑多线程并发 - private Set watchedKeySet = new HashSet<>(); - - @Override - protected String getKey(OperationMeta operationMeta) { - return operationMeta.getMicroserviceQualifiedName(); - } - - private QpsController initQpsLimit(String key, Integer qpsLimit) { - if (qpsLimit == null) { - return null; - } - - LOGGER.info("qpsLimit of {} init as {}", key, qpsLimit); - - QpsController qpsController = new QpsController(key, qpsLimit); - qpsControllerMap.put(key, qpsController); - return qpsController; - } - - private QpsController updateQpsLimit(String key, Integer qpsLimit) { - QpsController qpsController = qpsControllerMap.get(key); - if (qpsController == null && qpsLimit != null) { - qpsController = new QpsController(key, qpsLimit); - qpsControllerMap.put(key, qpsController); - } - - if (qpsController != null) { - LOGGER.info("qpsLimit of {} changed from {} to {}", key, qpsController.getQpsLimit(), qpsLimit); - - qpsController.setQpsLimit(qpsLimit); - } - - return qpsController; - } - - private QpsController findReference(String key) { - QpsController qpsController = qpsControllerMap.get(key); - if (qpsController == null) { - return null; - } - - if (qpsController.getQpsLimit() == null) { - return null; - } - - return qpsController; - } - - private QpsController findReference(OperationMeta operationMeta) { - QpsController qpsController = findReference(operationMeta.getMicroserviceQualifiedName()); - if (qpsController != null) { - return qpsController; - } - - qpsController = findReference(operationMeta.getSchemaMeta().getMicroserviceQualifiedName()); - if (qpsController != null) { - return qpsController; - } - - qpsController = findReference(operationMeta.getMicroserviceName()); - if (qpsController != null) { - return qpsController; - } - - return initQpsLimit(operationMeta.getMicroserviceName(), Integer.MAX_VALUE); - } - - @Override - protected QpsController create(OperationMeta operationMeta) { - // create在父类中是加了锁的,不存在并发的场景 - initConfig(operationMeta, operationMeta.getMicroserviceQualifiedName()); - initConfig(operationMeta, operationMeta.getSchemaMeta().getMicroserviceQualifiedName()); - initConfig(operationMeta, operationMeta.getMicroserviceName()); - - return findReference(operationMeta); - } - - private void initConfig(OperationMeta operationMeta, String key) { - if (watchedKeySet.contains(key)) { - return; - } - - watchedKeySet.add(key); - - String configKey = Config.CONSUMER_LIMIT_KEY_PREFIX + key; - DynamicProperty property = DynamicProperty.getInstance(configKey); - initQpsLimit(key, getIntegerLimitProperty(property)); - - property.addCallback(() -> { - updateQpsLimit(key, getIntegerLimitProperty(property)); - QpsController qpsController = findReference(operationMeta); - - objMap.put(operationMeta.getMicroserviceQualifiedName(), qpsController); - }); - } - - private Integer getIntegerLimitProperty(DynamicProperty property) { - try { - return property.getInteger(); - } catch (IllegalArgumentException e) { - LOGGER.error(e.getMessage()); - return null; - } - } -} diff --git a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/ConsumerQpsFlowControlHandler.java b/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/ConsumerQpsFlowControlHandler.java deleted file mode 100644 index 47d5483f1cb..00000000000 --- a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/ConsumerQpsFlowControlHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.qps; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.handler.impl.AbstractHandler; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -/** - * consumer端针对调用目标的qps控制 - * 支持microservice、schema、operation三个级别的控制 - * - */ -public class ConsumerQpsFlowControlHandler extends AbstractHandler { - private ConsumerQpsControllerManager qpsControllerMgr = new ConsumerQpsControllerManager(); - - @Override - public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception { - if (!Config.INSTANCE.isConsumerEnabled()) { - invocation.next(asyncResp); - return; - } - - OperationMeta operationMeta = invocation.getOperationMeta(); - QpsController qpsController = qpsControllerMgr.getOrCreate(operationMeta); - if (qpsController.isLimitNewRequest()) { - // 429 - CommonExceptionData errorData = new CommonExceptionData("rejected by qps flowcontrol"); - asyncResp.consumerFail( - new InvocationException(QpsConst.TOO_MANY_REQUESTS_STATUS, errorData)); - return; - } - - invocation.next(asyncResp); - } -} diff --git a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/ProviderQpsControllerManager.java b/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/ProviderQpsControllerManager.java deleted file mode 100644 index 1d386d2eb17..00000000000 --- a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/ProviderQpsControllerManager.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.qps; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.config.DynamicProperty; - -import io.servicecomb.foundation.common.AbstractObjectManager; - -public class ProviderQpsControllerManager - extends AbstractObjectManager { - private static final Logger LOGGER = LoggerFactory.getLogger(ProviderQpsControllerManager.class); - - private Map qpsControllerMap = new ConcurrentHashMap<>(); - - // 避免重复watch - // 只会在create流程中调用,是有锁保护的,不必考虑多线程并发 - private Set watchedKeySet = new HashSet<>(); - - private QpsController globalQpsController = null; - - @Override - protected String getKey(String microServiceName) { - return microServiceName; - } - - @Override - public QpsController getOrCreate(String keyOwner) { - if (keyOwner == null) { - if (globalQpsController == null) { - synchronized (lockObj) { - if (globalQpsController == null) { - DynamicProperty property = - DynamicProperty.getInstance(Config.PROVIDER_LIMIT_KEY_GLOBAL); - globalQpsController = new QpsController(keyOwner, getIntegerLimitProperty(property)); - property.addCallback(() -> { - globalQpsController.setQpsLimit(getIntegerLimitProperty(property)); - }); - } - } - } - return globalQpsController; - } else { - return super.getOrCreate(keyOwner); - } - } - - private QpsController initQpsLimit(String key, Integer qpsLimit) { - if (qpsLimit == null) { - qpsLimit = null; - } - - LOGGER.info("qpsLimit of {} init as {}", key, qpsLimit); - - QpsController qpsController = new QpsController(key, qpsLimit); - qpsControllerMap.put(key, qpsController); - return qpsController; - } - - private QpsController updateQpsLimit(String key, Integer qpsLimit) { - QpsController qpsController = qpsControllerMap.get(key); - if (qpsController == null && qpsLimit != null) { - qpsController = new QpsController(key, qpsLimit); - qpsControllerMap.put(key, qpsController); - } - - if (qpsController != null) { - LOGGER.info("qpsLimit of {} changed from {} to {}", - key, - qpsController.getQpsLimit(), - qpsLimit); - - qpsController.setQpsLimit(qpsLimit); - } - - return qpsController; - } - - private QpsController findReference(String key) { - QpsController qpsController = qpsControllerMap.get(key); - if (qpsController == null) { - return initQpsLimit(key, Integer.MAX_VALUE); - } - return qpsController; - } - - @Override - protected QpsController create(String microServiceName) { - // create在父类中是加了锁的,不存在并发的场景 - initConfig(microServiceName); - - return findReference(microServiceName); - } - - private void initConfig(String key) { - if (watchedKeySet.contains(key)) { - return; - } - - watchedKeySet.add(key); - - String configKey = Config.PROVIDER_LIMIT_KEY_PREFIX + key; - DynamicProperty property = DynamicProperty.getInstance(configKey); - initQpsLimit(key, getIntegerLimitProperty(property)); - - property.addCallback(() -> { - updateQpsLimit(key, getIntegerLimitProperty(property)); - QpsController qpsController = findReference(key); - - objMap.put(key, qpsController); - }); - } - - private Integer getIntegerLimitProperty(DynamicProperty property) { - try { - return property.getInteger(); - } catch (IllegalArgumentException e) { - LOGGER.error(e.getMessage()); - return null; - } - } -} diff --git a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/ProviderQpsFlowControlHandler.java b/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/ProviderQpsFlowControlHandler.java deleted file mode 100644 index fed4994c3b1..00000000000 --- a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/ProviderQpsFlowControlHandler.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.qps; - -import io.servicecomb.core.Const; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.handler.impl.AbstractHandler; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -public class ProviderQpsFlowControlHandler extends AbstractHandler { - private ProviderQpsControllerManager qpsControllerMgr = new ProviderQpsControllerManager(); - - @Override - public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception { - if (!Config.INSTANCE.isProviderEnabled()) { - invocation.next(asyncResp); - return; - } - - String microServiceName = (String) invocation.getContext(Const.SRC_MICROSERVICE); - if (microServiceName != null && !microServiceName.isEmpty()) { - QpsController qpsController = qpsControllerMgr.getOrCreate(microServiceName); - if (isLimitNewRequest(qpsController, asyncResp)) { - return; - } - } - - QpsController globalQpsController = qpsControllerMgr.getOrCreate(null); - if (isLimitNewRequest(globalQpsController, asyncResp)) { - return; - } - - invocation.next(asyncResp); - } - - private boolean isLimitNewRequest(QpsController qpsController, AsyncResponse asyncResp) { - if (qpsController.isLimitNewRequest()) { - CommonExceptionData errorData = new CommonExceptionData("rejected by qps flowcontrol"); - asyncResp.producerFail(new InvocationException(QpsConst.TOO_MANY_REQUESTS_STATUS, errorData)); - return true; - } else { - return false; - } - } -} diff --git a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/QpsConst.java b/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/QpsConst.java deleted file mode 100644 index af46452d1e0..00000000000 --- a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/QpsConst.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.qps; - -import javax.ws.rs.core.Response.StatusType; - -import io.servicecomb.swagger.invocation.context.HttpStatus; - -public final class QpsConst { - public static final StatusType TOO_MANY_REQUESTS_STATUS = new HttpStatus(429, "Too Many Requests"); - - private QpsConst() { - } -} diff --git a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/QpsController.java b/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/QpsController.java deleted file mode 100644 index 3c93ca2e4b0..00000000000 --- a/handlers/handler-flowcontrol-qps/src/main/java/io/servicecomb/qps/QpsController.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.qps; - -import java.util.concurrent.atomic.AtomicLong; - -public class QpsController { - private String key; - - private Integer qpsLimit; - - // 本周期的开始时间 - private volatile long msCycleBegin; - - // 到目前为止的请求数 - private AtomicLong requestCount = new AtomicLong(); - - // 本周期之前的请求数 - private volatile long lastRequestCount = 0; - - private static final int CYCLE_LENGTH = 1000; - - public QpsController(String key, Integer qpsLimit) { - this.key = key; - this.qpsLimit = qpsLimit; - this.msCycleBegin = System.currentTimeMillis(); - } - - public String getKey() { - return key; - } - - public Integer getQpsLimit() { - return qpsLimit; - } - - public void setQpsLimit(Integer qpsLimit) { - this.qpsLimit = qpsLimit; - } - - // 返回true,表示需要被控制 - public boolean isLimitNewRequest() { - long newCount = requestCount.incrementAndGet(); - long msNow = System.currentTimeMillis(); - if (msNow - msCycleBegin > CYCLE_LENGTH) { - // 新周期 - // 会有多线程竞争,互相覆盖的问题,不过无所谓,不会有什么后果 - lastRequestCount = newCount; - msCycleBegin = msNow; - } - - // 配置更新与配置使用是多线程并发的 - // 所以可能operation级别刚刚更新为null - // 还没来得及修改为引用schema级别或是microservice级别,其他线程还在使用,所以需要规避 - int limitValue = (qpsLimit == null) ? Integer.MAX_VALUE : qpsLimit; - return newCount - lastRequestCount >= limitValue; - } -} 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 new file mode 100644 index 00000000000..8325352d726 --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/Config.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.qps; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; + +public final class Config { + public static final String CONFIG_PREFIX = "servicecomb.flowcontrol."; + + public static final String STRATEGY_KEY = "servicecomb.flowcontrol.strategy"; + + public static final String ANY_SERVICE = "ANY"; + + public static final String CONSUMER_BUCKET_KEY_PREFIX = "servicecomb.flowcontrol.Consumer.qps.bucket."; + + public static final String PROVIDER_BUCKET_KEY_PREFIX = "servicecomb.flowcontrol.Provider.qps.bucket."; + + public static final String PROVIDER_BUCKET_KEY_GLOBAL = + "servicecomb.flowcontrol.Provider.qps.global.bucket"; + + public static final String CONSUMER_BUCKET_KEY_GLOBAL = + "servicecomb.flowcontrol.Consumer.qps.global.bucket"; + + public static final String CONSUMER_LIMIT_KEY_PREFIX = "servicecomb.flowcontrol.Consumer.qps.limit."; + + public static final String PROVIDER_LIMIT_KEY_PREFIX = "servicecomb.flowcontrol.Provider.qps.limit."; + + public static final String PROVIDER_LIMIT_KEY_GLOBAL = + "servicecomb.flowcontrol.Provider.qps.global.limit"; + + public static final String CONSUMER_LIMIT_KEY_GLOBAL = + "servicecomb.flowcontrol.Consumer.qps.global.limit"; + + public static final String CONSUMER_ENABLED = "servicecomb.flowcontrol.Consumer.qps.enabled"; + + public static final String PROVIDER_ENABLED = "servicecomb.flowcontrol.Provider.qps.enabled"; + + public static Config INSTANCE = new Config(); + + public Config() { + + } + + public boolean isConsumerEnabled() { + return LegacyPropertyFactory.getBooleanProperty(CONSUMER_ENABLED, true); + } + + public boolean isProviderEnabled() { + 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 new file mode 100644 index 00000000000..72828b35ccf --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerFlowControlFilter.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.qps; + +import java.util.concurrent.CompletableFuture; + +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.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 extends AbstractFilter implements ConsumerFilter, EdgeFilter { + private final QpsControllerManager qpsControllerMgr; + + public ConsumerFlowControlFilter(Environment environment) { + qpsControllerMgr = new QpsControllerManager(false, environment); + } + + @VisibleForTesting + public QpsControllerManager getQpsControllerMgr() { + return qpsControllerMgr; + } + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1990; + } + + @Override + public String getName() { + return "consumer-flow-control"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + if (!Config.INSTANCE.isConsumerEnabled()) { + return nextNode.onFilter(invocation); + } + + QpsStrategy qpsStrategy = qpsControllerMgr.getOrCreate(invocation.getMicroserviceName(), invocation); + if (qpsStrategy.isLimitNewRequest()) { + CommonExceptionData errorData = new CommonExceptionData( + "consumer request rejected by flow control."); + return CompletableFuture.failedFuture(new InvocationException(QpsConst.TOO_MANY_REQUESTS_STATUS, errorData)); + } + + return nextNode.onFilter(invocation); + } +} 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 new file mode 100644 index 00000000000..d5fd7702cd1 --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/FlowControlQpsConfiguration.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.qps; + +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") +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 scbProviderFlowControlFilter(Environment environment) { + return new ProviderFlowControlFilter(environment); + } + + @Bean + 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 new file mode 100644 index 00000000000..00f78f126f1 --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderFlowControlFilter.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.qps; + +import java.util.concurrent.CompletableFuture; + +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.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 extends AbstractFilter implements ProviderFilter { + private final QpsControllerManager qpsControllerMgr; + + public ProviderFlowControlFilter(Environment environment) { + qpsControllerMgr = new QpsControllerManager(true, environment); + } + + @Override + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1990; + } + + @Override + public String getName() { + return "provider-flow-control"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + if (!Config.INSTANCE.isProviderEnabled()) { + return nextNode.onFilter(invocation); + } + + String microserviceName = invocation.getContext(CoreConst.SRC_MICROSERVICE); + QpsStrategy qpsStrategy = qpsControllerMgr.getOrCreate(microserviceName, invocation); + if (qpsStrategy.isLimitNewRequest()) { + CommonExceptionData errorData = new CommonExceptionData( + "provider request rejected by flow control."); + return CompletableFuture.failedFuture(new InvocationException(QpsConst.TOO_MANY_REQUESTS_STATUS, errorData)); + } + return nextNode.onFilter(invocation); + } + + @VisibleForTesting + public QpsControllerManager getQpsControllerMgr() { + return qpsControllerMgr; + } +} 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 new file mode 100644 index 00000000000..ab1713ec497 --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsConst.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.qps; + +import jakarta.ws.rs.core.Response.StatusType; + +import org.apache.servicecomb.swagger.invocation.context.HttpStatus; + +public final class QpsConst { + public static final StatusType TOO_MANY_REQUESTS_STATUS = new HttpStatus(429, "Too Many Requests"); + + private QpsConst() { + } +} 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 new file mode 100644 index 00000000000..e4cf5e18457 --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsControllerManager.java @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.List; +import java.util.Map; + +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.google.common.eventbus.Subscribe; + +public final class QpsControllerManager { + private static final Logger LOGGER = LoggerFactory.getLogger(QpsControllerManager.class); + + public static final String SEPARATOR = "."; + + /** + * Describe the relationship between configuration and qpsController. + */ + private final Map configQpsControllerMap = new ConcurrentHashMapEx<>(); + + /** + * Describe the relationship between qualifiedKey(format is "microservice.schema.operation") and qpsController. + */ + private final Map qualifiedNameControllerMap = new ConcurrentHashMapEx<>(); + + private AbstractQpsStrategy globalQpsStrategy; + + private final String limitKeyPrefix; + + private final String bucketKeyPrefix; + + private final String globalLimitKey; + + private final String globalBucketKey; + + 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; + globalLimitKey = Config.PROVIDER_LIMIT_KEY_GLOBAL; + globalBucketKey = Config.PROVIDER_BUCKET_KEY_GLOBAL; + } else { + limitKeyPrefix = Config.CONSUMER_LIMIT_KEY_PREFIX; + bucketKeyPrefix = Config.CONSUMER_BUCKET_KEY_PREFIX; + globalLimitKey = Config.CONSUMER_LIMIT_KEY_GLOBAL; + globalBucketKey = Config.CONSUMER_BUCKET_KEY_GLOBAL; + } + + 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 + public Map getQualifiedNameControllerMap() { + return qualifiedNameControllerMap; + } + + public QpsStrategy getOrCreate(String microserviceName, Invocation invocation) { + final String name = validatedName(microserviceName); + return qualifiedNameControllerMap + .computeIfAbsent( + name + SEPARATOR + invocation.getOperationMeta().getSchemaQualifiedName(), + key -> create(key, name, invocation)); + } + + private String validatedName(String microserviceName) { + String name = microserviceName; + if (StringUtils.isEmpty(microserviceName)) { + name = Config.ANY_SERVICE; + } + return name; + } + + /** + * Create relevant qpsLimit dynamicProperty and watch the configuration change. + * Search and return a valid qpsController. + */ + @VisibleForTesting + AbstractQpsStrategy create(String qualifiedNameKey, String microserviceName, + Invocation invocation) { + createForService(qualifiedNameKey, microserviceName, invocation); + String qualifiedAnyServiceName = Config.ANY_SERVICE + qualifiedNameKey.substring(microserviceName.length()); + createForService(qualifiedAnyServiceName, Config.ANY_SERVICE, invocation); + + AbstractQpsStrategy strategy = searchQpsController(qualifiedNameKey); + if (strategy == null) { + strategy = searchQpsController(qualifiedAnyServiceName); + } + if (strategy == null) { + return globalQpsStrategy; + } + return strategy; + } + + private void createForService(String qualifiedNameKey, String microserviceName, + Invocation invocation) { + // create "microservice" + createQpsControllerIfNotExist(microserviceName); + // create "microservice.schema" + createQpsControllerIfNotExist( + qualifiedNameKey.substring(0, microserviceName.length() + invocation.getSchemaId().length() + 1)); + // create "microservice.schema.operation" + createQpsControllerIfNotExist(qualifiedNameKey); + } + + /** + *

Use qualifiedNameKey to search {@link QpsStrategy}. + * Firstly try to search "microservice.schema.operation". If no valid result found, then try "microservice.schema", + * and then "microservice" or global qpsController(If there is a global qpsController).

+ *

This method ensures that there is always an existing qpsController returned, as the relevant qpsController has + * been created and stored in {@link #create(String, String, Invocation)}

+ * + * @param qualifiedNameKey qualifiedNameKey in {@link #qualifiedNameControllerMap} + * @return a qps controller, lower level controllers with valid qpsLimit have priority. + */ + private AbstractQpsStrategy searchQpsController(String qualifiedNameKey) { + AbstractQpsStrategy qpsStrategy = configQpsControllerMap.get(qualifiedNameKey); + if (isValidQpsController(qpsStrategy)) { + return qpsStrategy; + } + + int index = qualifiedNameKey.lastIndexOf(SEPARATOR); + while (index > 0) { + qpsStrategy = configQpsControllerMap.get(qualifiedNameKey.substring(0, index)); + if (isValidQpsController(qpsStrategy)) { + return qpsStrategy; + } + + index = qualifiedNameKey.lastIndexOf(SEPARATOR, index - 1); + } + + if (isValidQpsController(qpsStrategy)) { + return qpsStrategy; + } + + return null; + } + + private boolean isValidQpsController(AbstractQpsStrategy qpsStrategy) { + return null != qpsStrategy && null != qpsStrategy.getQpsLimit(); + } + + private void createQpsControllerIfNotExist(String configKey) { + if (configQpsControllerMap.containsKey(configKey)) { + return; + } + + LOGGER.info("Create qpsController, configKey = [{}]", configKey); + 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); + } + + private void initGlobalQpsController() { + 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, + String strategyName) { + if (StringUtils.isEmpty(strategyName)) { + strategyName = "FixedWindow"; + } + AbstractQpsStrategy strategy = null; + List strategyFactories = SPIServiceUtils + .getOrLoadSortedService(IStrategyFactory.class); + for (IStrategyFactory strategyFactory : strategyFactories) { + strategy = strategyFactory.createStrategy(strategyName); + if (strategy != null) { + break; + } + } + if (strategy == null) { + throw new ServiceCombException( + "the qps strategy name " + strategyName + " is not exist , please check."); + } + strategy.setKey(configKey); + strategy.setQpsLimit(limit); + strategy.setBucketLimit(bucket); + return strategy; + } +} diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsStrategy.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsStrategy.java new file mode 100644 index 00000000000..8a712e3e07d --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsStrategy.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.qps; + +public interface QpsStrategy { + + boolean isLimitNewRequest(); + + String name(); +} diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/AbstractQpsStrategy.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/AbstractQpsStrategy.java new file mode 100644 index 00000000000..65d36aa3444 --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/AbstractQpsStrategy.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.qps.strategy; + +import org.apache.servicecomb.qps.QpsStrategy; + + +public abstract class AbstractQpsStrategy implements QpsStrategy { + + private Long qpsLimit; + + private Long bucketLimit; + + private String key; + + public Long getBucketLimit() { + return bucketLimit; + } + + public void setBucketLimit(Long bucketLimit) { + this.bucketLimit = bucketLimit; + } + + @Override + public abstract boolean isLimitNewRequest(); + + @Override + public abstract String name(); + + public void setQpsLimit(Long qpsLimit) { + this.qpsLimit = qpsLimit; + } + + public Long getQpsLimit() { + return qpsLimit; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } +} diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/DefaultStrategyFactory.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/DefaultStrategyFactory.java new file mode 100644 index 00000000000..79037f791a4 --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/DefaultStrategyFactory.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.qps.strategy; + +public class DefaultStrategyFactory implements IStrategyFactory { + + public AbstractQpsStrategy createStrategy(String strategyName) { + switch (strategyName) { + case "TokenBucket": + return new TokenBucketStrategy(); + case "LeakyBucket": + return new LeakyBucketStrategy(); + case "FixedWindow": + return new FixedWindowStrategy(); + default: + return null; + } + } +} 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 new file mode 100644 index 00000000000..c95c1ab2cba --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/FixedWindowStrategy.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.qps.strategy; + +import java.util.concurrent.atomic.AtomicLong; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FixedWindowStrategy extends AbstractQpsStrategy { + + private static final Logger LOGGER = LoggerFactory.getLogger(FixedWindowStrategy.class); + + // Interval begin time + private volatile long msCycleBegin; + + // Request count between Interval begin and now in one interval + private final AtomicLong requestCount = new AtomicLong(); + + // request count before an interval + private volatile long lastRequestCount = 1; + + private static final int CYCLE_LENGTH = 1000; + + private static final String STRATEGY_NAME = "FixedWindow"; + + // return true means new request need to be rejected + public boolean isLimitNewRequest() { + if (this.getQpsLimit() == null) { + throw new IllegalStateException("should not happen"); + } + long newCount = requestCount.incrementAndGet(); + long msNow = System.currentTimeMillis(); + //Time jump cause the new request injected + if (msNow - msCycleBegin > CYCLE_LENGTH || msNow < msCycleBegin) { + //no need worry about concurrency problem + lastRequestCount = newCount; + msCycleBegin = msNow; + } + + // 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){ + LOGGER.warn("qps flowcontrol open, qpsLimit is {} and tps is {}", this.getQpsLimit(), newCount - lastRequestCount + 1); + } + return isLimitRequest; + } + + @Override + public String name() { + return STRATEGY_NAME; + } +} diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/IStrategyFactory.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/IStrategyFactory.java new file mode 100644 index 00000000000..bebe8ce90b0 --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/IStrategyFactory.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.qps.strategy; + +public interface IStrategyFactory { + + AbstractQpsStrategy createStrategy(String strategyName); +} diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/LeakyBucketStrategy.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/LeakyBucketStrategy.java new file mode 100644 index 00000000000..b8bf65c5e7e --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/LeakyBucketStrategy.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.strategy; + +import java.util.concurrent.atomic.AtomicLong; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * leaky bucket algorithm include 2 implementation : + * 1. as a meter : it's same as the token bucket. + * 2. as a queue : the bucket size equal to qpsLimit. + * + **/ +public class LeakyBucketStrategy extends AbstractQpsStrategy { + + private static final Logger LOGGER = LoggerFactory.getLogger(LeakyBucketStrategy.class); + + // Request count between Interval begin and now in one interval + private final AtomicLong requestCount = new AtomicLong(); + + private volatile long lastTime; + + private long remainder = 0; + + private static final String STRATEGY_NAME = "LeakyBucket"; + + @Override + public boolean isLimitNewRequest() { + if (this.getQpsLimit() == null) { + throw new IllegalStateException("should not happen"); + } + if (this.getBucketLimit() == null) { + this.setBucketLimit(Math.min(2 * this.getQpsLimit(), Integer.MAX_VALUE)); + } + long nowTime = System.currentTimeMillis(); + //get the num of te period time + long leakCount = ((nowTime - lastTime + remainder) / 1000) * this.getQpsLimit(); + remainder = (nowTime - lastTime + remainder) % 1000; + // leak the request + if (requestCount.longValue() > leakCount) { + requestCount.addAndGet(-leakCount); + } else { + requestCount.set(0); + } + lastTime = nowTime; + //compute this time + if (requestCount.longValue() < this.getBucketLimit()) { + requestCount.incrementAndGet(); + return false; + } + LOGGER.warn("qps flowcontrol open, qpsLimit is {} and tps is {}", this.getQpsLimit(), requestCount.longValue() + 1); + return true; + } + + @Override + public String name() { + return STRATEGY_NAME; + } +} diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/TokenBucketStrategy.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/TokenBucketStrategy.java new file mode 100644 index 00000000000..082906ffa87 --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/TokenBucketStrategy.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.qps.strategy; + +public class TokenBucketStrategy extends LeakyBucketStrategy { + + private static final String STRATEGY_NAME = "TokenBucket"; + + @Override + public String name() { + return STRATEGY_NAME; + } +} diff --git a/handlers/handler-flowcontrol-qps/src/main/resources/META-INF/services/org.apache.servicecomb.qps.strategy.IStrategyFactory b/handlers/handler-flowcontrol-qps/src/main/resources/META-INF/services/org.apache.servicecomb.qps.strategy.IStrategyFactory new file mode 100644 index 00000000000..32f53faffb7 --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/main/resources/META-INF/services/org.apache.servicecomb.qps.strategy.IStrategyFactory @@ -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.strategy.DefaultStrategyFactory 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/main/resources/config/cse.handler.xml b/handlers/handler-flowcontrol-qps/src/main/resources/config/cse.handler.xml deleted file mode 100644 index 877ef3fe9cd..00000000000 --- a/handlers/handler-flowcontrol-qps/src/main/resources/config/cse.handler.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestConfig.java b/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestConfig.java deleted file mode 100644 index 5815180e08d..00000000000 --- a/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestConfig.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.qps; - -import org.junit.Assert; -import org.junit.Test; - -/** - * - * - */ -public class TestConfig { - @Test - public void testEnabled() { - Assert.assertEquals(true, Config.INSTANCE.isProviderEnabled()); - Utils.updateProperty(Config.PROVIDER_ENABLED, false); - Assert.assertEquals(false, Config.INSTANCE.isProviderEnabled()); - - Utils.updateProperty(Config.PROVIDER_ENABLED, null); - Assert.assertEquals(true, Config.INSTANCE.isProviderEnabled()); - - Assert.assertEquals(true, Config.INSTANCE.isConsumerEnabled()); - Utils.updateProperty(Config.CONSUMER_ENABLED, false); - Assert.assertEquals(false, Config.INSTANCE.isConsumerEnabled()); - - Utils.updateProperty(Config.CONSUMER_ENABLED, null); - Assert.assertEquals(true, Config.INSTANCE.isConsumerEnabled()); - } -} diff --git a/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestConsumerQpsControllermanager.java b/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestConsumerQpsControllermanager.java deleted file mode 100644 index 189bd9d485d..00000000000 --- a/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestConsumerQpsControllermanager.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.qps; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import mockit.Expectations; -import mockit.Mocked; - -/** - * - * - */ -public class TestConsumerQpsControllermanager { - private static String microserviceName = "pojo"; - - private static String schemaQualified = microserviceName + ".server"; - - private static String operationQualified = schemaQualified + ".test"; - - @Test - public void testQpsLimit(@Mocked SchemaMeta schemaMeta, @Mocked OperationMeta operationMeta) { - new Expectations() { - { - operationMeta.getMicroserviceQualifiedName(); - result = operationQualified; - - schemaMeta.getMicroserviceQualifiedName(); - result = schemaQualified; - - operationMeta.getMicroserviceName(); - result = microserviceName; - } - }; - - ConsumerQpsControllerManager mgr = new ConsumerQpsControllerManager(); - QpsController qpsController = mgr.getOrCreate(operationMeta); - Assert.assertEquals((Integer) Integer.MAX_VALUE, qpsController.getQpsLimit()); - Assert.assertEquals(microserviceName, qpsController.getKey()); - - doTestQpsLimit(mgr, operationMeta, microserviceName, 100, microserviceName, 100); - doTestQpsLimit(mgr, operationMeta, schemaQualified, 200, schemaQualified, 200); - doTestQpsLimit(mgr, operationMeta, operationQualified, 300, operationQualified, 300); - doTestQpsLimit(mgr, operationMeta, operationQualified, null, schemaQualified, 200); - doTestQpsLimit(mgr, operationMeta, schemaQualified, null, microserviceName, 100); - doTestQpsLimit(mgr, operationMeta, microserviceName, null, microserviceName, Integer.MAX_VALUE); - } - - private void doTestQpsLimit(ConsumerQpsControllerManager mgr, OperationMeta operationMeta, String key, - Integer newValue, - String expectKey, Integer expectValue) { - Utils.updateProperty(Config.CONSUMER_LIMIT_KEY_PREFIX + key, newValue); - QpsController qpsController = mgr.getOrCreate(operationMeta); - Assert.assertEquals(expectValue, qpsController.getQpsLimit()); - Assert.assertEquals(expectKey, qpsController.getKey()); - } -} diff --git a/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestConsumerQpsFlowControlHandler.java b/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestConsumerQpsFlowControlHandler.java deleted file mode 100644 index 1e79240c961..00000000000 --- a/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestConsumerQpsFlowControlHandler.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.qps; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.swagger.invocation.AsyncResponse; -import mockit.Mock; -import mockit.MockUp; - -/** - * - * - */ -public class TestConsumerQpsFlowControlHandler { - - ConsumerQpsFlowControlHandler handler = new ConsumerQpsFlowControlHandler(); - - Invocation invocation = Mockito.mock(Invocation.class); - - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - - @Test - public void testQpsController() throws Exception { - QpsController qpsController = new QpsController("abc", 100); - Assert.assertEquals(false, qpsController.isLimitNewRequest()); - - qpsController.setQpsLimit(1); - Assert.assertEquals(true, qpsController.isLimitNewRequest()); - } - - @Test - public void testHandleWithException() { - boolean validAssert; - try { - validAssert = true; - - handler.handle(invocation, asyncResp); - } catch (Exception e) { - validAssert = false; - } - Assert.assertFalse(validAssert); - } - - @Test - public void testHandle() { - boolean validAssert; - try { - validAssert = true; - Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - Mockito.when(operationMeta.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName"); - - new MockUp() { - @Mock - public boolean isLimitNewRequest() { - return true; - } - }; - - new MockUp() { - - @Mock - protected QpsController create(OperationMeta operationMeta) { - return new QpsController("key", 12); - } - }; - handler.handle(invocation, asyncResp); - } catch (Exception e) { - validAssert = false; - } - Assert.assertTrue(validAssert); - } - - @Test - public void testHandleIsLimitNewRequestAsFalse() { - boolean validAssert; - try { - validAssert = true; - Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - Mockito.when(operationMeta.getMicroserviceQualifiedName()).thenReturn("MicroserviceQualifiedName"); - - new MockUp() { - @Mock - public boolean isLimitNewRequest() { - return false; - } - }; - - new MockUp() { - - @Mock - protected QpsController create(OperationMeta operationMeta) { - return new QpsController("key", 12); - } - }; - handler.handle(invocation, asyncResp); - } catch (Exception e) { - validAssert = false; - } - Assert.assertTrue(validAssert); - } -} diff --git a/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestProviderQpsControllermanager.java b/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestProviderQpsControllermanager.java deleted file mode 100644 index a719f6d16e4..00000000000 --- a/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestProviderQpsControllermanager.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.qps; - -import org.junit.Assert; -import org.junit.Test; - -public class TestProviderQpsControllermanager { - private static String microserviceName = "pojo"; - - @Test - public void testQpsLimit() { - ProviderQpsControllerManager mgr = new ProviderQpsControllerManager(); - QpsController qpsController = mgr.getOrCreate(microserviceName); - Assert.assertEquals(null, qpsController.getQpsLimit()); - Assert.assertEquals(microserviceName, qpsController.getKey()); - - doTestQpsLimit(mgr, microserviceName, 100, microserviceName, 100); - doTestQpsLimit(mgr, microserviceName, null, microserviceName, null); - } - - private void doTestQpsLimit(ProviderQpsControllerManager mgr, String key, Integer newValue, - String expectKey, Integer expectValue) { - Utils.updateProperty(Config.PROVIDER_LIMIT_KEY_PREFIX + key, newValue); - QpsController qpsController = mgr.getOrCreate(key); - Assert.assertEquals(expectValue, qpsController.getQpsLimit()); - Assert.assertEquals(expectKey, qpsController.getKey()); - } -} diff --git a/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestProviderQpsFlowControlHandler.java b/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestProviderQpsFlowControlHandler.java deleted file mode 100644 index bb52a24470e..00000000000 --- a/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/TestProviderQpsFlowControlHandler.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.qps; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.Const; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.swagger.invocation.AsyncResponse; -import mockit.Expectations; -import mockit.Injectable; -import mockit.Mock; -import mockit.MockUp; - -public class TestProviderQpsFlowControlHandler { - ProviderQpsFlowControlHandler handler = new ProviderQpsFlowControlHandler(); - - Invocation invocation = Mockito.mock(Invocation.class); - - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - - @Before - public void setUP() { - Utils.updateProperty(Config.PROVIDER_LIMIT_KEY_PREFIX + "test", 1); - } - - @Test - public void testGlobalQpsControl(final @Injectable Invocation invocation, - final @Injectable AsyncResponse asyncResp) throws Exception { - new Expectations() { - { - invocation.getContext(Const.SRC_MICROSERVICE); - result = "test"; - asyncResp.producerFail((Throwable) any); - result = new RuntimeException("test error"); - } - }; - - ProviderQpsFlowControlHandler gHandler = new ProviderQpsFlowControlHandler(); - gHandler.handle(invocation, asyncResp); - - Utils.updateProperty(Config.PROVIDER_LIMIT_KEY_GLOBAL, 3); - - int count = 1; - try { - gHandler.handle(invocation, asyncResp); - count++; - gHandler.handle(invocation, asyncResp); - Assert.assertTrue(false); - } catch (Exception e) { - Assert.assertEquals(2, count); - Assert.assertEquals("test error", e.getMessage()); - } - } - - @Test - public void testQpsController() throws Exception { - QpsController qpsController = new QpsController("abc", 100); - Assert.assertEquals(false, qpsController.isLimitNewRequest()); - - qpsController.setQpsLimit(1); - Assert.assertEquals(true, qpsController.isLimitNewRequest()); - } - - @Test - public void testHandleWithException() { - boolean validAssert; - try { - Mockito.when(invocation.getContext(Const.SRC_MICROSERVICE)).thenReturn(null); - - validAssert = true; - handler.handle(invocation, asyncResp); - handler.handle(invocation, asyncResp); - } catch (Exception e) { - validAssert = false; - } - Assert.assertTrue(validAssert); - } - - @Test - public void testHandle() { - boolean validAssert; - try { - validAssert = true; - Mockito.when(invocation.getContext(Const.SRC_MICROSERVICE)).thenReturn("test"); - - new MockUp() { - @Mock - public boolean isLimitNewRequest() { - return true; - } - }; - - new MockUp() { - - @Mock - protected QpsController create(String serviceName) { - return new QpsController(serviceName, 12); - } - }; - handler.handle(invocation, asyncResp); - } catch (Exception e) { - validAssert = false; - } - Assert.assertTrue(validAssert); - } - - @Test - public void testHandleIsLimitNewRequestAsFalse() { - boolean validAssert; - try { - validAssert = true; - Mockito.when(invocation.getContext(Const.SRC_MICROSERVICE)).thenReturn("test"); - - new MockUp() { - @Mock - public boolean isLimitNewRequest() { - return false; - } - }; - - new MockUp() { - - @Mock - protected QpsController create(String serviceName) { - return new QpsController(serviceName, 12); - } - }; - handler.handle(invocation, asyncResp); - } catch (Exception e) { - validAssert = false; - } - Assert.assertTrue(validAssert); - } -} diff --git a/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/Utils.java b/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/Utils.java deleted file mode 100644 index d8ce061dbf8..00000000000 --- a/handlers/handler-flowcontrol-qps/src/test/java/io/servicecomb/qps/Utils.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.qps; - -import java.lang.reflect.Method; - -import org.springframework.util.ReflectionUtils; - -import com.netflix.config.DynamicProperty; - -public class Utils { - private static Method updatePropertyMethod = - ReflectionUtils.findMethod(DynamicProperty.class, "updateProperty", String.class, Object.class); - - static { - updatePropertyMethod.setAccessible(true); - } - - public static void updateProperty(String key, Object value) { - ReflectionUtils.invokeMethod(updatePropertyMethod, null, key, value); - } -} 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 new file mode 100644 index 00000000000..e2b5f79fa69 --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/QpsControllerManagerTest.java @@ -0,0 +1,391 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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.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() { + 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() { + + } + + @Test + public void testGetOrCreate() { + Invocation invocation = Mockito.mock(Invocation.class); + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + Mockito.when(invocation.getSchemaId()).thenReturn("server"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); + + QpsControllerManager testQpsControllerManager = new QpsControllerManager(false, environment); + initTestQpsControllerManager(false, testQpsControllerManager, invocation, operationMeta); + + // pojo + setConfigWithDefaultPrefix(false, "pojo", 100); + QpsStrategy qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); + Assertions.assertEquals("pojo", ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(100, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); + qpsStrategy = testQpsControllerManager.getOrCreate("pojo2", invocation); + Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + + qpsStrategy = testQpsControllerManager.getOrCreate("poj", invocation); + Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + + 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); + + 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); + + testGetOrCreateCommon(false, testQpsControllerManager, invocation, operationMeta); + } + + @Test + public void testGetOrCreateWithGlobalConfig() { + Invocation invocation = Mockito.mock(Invocation.class); + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + Mockito.when(invocation.getSchemaId()).thenReturn("server"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); + + QpsControllerManager testQpsControllerManager = new QpsControllerManager(true, environment); + + // global + 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()); + qpsStrategy = testQpsControllerManager.getOrCreate("pojo2", invocation); + Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(50, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); + qpsStrategy = testQpsControllerManager.getOrCreate("poj", invocation); + Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(50, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); + + // pojo + setConfigWithDefaultPrefix(true, "pojo", 100); + qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); + Assertions.assertEquals("pojo", ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(100, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); + qpsStrategy = testQpsControllerManager.getOrCreate("pojo2", invocation); + Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(50, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); + qpsStrategy = testQpsControllerManager.getOrCreate("poj", invocation); + Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(50, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); + + testGetOrCreateCommon(true, testQpsControllerManager, invocation, operationMeta); + } + + @Test + public void testQualifiedNameKey() { + Invocation invocation = Mockito.mock(Invocation.class); + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + Mockito.when(invocation.getSchemaId()).thenReturn("schema"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("schema.opr"); + QpsControllerManager qpsControllerManager = new QpsControllerManager(true, environment); + QpsStrategy qpsStrategy = qpsControllerManager.getOrCreate("service", invocation); + Assertions.assertEquals("servicecomb.flowcontrol.Provider.qps.global.limit", + ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + + Mockito.when(invocation.getSchemaId()).thenReturn("test_schema"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("test_schema.test_opr"); + qpsStrategy = qpsControllerManager.getOrCreate("test_service", invocation); + Assertions.assertEquals("servicecomb.flowcontrol.Provider.qps.global.limit", + ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + + Mockito.when(invocation.getSchemaId()).thenReturn("test_schema"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("test-schema.test-opr"); + qpsStrategy = qpsControllerManager.getOrCreate("test-service", invocation); + Assertions.assertEquals("servicecomb.flowcontrol.Provider.qps.global.limit", + ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + + Mockito.when(invocation.getSchemaId()).thenReturn("schema"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("schema.opr.tail"); + qpsStrategy = qpsControllerManager.getOrCreate("svc", invocation); + Assertions.assertEquals("servicecomb.flowcontrol.Provider.qps.global.limit", + ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + + Mockito.when(invocation.getSchemaId()).thenReturn("schema.opr2"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("schema.opr2.tail"); + qpsStrategy = qpsControllerManager.getOrCreate("svc", invocation); + Assertions.assertEquals("servicecomb.flowcontrol.Provider.qps.global.limit", + ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + } + + private void testGetOrCreateCommon(boolean isProvider, QpsControllerManager testQpsControllerManager, + Invocation invocation, + OperationMeta operationMeta) { + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); + + setConfigWithDefaultPrefix(isProvider, "pojo.server", 200); + QpsStrategy qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); + Assertions.assertEquals("pojo.server", ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(200, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server2.test"); + qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); + Assertions.assertEquals("pojo", ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(100, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("serve.test"); + qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); + Assertions.assertEquals("pojo", ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(100, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); + + // pojo.server.test + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); + setConfigWithDefaultPrefix(isProvider, "pojo.server.test", 300); + qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); + Assertions.assertEquals("pojo.server.test", ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(300, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test2"); + qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); + Assertions.assertEquals("pojo.server", ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(200, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.tes"); + qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); + Assertions.assertEquals("pojo.server", ((AbstractQpsStrategy) qpsStrategy).getKey()); + Assertions.assertEquals(200, (long) ((AbstractQpsStrategy) qpsStrategy).getQpsLimit()); + } + + /** + * Init testQpsControllerManager to test search function. + */ + private void initTestQpsControllerManager(boolean isProvider, QpsControllerManager testQpsControllerManager, + Invocation invocation, + OperationMeta operationMeta) { + // pojo.server.test + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + Mockito.when(invocation.getSchemaId()).thenReturn("server"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); + QpsStrategy qpsStrategy = testQpsControllerManager.getOrCreate("pojo", invocation); + if (isProvider) { + Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } else { + Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + + // pojo.server.test2 + Mockito.when(invocation.getSchemaId()).thenReturn("server"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test2"); + testQpsControllerManager.getOrCreate("pojo", invocation); + if (isProvider) { + Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } else { + Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + + // pojo.server.tes + Mockito.when(invocation.getSchemaId()).thenReturn("server"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.tes"); + testQpsControllerManager.getOrCreate("pojo", invocation); + if (isProvider) { + Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } else { + Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + + // pojo.server2.test + Mockito.when(invocation.getSchemaId()).thenReturn("server2"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server2.test"); + testQpsControllerManager.getOrCreate("pojo", invocation); + if (isProvider) { + Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } else { + Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + + // pojo.serve.test + Mockito.when(invocation.getSchemaId()).thenReturn("serve"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("serve.test"); + testQpsControllerManager.getOrCreate("pojo", invocation); + if (isProvider) { + Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } else { + Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + + // pojo2.server.test + Mockito.when(invocation.getSchemaId()).thenReturn("server"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); + qpsStrategy = testQpsControllerManager.getOrCreate("pojo2", invocation); + if (isProvider) { + Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } else { + Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + + // poj.server.test + Mockito.when(invocation.getSchemaId()).thenReturn("server"); + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("server.test"); + qpsStrategy = testQpsControllerManager.getOrCreate("poj", invocation); + if (isProvider) { + Assertions.assertEquals(Config.PROVIDER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } else { + Assertions.assertEquals(Config.CONSUMER_LIMIT_KEY_GLOBAL, ((AbstractQpsStrategy) qpsStrategy).getKey()); + } + Assertions.assertEquals(Integer.MAX_VALUE, ((AbstractQpsStrategy) qpsStrategy).getQpsLimit().intValue()); + } + + @Test + public void testMock() { + Invocation invocation = getMockInvocation("service", "schema", "oper"); + OperationMeta operationMeta = invocation.getOperationMeta(); + SchemaMeta schemaMeta = operationMeta.getSchemaMeta(); + + Assertions.assertEquals("service", operationMeta.getMicroserviceName()); + Assertions.assertEquals("service.schema.oper", operationMeta.getMicroserviceQualifiedName()); + Assertions.assertEquals("schema.oper", operationMeta.getSchemaQualifiedName()); + Assertions.assertEquals("schema", schemaMeta.getSchemaId()); + } + + public static Invocation getMockInvocation(String microserviceName, String schemaId, String operationId) { + return getMockInvocation( + getMockOperationMeta(microserviceName, schemaId, operationId)); + } + + private static Invocation getMockInvocation(OperationMeta mockOperationMeta) { + Invocation invocation = Mockito.mock(Invocation.class); + Mockito.when(invocation.getOperationMeta()).thenReturn(mockOperationMeta); + return invocation; + } + + public static OperationMeta getMockOperationMeta(String microserviceName, String schemaId, String operationId) { + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); + + Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); + Mockito.when(operationMeta.getSchemaQualifiedName()) + .thenReturn(schemaId + QpsControllerManager.SEPARATOR + operationId); + Mockito.when(operationMeta.getMicroserviceQualifiedName()).thenReturn( + microserviceName + QpsControllerManager.SEPARATOR + schemaId + QpsControllerManager.SEPARATOR + + operationId); + Mockito.when(operationMeta.getMicroserviceName()).thenReturn(microserviceName); + Mockito.when(schemaMeta.getSchemaId()).thenReturn(schemaId); + + return operationMeta; + } + + 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; + } + + 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) { + String configKey = Config.CONSUMER_LIMIT_KEY_PREFIX + key; + if (isProvider) { + configKey = Config.PROVIDER_LIMIT_KEY_PREFIX + key; + } + + Mockito.when(environment.getProperty(configKey, Long.class)).thenReturn(null); + HashMap updated = new HashMap<>(); + updated.put(configKey, null); + EventManager.post(ConfigurationChangedEvent.createIncremental(updated)); + } + + @Test + public void testDeleteQpsController() { + + final String microserviceName = "springmvcClient"; + final String schemaId = "controller"; + final String operationId = "add"; + final String configKey = "springmvcClient.controller.add"; + + QpsControllerManager testManager = new QpsControllerManager(true, environment); + Invocation testInvocation = getMockInvocation(microserviceName, schemaId, operationId); + Mockito.when(testInvocation.getSchemaId()).thenReturn(schemaId); + + QpsStrategy strategy1 = testManager.getOrCreate(microserviceName, testInvocation); + + setConfigWithDefaultPrefix(true, configKey, 1); + + deleteConfigWithDefaultPrefix(true, configKey); + + QpsStrategy strategy2 = testManager.getOrCreate(microserviceName, testInvocation); + + 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/TestQpsStrategy.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestQpsStrategy.java new file mode 100644 index 00000000000..f34b6c5a261 --- /dev/null +++ b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestQpsStrategy.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.qps; + +import org.apache.servicecomb.qps.strategy.AbstractQpsStrategy; +import org.apache.servicecomb.qps.strategy.FixedWindowStrategy; +import org.apache.servicecomb.qps.strategy.LeakyBucketStrategy; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author GuoYl123 + * @since 2020/7/16 + **/ +public class TestQpsStrategy { + + @Test + public void testFixedWindowStrategy() { + AbstractQpsStrategy qpsStrategy = new FixedWindowStrategy(); + qpsStrategy.setKey("abc"); + qpsStrategy.setQpsLimit(100L); + Assertions.assertFalse(qpsStrategy.isLimitNewRequest()); + + qpsStrategy.setQpsLimit(1L); + Assertions.assertTrue(qpsStrategy.isLimitNewRequest()); + } + + + @Test + public void testLeakyBucketStrategy() { + LeakyBucketStrategy qpsStrategy = new LeakyBucketStrategy(); + qpsStrategy.setKey("abc"); + qpsStrategy.setQpsLimit(100L); + Assertions.assertFalse(qpsStrategy.isLimitNewRequest()); + + qpsStrategy.setQpsLimit(1L); + qpsStrategy.setBucketLimit(1L); + Assertions.assertTrue(qpsStrategy.isLimitNewRequest()); + } + +} diff --git a/handlers/handler-flowcontrol-qps/src/test/resources/log4j.properties b/handlers/handler-flowcontrol-qps/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/handlers/handler-flowcontrol-qps/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/handlers/handler-governance/README.md b/handlers/handler-governance/README.md new file mode 100644 index 00000000000..b6a237f6510 --- /dev/null +++ b/handlers/handler-governance/README.md @@ -0,0 +1,3 @@ +Enable governance features by handler. + +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 new file mode 100644 index 00000000000..7db533e562e --- /dev/null +++ b/handlers/handler-governance/pom.xml @@ -0,0 +1,65 @@ + + + + 4.0.0 + + org.apache.servicecomb + handlers + 3.4.0-SNAPSHOT + + + handler-governance + Java Chassis::Handlers::Governance + + + UTF-8 + + + + + org.apache.servicecomb + java-chassis-core + + + org.apache.servicecomb + servicecomb-governance + + + org.apache.servicecomb + registry-local + test + + + org.apache.servicecomb + foundation-test-scaffolding + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + + 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 new file mode 100644 index 00000000000..1b007e111ad --- /dev/null +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceBulkheadFilter.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.handler.governance; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +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.MatchType; +import org.apache.servicecomb.governance.handler.InstanceBulkheadHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +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 org.springframework.beans.factory.annotation.Autowired; + +import io.github.resilience4j.bulkhead.Bulkhead; +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 extends AbstractFilter implements ConsumerFilter, EdgeFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerInstanceBulkheadFilter.class); + + private final InstanceBulkheadHandler instanceBulkheadHandler; + + @Autowired + public ConsumerInstanceBulkheadFilter(InstanceBulkheadHandler instanceBulkheadHandler) { + this.instanceBulkheadHandler = instanceBulkheadHandler; + } + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1060; + } + + @Override + public String getName() { + return "instance-bulkhead"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + if (invocation.getEndpoint() == null) { + return CompletableFuture.failedFuture(new InvocationException(Status.INTERNAL_SERVER_ERROR, + new CommonExceptionData("instance bulkhead should work after load balancer."))); + } + Supplier> next = createBusinessCompletionStageSupplier(invocation, nextNode); + DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); + GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); + + addBulkhead(dcs, request); + + CompletableFuture future = new CompletableFuture<>(); + + dcs.get().whenComplete((r, e) -> { + if (e == null) { + future.complete(r); + return; + } + + if (e instanceof BulkheadFullException) { + // return 503 so that consumer can retry + future.completeExceptionally(new InvocationException(503, "bulkhead is full and does not permit further calls.", + new CommonExceptionData("bulkhead is full and does not permit further calls."))); + LOGGER.warn("bulkhead is full and does not permit further calls by policy : {}", e.getMessage()); + } else { + future.completeExceptionally(e); + } + }); + + return future; + } + + private void addBulkhead(DecorateCompletionStage dcs, GovernanceRequestExtractor request) { + Bulkhead bulkhead = instanceBulkheadHandler.getActuator(request); + if (bulkhead != null) { + dcs.withBulkhead(bulkhead); + } + } + + private Supplier> createBusinessCompletionStageSupplier(Invocation invocation, + FilterNode nextNode) { + return () -> nextNode.onFilter(invocation); + } +} 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 new file mode 100644 index 00000000000..e60d52fab18 --- /dev/null +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationFilter.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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 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.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; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import io.github.resilience4j.circuitbreaker.CallNotPermittedException; +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 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() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1050; + } + + @Override + public String getName() { + return "instance-isolation"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + if (invocation.getEndpoint() == null) { + 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); + + addCircuitBreaker(dcs, request); + + CompletableFuture future = new CompletableFuture<>(); + + dcs.get().whenComplete((r, e) -> { + if (e == null) { + future.complete(r); + return; + } + + if (e instanceof CallNotPermittedException) { + LOGGER.warn("instance isolation circuitBreaker is open by policy : {}", e.getMessage()); + 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.")))); + } else { + future.completeExceptionally(e); + } + }); + + return future; + } + + private void addCircuitBreaker(DecorateCompletionStage dcs, GovernanceRequestExtractor request) { + CircuitBreaker circuitBreaker = instanceIsolationHandler.getActuator(request); + if (circuitBreaker != null) { + dcs.withCircuitBreaker(circuitBreaker); + } + } + + private Supplier> createBusinessCompletionStageSupplier(Invocation invocation, + FilterNode nextNode) { + return () -> nextNode.onFilter(invocation); + } +} 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 new file mode 100644 index 00000000000..9223146a759 --- /dev/null +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/HandlerGovernanceConfiguration.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.handler.governance; + +import org.apache.servicecomb.governance.handler.BulkheadHandler; +import org.apache.servicecomb.governance.handler.CircuitBreakerHandler; +import org.apache.servicecomb.governance.handler.InstanceBulkheadHandler; +import org.apache.servicecomb.governance.handler.InstanceIsolationHandler; +import org.apache.servicecomb.governance.handler.RateLimitingHandler; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnProperty(value = HandlerGovernanceConfiguration.GOVERNANCE_ENABLED, + havingValue = "true", matchIfMissing = true) +public class HandlerGovernanceConfiguration { + public static final String GOVERNANCE_PREFIX = "servicecomb.governance"; + + public static final String GOVERNANCE_ENABLED = GOVERNANCE_PREFIX + ".enabled"; + + @Bean + public ConsumerInstanceBulkheadFilter scbConsumerInstanceBulkheadFilter( + InstanceBulkheadHandler instanceBulkheadHandler) { + return new ConsumerInstanceBulkheadFilter(instanceBulkheadHandler); + } + + @Bean + public ConsumerInstanceIsolationFilter scbConsumerInstanceIsolationFilter( + InstanceIsolationHandler instanceIsolationHandler) { + return new ConsumerInstanceIsolationFilter(instanceIsolationHandler); + } + + @Bean + public ProviderBulkheadFilter scbProviderBulkheadFilter(BulkheadHandler bulkheadHandler) { + return new ProviderBulkheadFilter(bulkheadHandler); + } + + @Bean + public ProviderCircuitBreakerFilter scbProviderCircuitBreakerFilter(CircuitBreakerHandler circuitBreakerHandler) { + return new ProviderCircuitBreakerFilter(circuitBreakerHandler); + } + + @Bean + 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 new file mode 100644 index 00000000000..a68ebf08fb1 --- /dev/null +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderBulkheadFilter.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.handler.governance; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.function.Supplier; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.core.governance.MatchType; +import org.apache.servicecomb.governance.handler.BulkheadHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +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 org.springframework.beans.factory.annotation.Autowired; + +import io.github.resilience4j.bulkhead.Bulkhead; +import io.github.resilience4j.bulkhead.BulkheadFullException; +import io.github.resilience4j.decorators.Decorators; +import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; + +public class ProviderBulkheadFilter extends AbstractFilter implements ProviderFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(ProviderBulkheadFilter.class); + + private final BulkheadHandler bulkheadHandler; + + @Autowired + public ProviderBulkheadFilter(BulkheadHandler bulkheadHandler) { + this.bulkheadHandler = bulkheadHandler; + } + + @Override + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1880; + } + + @Override + public String getName() { + return "provider-bulkhead"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + + Supplier> next = createBusinessCompletionStageSupplier(invocation, nextNode); + DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); + GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); + + addBulkhead(dcs, request); + + CompletableFuture future = new CompletableFuture<>(); + + dcs.get().whenComplete((r, e) -> { + if (e == null) { + future.complete(r); + return; + } + + if (e instanceof BulkheadFullException) { + future.completeExceptionally( + new InvocationException(429, "bulkhead is full and does not permit further calls.", + new CommonExceptionData("bulkhead is full and does not permit further calls."))); + LOGGER.warn("bulkhead is full and does not permit further calls by policy : {}", e.getMessage()); + } else { + future.completeExceptionally(e); + } + }); + + return future; + } + + private void addBulkhead(DecorateCompletionStage dcs, GovernanceRequestExtractor request) { + Bulkhead bulkhead = bulkheadHandler.getActuator(request); + if (bulkhead != null) { + dcs.withBulkhead(bulkhead); + } + } + + private Supplier> createBusinessCompletionStageSupplier(Invocation invocation, + FilterNode nextNode) { + return () -> nextNode.onFilter(invocation); + } +} 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 new file mode 100644 index 00000000000..dbc0b8978c6 --- /dev/null +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderCircuitBreakerFilter.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.handler.governance; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.function.Supplier; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.core.governance.MatchType; +import org.apache.servicecomb.governance.handler.CircuitBreakerHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +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 org.springframework.beans.factory.annotation.Autowired; + +import io.github.resilience4j.circuitbreaker.CallNotPermittedException; +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.decorators.Decorators; +import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; + +public class ProviderCircuitBreakerFilter extends AbstractFilter implements ProviderFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(ProviderCircuitBreakerFilter.class); + + private final CircuitBreakerHandler circuitBreakerHandler; + + @Autowired + public ProviderCircuitBreakerFilter(CircuitBreakerHandler circuitBreakerHandler) { + this.circuitBreakerHandler = circuitBreakerHandler; + } + + @Override + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1890; + } + + @Override + public String getName() { + return "provider-circuit-breaker"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + Supplier> next = createBusinessCompletionStageSupplier(invocation, nextNode); + DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); + GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); + + addCircuitBreaker(dcs, request); + + CompletableFuture future = new CompletableFuture<>(); + + dcs.get().whenComplete((r, e) -> { + if (e == null) { + future.complete(r); + return; + } + + if (e instanceof CallNotPermittedException) { + future.completeExceptionally(new InvocationException(429, "circuitBreaker is open.", + new CommonExceptionData("circuitBreaker is open."))); + LOGGER.warn("circuitBreaker is open by policy : {}", e.getMessage()); + } else { + future.completeExceptionally(e); + } + }); + + return future; + } + + private void addCircuitBreaker(DecorateCompletionStage dcs, GovernanceRequestExtractor request) { + CircuitBreaker circuitBreaker = circuitBreakerHandler.getActuator(request); + if (circuitBreaker != null) { + dcs.withCircuitBreaker(circuitBreaker); + } + } + + private Supplier> createBusinessCompletionStageSupplier(Invocation invocation, + FilterNode nextNode) { + return () -> nextNode.onFilter(invocation); + } +} 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 new file mode 100644 index 00000000000..732fcf48ab5 --- /dev/null +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderRateLimitingFilter.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.handler.governance; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.function.Supplier; + +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.core.governance.MatchType; +import org.apache.servicecomb.governance.handler.RateLimitingHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +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 org.springframework.beans.factory.annotation.Autowired; + +import io.github.resilience4j.decorators.Decorators; +import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RequestNotPermitted; + +public class ProviderRateLimitingFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(ProviderRateLimitingFilter.class); + + private final RateLimitingHandler rateLimitingHandler; + + @Autowired + public ProviderRateLimitingFilter(RateLimitingHandler rateLimitingHandler) { + this.rateLimitingHandler = rateLimitingHandler; + } + + @Override + public int getOrder() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1900; + } + + @Override + public String getName() { + return "rate-limiting"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + + Supplier> next = createBusinessCompletionStageSupplier(invocation, nextNode); + DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); + GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); + + addRateLimiting(dcs, request); + + CompletableFuture future = new CompletableFuture<>(); + + dcs.get().whenComplete((r, e) -> { + if (e == null) { + future.complete(r); + return; + } + + if (e instanceof RequestNotPermitted) { + future.completeExceptionally( + new InvocationException(429, "rate limited.", new CommonExceptionData("rate limited."))); + LOGGER.warn("the request is rate limit by policy : {}", e.getMessage()); + } else { + future.completeExceptionally(e); + } + }); + + return future; + } + + private void addRateLimiting(DecorateCompletionStage dcs, GovernanceRequestExtractor request) { + RateLimiter rateLimiter = rateLimitingHandler.getActuator(request); + if (rateLimiter != null) { + dcs.withRateLimiter(rateLimiter); + } + } + + private Supplier> createBusinessCompletionStageSupplier(Invocation invocation, + FilterNode nextNode) { + return () -> nextNode.onFilter(invocation); + } +} 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 6aa8cc8170b..0fd42dce838 100644 --- a/handlers/handler-loadbalance/pom.xml +++ b/handlers/handler-loadbalance/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - handlers - 0.3.0-SNAPSHOT - + 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 + handlers + 3.4.0-SNAPSHOT + - handler-loadbalance + handler-loadbalance + Java Chassis::Handlers::Loadbalance - - UTF-8 - + + UTF-8 + - - - io.servicecomb - java-chassis-core - - - com.netflix.ribbon - ribbon - - - com.netflix.ribbon - ribbon-loadbalancer - - - com.netflix.ribbon - ribbon-core - - - com.netflix.archaius - archaius-core - - - io.reactivex - rxjava - - - commons-lang - commons-lang - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + + + org.apache.servicecomb + java-chassis-core + + + org.apache.commons + commons-lang3 + + + io.github.resilience4j + resilience4j-all + + + org.apache.servicecomb + registry-local + test + + + org.apache.servicecomb + foundation-test-scaffolding + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.jmockit + jmockit + test + + 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/io/servicecomb/loadbalance/Configuration.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/Configuration.java deleted file mode 100644 index 46c8b56b133..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/Configuration.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import java.util.Map; - -import com.netflix.config.DynamicPropertyFactory; - -import io.servicecomb.serviceregistry.config.ConfigurePropertyUtils; - -/** - * configuration items - * - */ -public final class Configuration { - //// 2.1 configuration items - public static final String PROP_ROOT = "cse.loadbalance."; - - public static final String PROP_POLICY = "NFLoadBalancerRuleClassName"; - - // default rule - public static final String PROP_POLICY_ROUNDROBIN = "com.netflix.loadbalancer.RoundRobinRule"; - - // 2.0 configuration items - public static final String PROP_ROOT_20 = "ribbon."; - - // retry configurations - public static final String PROP_RETRY_ENABLED = "retryEnabled"; - - public static final String PROP_RETRY_ONNEXT = "retryOnNext"; - - public static final String PROP_RETRY_ONSAME = "retryOnSame"; - - // SessionStickinessRule configruation - public static final String SESSION_TIMEOUT_IN_SECONDS = "SessionStickinessRule.sessionTimeoutInSeconds"; - - public static final String SUCCESSIVE_FAILED_TIMES = "SessionStickinessRule.successiveFailedTimes"; - - // Begin: ServerListFilters configurations - //Enabled filter lists, e.g cse.loadbalance.serverListFilters=a,b,c - public static final String SERVER_LIST_FILTERS = "cse.loadbalance.serverListFilters"; - - //Class name of each filter: e.g cse.loadbalance.serverListFilter.a.className=io.servicecomb.MyServerListFilterExt - public static final String SERVER_LIST_FILTER_CLASS_HOLDER = "cse.loadbalance.serverListFilter.%s.className"; - - //Property of the class: e.g cse.loadbalance.serverListFilter.a.myproperty=sample - public static final String SERVER_LIST_FILTER_PROPERTY_HOLDER = "cse.loadbalance.serverListFilter.%s.%s"; - //End: ServerListFilters configurations - - 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_SINGLE_TEST = "singleTestTime"; - - public static final String TRANSACTIONCONTROL_OPTIONS_PREFIX_PATTERN = - "cse.loadbalance.%s.transactionControl.options"; - - public static final String TRANSACTIONCONTROL_POLICY_KEY_PATTERN = "cse.loadbalance.%s.transactionControl.policy"; - - public static final Configuration INSTANCE = new Configuration(); - - private Configuration() { - } - - public String getPolicy(String microservice) { - return getStringProperty(PROP_POLICY_ROUNDROBIN, - PROP_ROOT + microservice + "." + PROP_POLICY, - PROP_ROOT_20 + microservice + "." + PROP_POLICY, - PROP_ROOT + PROP_POLICY, - PROP_ROOT_20 + PROP_POLICY); - } - - public int getSessionTimeoutInSeconds() { - final int defaultValue = 30; - // do not support MicroService level now - String p = getStringProperty("30", - PROP_ROOT + SESSION_TIMEOUT_IN_SECONDS); - try { - return Integer.parseInt(p); // can be negative - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public int getSuccessiveFailedTimes() { - final int defaultValue = 5; - // do not support MicroService level now - String p = getStringProperty("5", - PROP_ROOT + SUCCESSIVE_FAILED_TIMES); - try { - return Integer.parseInt(p); // can be negative - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public boolean isRetryEnabled(String microservice) { - String p = getStringProperty("false", - PROP_ROOT + microservice + "." + PROP_RETRY_ENABLED, - PROP_ROOT + PROP_RETRY_ENABLED); - return Boolean.parseBoolean(p); - } - - public int getRetryOnNext(String microservice) { - final int defaultValue = 0; - String p = getStringProperty("0", - PROP_ROOT + microservice + "." + PROP_RETRY_ONNEXT, - PROP_ROOT + PROP_RETRY_ONNEXT); - try { - int result = Integer.parseInt(p); - if (result > 0) { - return result; - } else { - return defaultValue; - } - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public int getRetryOnSame(String microservice) { - final int defaultValue = 0; - String p = getStringProperty("0", - PROP_ROOT + microservice + "." + PROP_RETRY_ONSAME, - PROP_ROOT + PROP_RETRY_ONSAME); - try { - int result = Integer.parseInt(p); - if (result > 0) { - return result; - } else { - return defaultValue; - } - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public boolean isIsolationFilterOpen(String microservice) { - String p = getStringProperty("false", - PROP_ROOT + microservice + "." + FILTER_ISOLATION + FILTER_OPEN, - PROP_ROOT + FILTER_ISOLATION + FILTER_OPEN); - return Boolean.parseBoolean(p); - } - - public int getErrorThresholdPercentage(String microservice) { - final int defaultValue = 20; - String p = getStringProperty("20", - PROP_ROOT + microservice + "." + FILTER_ISOLATION + FILTER_ERROR_PERCENTAGE, - PROP_ROOT + FILTER_ISOLATION + FILTER_ERROR_PERCENTAGE); - try { - int result = Integer.parseInt(p); - if (result <= PERCENT && result > 0) { - return result; - } else { - return defaultValue; - } - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public int getEnableRequestThreshold(String microservice) { - final int defaultValue = 20; - String p = getStringProperty("20", - PROP_ROOT + microservice + "." + FILTER_ISOLATION + FILTER_ENABLE_REQUEST, - PROP_ROOT + FILTER_ISOLATION + FILTER_ENABLE_REQUEST); - try { - int result = Integer.parseInt(p); - if (result > 0) { - return result; - } else { - return defaultValue; - } - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public int getSingleTestTime(String microservice) { - final int defaultValue = 10000; - String p = getStringProperty("10000", - PROP_ROOT + microservice + "." + FILTER_ISOLATION + FILTER_SINGLE_TEST, - PROP_ROOT + FILTER_ISOLATION + FILTER_SINGLE_TEST); - try { - int result = Integer.parseInt(p); - if (result > 0) { - return result; - } else { - return defaultValue; - } - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public String getFlowsplitFilterPolicy(String microservice) { - return getStringProperty("", String.format(TRANSACTIONCONTROL_POLICY_KEY_PATTERN, microservice)); - } - - public Map getFlowsplitFilterOptions(String microservice) { - String keyPrefix = String.format(TRANSACTIONCONTROL_OPTIONS_PREFIX_PATTERN, microservice); - return ConfigurePropertyUtils.getPropertiesWithPrefix(keyPrefix); - } - - public static String getStringProperty(String defaultValue, String... keys) { - String property = null; - for (String key : keys) { - property = DynamicPropertyFactory.getInstance().getStringProperty(key, null).get(); - if (property != null) { - break; - } - } - - if (property != null) { - return property; - } else { - return defaultValue; - } - } -} diff --git a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/CseServer.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/CseServer.java deleted file mode 100644 index 318b2c74688..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/CseServer.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import com.netflix.loadbalancer.Server; - -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Transport; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.cache.CacheEndpoint; - -/** - * 服务器抽象,address只有transport识别, LB模块不识别 - * LB模块不提供服务器状态监测,这块功能是由注册中心进行处理的。 - * - */ -public class CseServer extends Server { - private final Endpoint endpoint; - - // 所属服务实例 - private final MicroserviceInstance instance; - - private long lastVisitTime = System.currentTimeMillis(); - - public long getLastVisitTime() { - return lastVisitTime; - } - - public void setLastVisitTime(long lastVisitTime) { - this.lastVisitTime = lastVisitTime; - } - - public CseServer(Transport transport, CacheEndpoint cacheEndpoint) { - super(null); - - endpoint = new Endpoint(transport, cacheEndpoint.getEndpoint(), cacheEndpoint.getInstance()); - instance = cacheEndpoint.getInstance(); - - this.setAlive(true); - this.setReadyToServe(true); - } - - public Endpoint getEndpoint() { - return endpoint; - } - - public MicroserviceInstance getInstance() { - return instance; - } - - public String toString() { - return endpoint.getEndpoint(); - } - - // used in LoadBalancerContext - public String getHost() { - return endpoint.getEndpoint(); - } - - public boolean equals(Object o) { - if (o instanceof CseServer) { - return this.getHost().equals(((CseServer) o).getHost()); - } else { - return false; - } - } - - public int hashCode() { - return this.getHost().hashCode(); - } -} diff --git a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/CseServerList.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/CseServerList.java deleted file mode 100644 index 1e4d7312630..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/CseServerList.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import java.util.List; - -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerList; - -/** - * 通过RegistryUtils查询服务器列表。 RegistryUtils本身具备缓存和刷新不可用服务器的功能,因此这里不需要进行列表缓存和状态检测。 - */ -public class CseServerList implements ServerList { - private ServerListCache serverListCache; - - public CseServerList(String appId, String microserviceName, String microserviceVersionRule, - String transportName) { - serverListCache = new ServerListCache(appId, microserviceName, microserviceVersionRule, transportName); - } - - @Override - public List getInitialListOfServers() { - return serverListCache.getLatestEndpoints(); - } - - @Override - public List getUpdatedListOfServers() { - return getInitialListOfServers(); - } -} diff --git a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/LoadBalancer.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/LoadBalancer.java deleted file mode 100644 index 87519551f36..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/LoadBalancer.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import com.netflix.loadbalancer.AbstractLoadBalancer; -import com.netflix.loadbalancer.IRule; -import com.netflix.loadbalancer.LoadBalancerStats; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerListFilter; - -import io.servicecomb.core.Invocation; - -/** - * 实现不包含服务器状态监测的负载均衡器。(这些职责在注册中心客户端实现) - * - */ -public class LoadBalancer extends AbstractLoadBalancer { - private CseServerList serverList; - - private IRule rule; - - private LoadBalancerStats lbStats; - - // 以filter类名为Key - private Map filters; - - public LoadBalancer(CseServerList serverList, IRule rule) { - this.serverList = serverList; - this.rule = rule; - this.rule.setLoadBalancer(this); - this.lbStats = new LoadBalancerStats(null); - this.filters = new ConcurrentHashMap<>(); - } - - @Override - public void addServers(List newServers) { - throw new UnsupportedOperationException("Not implemented."); - } - - @Override - public Server chooseServer(Object key) { - return rule.choose(key); - } - - @Override - public void markServerDown(Server server) { - throw new UnsupportedOperationException("Not implemented."); - } - - @Override - public List getServerList(boolean availableOnly) { - return getAllServers(); - } - - @Override - public List getReachableServers() { - return getAllServers(); - } - - @Override - public List getAllServers() { - List servers = serverList.getInitialListOfServers(); - for (ServerListFilter filter : filters.values()) { - servers = filter.getFilteredListOfServers(servers); - } - return servers; - } - - @Override - public List getServerList(ServerGroup serverGroup) { - throw new UnsupportedOperationException("Not implemented."); - } - - @Override - public LoadBalancerStats getLoadBalancerStats() { - return lbStats; - } - - public void setInvocation(Invocation invocation) { - for (ServerListFilterExt filter : filters.values()) { - filter.setInvocation(invocation); - } - } - - public void putFilter(String name, ServerListFilterExt filter) { - filters.put(name, filter); - } - - public int getFilterSize() { - return filters.size(); - } -} diff --git a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/LoadbalanceClientConfig.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/LoadbalanceClientConfig.java deleted file mode 100644 index a315edebfef..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/LoadbalanceClientConfig.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import com.netflix.client.config.DefaultClientConfigImpl; - -/** - * 配置转换(暂时没有配置项,待新增) - * - */ -public class LoadbalanceClientConfig extends DefaultClientConfigImpl { - private static final String PREFIX = "cse.loadbalance"; - - public LoadbalanceClientConfig(String namespace) { - super(namespace); - loadProperties(PREFIX); - } -} diff --git a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/LoadbalanceHandler.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/LoadbalanceHandler.java deleted file mode 100644 index fbc6ffc8c62..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/LoadbalanceHandler.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.client.DefaultLoadBalancerRetryHandler; -import com.netflix.loadbalancer.IRule; -import com.netflix.loadbalancer.RoundRobinRule; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.reactive.ExecutionContext; -import com.netflix.loadbalancer.reactive.ExecutionInfo; -import com.netflix.loadbalancer.reactive.ExecutionListener; -import com.netflix.loadbalancer.reactive.LoadBalancerCommand; -import com.netflix.loadbalancer.reactive.ServerOperation; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.exception.ExceptionUtils; -import io.servicecomb.core.handler.impl.AbstractHandler; -import io.servicecomb.core.provider.consumer.SyncResponseExecutor; -import io.servicecomb.loadbalance.filter.IsolationServerListFilter; -import io.servicecomb.loadbalance.filter.TransactionControlFilter; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.Response; -import rx.Observable; - -/** - * 负载均衡处理链 - * - */ -public class LoadbalanceHandler extends AbstractHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(LoadbalanceHandler.class); - - private static final ExecutorService RETRY_POOL = Executors.newCachedThreadPool(new ThreadFactory() { - private AtomicInteger count = new AtomicInteger(0); - - @Override - public Thread newThread(Runnable r) { - return new Thread(r, "retry-pool-thread-" + count.getAndIncrement()); - } - }); - - // 会给每个Microservice创建一个handler实例,因此这里的key为transportName,保证每个通道使用一个负载均衡策略 - private volatile Map loadBalancerMap = new ConcurrentHashMap<>(); - - private final Object lock = new Object(); - - private String policy = null; - - @Override - public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception { - String p = Configuration.INSTANCE.getPolicy(invocation.getMicroserviceName()); - if (this.policy != null && !this.policy.equals(p)) { - //配置变化,需要重新生成所有的lb实例 - synchronized (lock) { - loadBalancerMap.clear(); - } - } - this.policy = p; - - String transportName = invocation.getConfigTransportName(); - LoadBalancer lb = loadBalancerMap.get(transportName); - if (null == lb) { - synchronized (lock) { - lb = loadBalancerMap.get(transportName); - if (null == lb) { - // 只能使用微服务级别的属性,因为LoadBalancer实例是按照{微服务+transport}的个数创建的。 - lb = createLoadBalancer(invocation.getAppId(), - invocation.getMicroserviceName(), - invocation.getMicroserviceVersionRule(), - transportName); - loadBalancerMap.put(transportName, lb); - } - } - } - - // invocation是请求级别的,因此每次调用都需要设置一次 - lb.setInvocation(invocation); - final LoadBalancer choosenLB = lb; - - if (!Configuration.INSTANCE.isRetryEnabled(invocation.getMicroserviceName())) { - send(invocation, asyncResp, choosenLB); - } else { - sendWithRetry(invocation, asyncResp, choosenLB); - } - } - - protected void setIsolationFilter(LoadBalancer lb, String microserviceName) { - final String filterName = IsolationServerListFilter.class.getName(); - IsolationServerListFilter isolationListFilter = new IsolationServerListFilter(); - isolationListFilter.setMicroserviceName(microserviceName); - isolationListFilter.setLoadBalancerStats(lb.getLoadBalancerStats()); - lb.putFilter(filterName, isolationListFilter); - } - - protected void setTransactionControlFilter(LoadBalancer lb, String microserviceName) { - final String filterName = TransactionControlFilter.class.getName(); - String policyClsName = Configuration.INSTANCE.getFlowsplitFilterPolicy(microserviceName); - if (policyClsName.isEmpty()) { - return; - } - try { - Class policyCls = Class.forName(policyClsName); - if (!TransactionControlFilter.class.isAssignableFrom(policyCls)) { - String errMsg = String.format( - "Define instance filter %s in yaml, but not extends abstract class TransactionControlFilter.", - policyClsName); - LOGGER.error(errMsg); - throw new Error(errMsg); - } - TransactionControlFilter transactionControlFilter = (TransactionControlFilter) policyCls.newInstance(); - transactionControlFilter.setLoadBalancerStats(lb.getLoadBalancerStats()); - lb.putFilter(filterName, transactionControlFilter); - } catch (Throwable e) { - String errMsg = "Fail to create instance of class: " + policyClsName; - LOGGER.error(errMsg); - throw new Error(errMsg, e); - } - } - - private void send(Invocation invocation, AsyncResponse asyncResp, final LoadBalancer choosenLB) throws Exception { - long time = System.currentTimeMillis(); - CseServer server = (CseServer) choosenLB.chooseServer(invocation); - if (null == server) { - asyncResp.consumerFail(ExceptionUtils.lbAddressNotFound(invocation.getMicroserviceName(), - invocation.getMicroserviceVersionRule(), - invocation.getConfigTransportName())); - return; - } - server.setLastVisitTime(time); - choosenLB.getLoadBalancerStats().incrementNumRequests(server); - invocation.setEndpoint(server.getEndpoint()); - invocation.next(resp -> { - // this stats is for WeightedResponseTimeRule - choosenLB.getLoadBalancerStats().noteResponseTime(server, (System.currentTimeMillis() - time)); - if (resp.isFailed()) { - choosenLB.getLoadBalancerStats().incrementSuccessiveConnectionFailureCount(server); - } else { - choosenLB.getLoadBalancerStats().incrementActiveRequestsCount(server); - } - asyncResp.handle(resp); - }); - } - - private void sendWithRetry(Invocation invocation, AsyncResponse asyncResp, - final LoadBalancer choosenLB) throws Exception { - long time = System.currentTimeMillis(); - // retry in loadbalance, 2.0 feature - final int currentHandler = invocation.getHandlerIndex(); - - final SyncResponseExecutor orginExecutor; - final Executor newExecutor; - if (invocation.getResponseExecutor() instanceof SyncResponseExecutor) { - orginExecutor = (SyncResponseExecutor) invocation.getResponseExecutor(); - newExecutor = new Executor() { - @Override - public void execute(Runnable command) { - // retry的场景,对于同步调用, 同步调用的主线程已经被挂起,无法再主线程中进行重试; - // 重试也不能在网络线程(event-loop)中进行,未被保护的阻塞操作会导致网络线程挂起 - RETRY_POOL.submit(command); - } - }; - invocation.setResponseExecutor(newExecutor); - } else { - orginExecutor = null; - newExecutor = null; - } - - ExecutionListener listener = new ExecutionListener() { - @Override - public void onExecutionStart(ExecutionContext context) throws AbortExecutionException { - } - - @Override - public void onStartWithServer(ExecutionContext context, - ExecutionInfo info) throws AbortExecutionException { - } - - @Override - public void onExceptionWithServer(ExecutionContext context, Throwable exception, - ExecutionInfo info) { - LOGGER.error("onExceptionWithServer msg {}; server {}", - exception.getMessage(), - context.getRequest().getEndpoint()); - } - - @Override - public void onExecutionSuccess(ExecutionContext context, Response response, - ExecutionInfo info) { - if (orginExecutor != null) { - orginExecutor.execute(() -> { - asyncResp.complete(response); - }); - } else { - asyncResp.complete(response); - } - } - - @Override - public void onExecutionFailed(ExecutionContext context, Throwable finalException, - ExecutionInfo info) { - if (orginExecutor != null) { - orginExecutor.execute(() -> { - asyncResp.consumerFail(finalException); - }); - } else { - asyncResp.consumerFail(finalException); - } - } - }; - List> listeners = new ArrayList<>(0); - listeners.add(listener); - ExecutionContext context = new ExecutionContext<>(invocation, null, null, null); - - LoadBalancerCommand command = LoadBalancerCommand.builder() - .withLoadBalancer(choosenLB) - .withServerLocator(invocation) - .withRetryHandler(new DefaultLoadBalancerRetryHandler( - Configuration.INSTANCE.getRetryOnSame(invocation.getMicroserviceName()), - Configuration.INSTANCE.getRetryOnNext(invocation.getMicroserviceName()), true)) - .withListeners(listeners) - .withExecutionContext(context) - .build(); - - Observable observable = command.submit(new ServerOperation() { - public Observable call(Server s) { - return Observable.create(f -> { - try { - ((CseServer) s).setLastVisitTime(time); - choosenLB.getLoadBalancerStats().incrementNumRequests(s); - invocation.setHandlerIndex(currentHandler); // for retry - invocation.setEndpoint(((CseServer) s).getEndpoint()); - invocation.next(resp -> { - if (resp.isFailed()) { - LOGGER.error("service call error, msg is {}, server is {} ", - ((Throwable) resp.getResult()).getMessage(), - s); - choosenLB.getLoadBalancerStats().incrementSuccessiveConnectionFailureCount(s); - f.onError(resp.getResult()); - } else { - choosenLB.getLoadBalancerStats().incrementActiveRequestsCount(s); - choosenLB.getLoadBalancerStats().noteResponseTime(s, - (System.currentTimeMillis() - time)); - f.onNext(resp); - f.onCompleted(); - } - }); - } catch (Exception e) { - LOGGER.error("execution error, msg is " + e.getMessage()); - f.onError(e); - } - }); - } - }); - - observable.subscribe(response -> { - }, error -> { - }, () -> { - }); - } - - private LoadBalancer createLoadBalancer(String appId, String microserviceName, String microserviceVersionRule, - String transportName) { - IRule rule; - try { - rule = (IRule) Class.forName(policy, true, Thread.currentThread().getContextClassLoader()).newInstance(); - LOGGER.info("Using loadbalance rule [{}] for service [{},{}].", - policy, - microserviceName, - microserviceVersionRule); - } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { - LOGGER.warn("Loadbalance rule [{}] is incorrect, using default RoundRobinRule.", policy); - rule = new RoundRobinRule(); - } - - CseServerList serverList = new CseServerList(appId, microserviceName, - microserviceVersionRule, transportName); - LoadBalancer lb = new LoadBalancer(serverList, rule); - - loadServerListFilters(lb); - // tow lines below is for compatibility, will remove in future - setIsolationFilter(lb, microserviceName); - setTransactionControlFilter(lb, microserviceName); - - return lb; - } - - private void loadServerListFilters(LoadBalancer lb) { - String filterNames = Configuration.getStringProperty(null, Configuration.SERVER_LIST_FILTERS); - if (!StringUtils.isEmpty(filterNames)) { - for (String filter : filterNames.split(",")) { - loadFilter(filter, lb); - } - } - } - - private void loadFilter(String filter, LoadBalancer lb) { - String className = Configuration.getStringProperty(null, - String.format(Configuration.SERVER_LIST_FILTER_CLASS_HOLDER, filter)); - if (!StringUtils.isEmpty(className)) { - try { - Class filterClass = Class.forName(className, true, Thread.currentThread().getContextClassLoader()); - if (ServerListFilterExt.class.isAssignableFrom(filterClass)) { - ServerListFilterExt ext = (ServerListFilterExt) filterClass.newInstance(); - ext.setName(filter); - ext.setLoadBalancer(lb); - lb.putFilter(filter, ext); - } - } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { - LOGGER.warn("Unable to load filter class: " + className); - } - } - } -} diff --git a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/ServerListCache.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/ServerListCache.java deleted file mode 100644 index c8cee00077c..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/ServerListCache.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import com.netflix.loadbalancer.Server; - -import io.servicecomb.core.Transport; -import io.servicecomb.core.endpoint.AbstractEndpointsCache; -import io.servicecomb.serviceregistry.cache.CacheEndpoint; - -public class ServerListCache extends AbstractEndpointsCache { - - public ServerListCache(String appId, String microserviceName, String microserviceVersionRule, - String transportName) { - super(appId, microserviceName, microserviceVersionRule, transportName); - } - - @Override - protected Server createEndpoint(Transport transport, CacheEndpoint cacheEndpoint) { - return new CseServer(transport, cacheEndpoint); - } -} diff --git a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/ServerListFilterExt.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/ServerListFilterExt.java deleted file mode 100644 index 167767e4e5e..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/ServerListFilterExt.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerListFilter; - -import io.servicecomb.core.Invocation; - -/** - * 通过实现这个接口实现ServerListFilter扩展 - * - */ -public interface ServerListFilterExt extends ServerListFilter { - default void setName(String name) { - - } - - default void setLoadBalancer(LoadBalancer lb) { - - } - - default void setInvocation(Invocation invocation) { - - } -} diff --git a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/SessionStickinessRule.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/SessionStickinessRule.java deleted file mode 100644 index a0a4b18babe..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/SessionStickinessRule.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.loadbalancer.AbstractLoadBalancer; -import com.netflix.loadbalancer.ILoadBalancer; -import com.netflix.loadbalancer.IRule; -import com.netflix.loadbalancer.LoadBalancerStats; -import com.netflix.loadbalancer.RoundRobinRule; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerStats; - -/** - * 会话保持策略:优先选择上一次选中的服务器,保证请求都发送到同一个服务器上去。 - * 提供当会话过期或者失败次数超过限制后,轮询选择其他服务器的能力。 - * - */ -public class SessionStickinessRule implements IRule { - private static final Logger LOG = LoggerFactory.getLogger(SessionStickinessRule.class); - - private final Object lock = new Object(); - - private ILoadBalancer lb; - - // use random rule as the trigger rule, to prevent consumer instance select the same producer instance. - private IRule triggerRule; - - private volatile Server lastServer = null; - - private long lastAccessedTime = 0; - - private volatile boolean errorThresholdMet = false; - - private static final int MILLI_COUNT_IN_SECOND = 1000; - - public SessionStickinessRule() { - triggerRule = new RoundRobinRule(); - } - - private Server chooseNextServer(Object key) { - AbstractLoadBalancer lb = (AbstractLoadBalancer) getLoadBalancer(); - triggerRule.setLoadBalancer(lb); - lastServer = triggerRule.choose(key); - lastAccessedTime = System.currentTimeMillis(); - return lastServer; - } - - private Server chooseInitialServer(Object key) { - synchronized (lock) { - if (lastServer == null) { - chooseNextServer(key); - } - } - return lastServer; - } - - private Server chooseServerWhenTimeout(Object key) { - synchronized (lock) { - if (isTimeOut()) { - chooseNextServer(key); - } - } - return lastServer; - } - - private Server chooseServerErrorThresholdMet(Object key) { - synchronized (lock) { - if (errorThresholdMet) { - chooseNextServer(key); - errorThresholdMet = false; - } - } - return lastServer; - } - - private boolean isTimeOut() { - return Configuration.INSTANCE.getSessionTimeoutInSeconds() > 0 - && System.currentTimeMillis() - - this.lastAccessedTime > ((long) Configuration.INSTANCE.getSessionTimeoutInSeconds() - * MILLI_COUNT_IN_SECOND); - } - - private boolean isErrorThresholdMet() { - AbstractLoadBalancer lb = (AbstractLoadBalancer) getLoadBalancer(); - LoadBalancerStats stats = lb.getLoadBalancerStats(); - - if (stats != null && stats.getServerStats() != null && stats.getServerStats().size() > 0) { - ServerStats serverStats = stats.getSingleServerStat(lastServer); - int successiveFaildCount = serverStats.getSuccessiveConnectionFailureCount(); - if (Configuration.INSTANCE.getSuccessiveFailedTimes() > 0 - && successiveFaildCount >= Configuration.INSTANCE.getSuccessiveFailedTimes()) { - serverStats.clearSuccessiveConnectionFailureCount(); - return true; - } - } - return false; - } - - @Override - public Server choose(Object key) { - if (lastServer == null) { - return chooseInitialServer(key); - } - - if (isTimeOut()) { - LOG.warn("session timeout. choose another server."); - return chooseServerWhenTimeout(key); - } else { - this.lastAccessedTime = System.currentTimeMillis(); - } - - if (isErrorThresholdMet()) { - LOG.warn("reached max error. choose another server."); - errorThresholdMet = true; - return chooseServerErrorThresholdMet(key); - } - - return lastServer; - } - - @Override - public void setLoadBalancer(ILoadBalancer lb) { - this.lb = lb; - } - - @Override - public ILoadBalancer getLoadBalancer() { - return this.lb; - } -} diff --git a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/exception/LoadbalanceExceptionUtils.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/exception/LoadbalanceExceptionUtils.java deleted file mode 100644 index c4698cfe0d1..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/exception/LoadbalanceExceptionUtils.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance.exception; - -import io.servicecomb.core.exception.CseException; -import io.servicecomb.core.exception.ExceptionUtils; - -public class LoadbalanceExceptionUtils extends ExceptionUtils { - - public static final String CSE_HANDLER_LB_WRONG_RULE = "cse.handler.lb.wrong.rule"; - - static { - ERROR_DESC_MGR.register(CSE_HANDLER_LB_WRONG_RULE, "Configured rule name is wrong."); - } - - public static CseException createLoadbalanceException(String code, Throwable cause, Object... args) { - String msg = String.format(ERROR_DESC_MGR.ensureFindValue(code), args); - CseException exception = new CseException(code, msg, cause); - return exception; - } -} diff --git a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/filter/IsolationServerListFilter.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/filter/IsolationServerListFilter.java deleted file mode 100644 index 0d2be530a47..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/filter/IsolationServerListFilter.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance.filter; - -import java.util.ArrayList; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.loadbalancer.LoadBalancerStats; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerStats; - -import io.servicecomb.core.Invocation; -import io.servicecomb.loadbalance.Configuration; -import io.servicecomb.loadbalance.CseServer; -import io.servicecomb.loadbalance.ServerListFilterExt; - -public final class IsolationServerListFilter implements ServerListFilterExt { - - private static final Logger LOGGER = LoggerFactory.getLogger(IsolationServerListFilter.class); - - private static final double PERCENT = 100; - - private String microserviceName; - - private int errorThresholdPercentage; - - private long singleTestTime; - - private long enableRequestThreshold; - - private Invocation invocation; - - private LoadBalancerStats stats; - - public void setInvocation(Invocation invocation) { - this.invocation = invocation; - } - - public void setLoadBalancerStats(LoadBalancerStats stats) { - this.stats = stats; - } - - public LoadBalancerStats getLoadBalancerStats() { - return stats; - } - - public String getMicroserviceName() { - return microserviceName; - } - - public void setMicroserviceName(String microserviceName) { - this.microserviceName = microserviceName; - } - - @Override - public List getFilteredListOfServers(List servers) { - if (!Configuration.INSTANCE.isIsolationFilterOpen(invocation.getMicroserviceName())) { - return servers; - } - - List filteredServers = new ArrayList(); - for (Server server : servers) { - if (allowVisit(server)) { - filteredServers.add(server); - } - } - return filteredServers; - } - - private void updateSettings() { - errorThresholdPercentage = Configuration.INSTANCE.getErrorThresholdPercentage(microserviceName); - singleTestTime = Configuration.INSTANCE.getSingleTestTime(microserviceName); - enableRequestThreshold = Configuration.INSTANCE.getEnableRequestThreshold(microserviceName); - } - - private boolean allowVisit(Server server) { - updateSettings(); - ServerStats serverStats = stats.getSingleServerStat(server); - long totalRequest = serverStats.getTotalRequestsCount(); - long failureRequest = serverStats.getSuccessiveConnectionFailureCount(); - - if (totalRequest < enableRequestThreshold) { - return true; - } - - if ((failureRequest / (double) totalRequest) * PERCENT < errorThresholdPercentage) { - return true; - } - if ((System.currentTimeMillis() - ((CseServer) server).getLastVisitTime()) > singleTestTime) { - LOGGER.info("The Service {}'s instance {} has been break, will give a single test opportunity.", - microserviceName, - server); - return true; - } - LOGGER.warn("The Service {}'s instance {} has been break!", microserviceName, server); - return false; - } -} diff --git a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/filter/SimpleTransactionControlFilter.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/filter/SimpleTransactionControlFilter.java deleted file mode 100644 index 5322f27ba28..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/filter/SimpleTransactionControlFilter.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance.filter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import com.netflix.loadbalancer.Server; - -import io.servicecomb.loadbalance.Configuration; -import io.servicecomb.loadbalance.CseServer; - -/** - * 简单的分流filter - * 策略:选择properties包含filter的所有options的所有实例,即filter的options为所选实例的properties的一个子集 - */ -public class SimpleTransactionControlFilter extends TransactionControlFilter { - - @Override - public List getFilteredListOfServers(List servers) { - List filteredServers = new ArrayList<>(); - Map filterOptions = - Configuration.INSTANCE.getFlowsplitFilterOptions(getInvocation().getMicroserviceName()); - for (Server server : servers) { - if (allowVisit((CseServer) server, filterOptions)) { - filteredServers.add(server); - } - } - return filteredServers; - } - - protected boolean allowVisit(CseServer server, Map filterOptions) { - Map propertiesMap = server.getInstance().getProperties(); - for (Entry entry : filterOptions.entrySet()) { - if (!entry.getValue().equals(propertiesMap.get(entry.getKey()))) { - return false; - } - } - return true; - } -} diff --git a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/filter/TransactionControlFilter.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/filter/TransactionControlFilter.java deleted file mode 100644 index c9398c6a081..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/filter/TransactionControlFilter.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance.filter; - -import com.netflix.loadbalancer.LoadBalancerStats; - -import io.servicecomb.core.Invocation; -import io.servicecomb.loadbalance.ServerListFilterExt; - -public abstract class TransactionControlFilter implements ServerListFilterExt { - - private Invocation invocation; - - private LoadBalancerStats stats; - - public void setLoadBalancerStats(LoadBalancerStats stats) { - this.stats = stats; - } - - public LoadBalancerStats getLoadBalancerStats() { - return stats; - } - - public Invocation getInvocation() { - return invocation; - } - - public void setInvocation(Invocation invocation) { - this.invocation = invocation; - } -} diff --git a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/filter/ZoneAwareServerListFilterExt.java b/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/filter/ZoneAwareServerListFilterExt.java deleted file mode 100644 index cf5cb202fc3..00000000000 --- a/handlers/handler-loadbalance/src/main/java/io/servicecomb/loadbalance/filter/ZoneAwareServerListFilterExt.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance.filter; - -import java.util.ArrayList; -import java.util.List; - -import com.netflix.loadbalancer.Server; - -import io.servicecomb.loadbalance.CseServer; -import io.servicecomb.loadbalance.ServerListFilterExt; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; - -public class ZoneAwareServerListFilterExt implements ServerListFilterExt { - - @Override - public List getFilteredListOfServers(List list) { - List result = new ArrayList<>(); - MicroserviceInstance myself = RegistryUtils.getMicroserviceInstance(); - boolean find = false; - for (Server server : list) { - CseServer cseServer = (CseServer) server; - if (regionAndAZMatch(myself, cseServer.getInstance())) { - result.add(cseServer); - find = true; - } - } - - if (!find) { - for (Server server : list) { - CseServer cseServer = (CseServer) server; - if (regionMatch(myself, cseServer.getInstance())) { - result.add(cseServer); - find = true; - } - } - } - - if (!find) { - result = list; - } - return result; - } - - private boolean regionAndAZMatch(MicroserviceInstance myself, MicroserviceInstance target) { - if (myself.getDataCenterInfo() != null && 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 (myself.getDataCenterInfo() != null && target.getDataCenterInfo() != null) { - return myself.getDataCenterInfo().getRegion().equals(target.getDataCenterInfo().getRegion()); - } - return false; - } -} 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 new file mode 100644 index 00000000000..87a527c9aff --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/Configuration.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.Map; + +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 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."; + + // SessionStickinessRule configuration + public static final String SESSION_TIMEOUT_IN_SECONDS = "SessionStickinessRule.sessionTimeoutInSeconds"; + + public static final String SUCCESSIVE_FAILED_TIMES = "SessionStickinessRule.successiveFailedTimes"; + + public static final String FILTER_ISOLATION = "isolation."; + + public static final String FILTER_MAX_SINGLE_TEST_WINDOW = "maxSingleTestWindow"; + + 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 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) { + final int defaultValue = 30; + String p = getStringProperty("30", + ROOT + microservice + "." + SESSION_TIMEOUT_IN_SECONDS, + ROOT + SESSION_TIMEOUT_IN_SECONDS); + try { + return Integer.parseInt(p); // can be negative + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public int getSuccessiveFailedTimes(String microservice) { + final int defaultValue = 5; + String p = getStringProperty("5", + ROOT + microservice + "." + SUCCESSIVE_FAILED_TIMES, + ROOT + SUCCESSIVE_FAILED_TIMES); + try { + return Integer.parseInt(p); // can be negative + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public int getMaxSingleTestWindow() { + final int defaultValue = 60000; + String p = getStringProperty(Integer.toString(defaultValue), + ROOT + FILTER_ISOLATION + FILTER_MAX_SINGLE_TEST_WINDOW); + try { + int result = Integer.parseInt(p); + if (result >= 0) { + return result; + } + return defaultValue; + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public Map getFlowsplitFilterOptions(String microservice) { + String keyPrefix = String.format(TRANSACTIONCONTROL_OPTIONS_PREFIX_PATTERN, microservice); + return ConfigUtil.stringPropertiesWithPrefix(LegacyPropertyFactory.getEnvironment(), keyPrefix); + } + + public static String getStringProperty(String defaultValue, String... keys) { + String property; + for (String key : keys) { + property = LegacyPropertyFactory.getStringProperty(key); + if (property != null) { + return property; + } + } + 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 new file mode 100644 index 00000000000..dc4cd6b6082 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsFactory.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.loadbalance; + +/** + * By implements ExtensionsFactory, users can add new extends for rules, filters, etc. + */ +public interface ExtensionsFactory { + boolean isSupport(String key, String value); + + default RuleExt createLoadBalancerRule(String ruleName) { + 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 new file mode 100644 index 00000000000..2187f59e789 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsManager.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.loadbalance; + +import java.util.List; + +public class ExtensionsManager { + private final List extensionsFactories; + + public ExtensionsManager(List extensionsFactories) { + this.extensionsFactories = extensionsFactories; + } + + public RuleExt createLoadBalancerRule(String ruleStrategyName) { + RuleExt rule = null; + + for (ExtensionsFactory factory : extensionsFactories) { + if (factory.isSupport(Configuration.RULE_STRATEGY_NAME, ruleStrategyName)) { + rule = factory.createLoadBalancerRule(ruleStrategyName); + break; + } + } + + if (rule == null) { + rule = new RoundRobinRuleExt(); + } + + 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 new file mode 100644 index 00000000000..d65726e0ada --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceConfiguration.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.loadbalance; + +import java.util.List; + +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; + +@Configuration +@ConditionalOnProperty(value = LoadBalanceConfiguration.LOAD_BALANCE_ENABLED, + havingValue = "true", matchIfMissing = true) +public class LoadBalanceConfiguration { + public static final String LOAD_BALANCE_PREFIX = "servicecomb.loadbalance"; + + public static final String LOAD_BALANCE_ENABLED = LOAD_BALANCE_PREFIX + ".enabled"; + + @Bean + public LoadBalanceFilter scbLoadBalanceFilter(ExtensionsManager extensionsManager, DiscoveryTree discoveryTree, + SCBEngine scbEngine) { + return new LoadBalanceFilter(extensionsManager, discoveryTree, scbEngine); + } + + @Bean + public RuleNameExtensionsFactory scbRuleNameExtensionsFactory() { + return new RuleNameExtensionsFactory(); + } + + @Bean + 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 new file mode 100644 index 00000000000..e97263ac465 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceFilter.java @@ -0,0 +1,301 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.net.URI; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +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.foundation.common.event.EventManager; +import org.apache.servicecomb.loadbalance.Configuration.RuleType; +import org.apache.servicecomb.registry.discovery.DiscoveryContext; +import org.apache.servicecomb.registry.discovery.DiscoveryTree; +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.google.common.eventbus.Subscribe; + +import io.github.resilience4j.core.metrics.Metrics.Outcome; +import jakarta.ws.rs.core.Response.Status; + +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. + private static final int COUNT = 17; + + public static final String CONTEXT_KEY_SERVER_LIST = "x-context-server-list"; + + public static final String SERVICECOMB_SERVER_ENDPOINT = "scb-endpoint"; + + private static final Logger LOGGER = LoggerFactory.getLogger(LoadBalanceFilter.class); + + private final DiscoveryTree discoveryTree; + + // key为grouping filter qualified name + private final Map loadBalancerMap = new ConcurrentHashMapEx<>(); + + private final Object lock = new Object(); + + private final ExtensionsManager extensionsManager; + + private final SCBEngine scbEngine; + + // 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); + } + + @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(SCBEngine scbEngine) { + // Old configurations check.Just print an error, because configurations may given in dynamic and fail on runtime. + 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 = scbEngine.getEnvironment() + .getProperty("servicecomb.loadbalance.serverListFilters"); + if (!StringUtils.isEmpty(filterNames)) { + LOGGER.error( + "Server list implementation changed to SPI. Configuration [servicecomb.loadbalance.serverListFilters]" + + " is not used any more. For ServiceComb defined filters, you do not need config and can " + + "remove this configuration safely. If you define your own filter, need to change it to SPI to make it work."); + } + } + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER; + } + + @Override + public String getName() { + return "load-balance"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + try { + if (handleSuppliedEndpoint(invocation)) { + invocation.addLocalContext(RetryContext.RETRY_LOAD_BALANCE, false); + return nextNode.onFilter(invocation); + } + } catch (Exception e) { + return CompletableFuture.failedFuture(e); + } + + invocation.addLocalContext(RetryContext.RETRY_LOAD_BALANCE, true); + + LoadBalancer loadBalancer = getOrCreateLoadBalancer(invocation); + + return send(invocation, nextNode, loadBalancer); + } + + // user's can invoke a service by supplying target Endpoint. + // 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; + } + + if (supportDefinedEndpoint) { + return defineEndpointAndHandle(invocation); + } + + return false; + } + + private Endpoint parseEndpoint(String endpointUri) throws Exception { + URI formatUri = new URI(endpointUri); + 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, + "the endpoint's transport is not found."); + } + return new Endpoint(transport, endpointUri); + } + + private boolean defineEndpointAndHandle(Invocation invocation) throws Exception { + Object endpoint = invocation.getLocalContext(SERVICECOMB_SERVER_ENDPOINT); + if (endpoint == null) { + return false; + } + if (endpoint instanceof String) { + // compatible to old usage + endpoint = parseEndpoint((String) endpoint); + } + + invocation.setEndpoint((Endpoint) endpoint); + return true; + } + + private void clearLoadBalancer() { + loadBalancerMap.clear(); + } + + @VisibleForTesting + CompletableFuture send(Invocation invocation, FilterNode filterNode, LoadBalancer chosenLB) { + long time = System.currentTimeMillis(); + ServiceCombServer server = chooseServer(invocation, chosenLB); + if (null == server) { + return CompletableFuture.failedFuture( + new InvocationException(Status.INTERNAL_SERVER_ERROR, + String.format("No available address found for %s/%s.", invocation.getAppId(), + invocation.getMicroserviceName()))); + } + invocation.setEndpoint(server.getEndpoint()); + return filterNode.onFilter(invocation).whenComplete((r, e) -> { + if (e != null || isFailedResponse(r)) { + server.getServerMetrics().record((System.currentTimeMillis() - time), TimeUnit.MILLISECONDS, Outcome.ERROR); + } else { + server.getServerMetrics().record((System.currentTimeMillis() - time), TimeUnit.MILLISECONDS, Outcome.SUCCESS); + } + }); + } + + private ServiceCombServer chooseServer(Invocation invocation, LoadBalancer chosenLB) { + RetryContext retryContext = invocation.getLocalContext(RetryContext.RETRY_CONTEXT); + if (retryContext == null) { + return chosenLB.chooseServer(invocation); + } + + if (!retryContext.isRetry()) { + ServiceCombServer server = chosenLB.chooseServer(invocation); + invocation.addLocalContext(CONTEXT_KEY_LAST_SERVER, server); + return server; + } + + ServiceCombServer lastServer = invocation.getLocalContext(CONTEXT_KEY_LAST_SERVER); + ServiceCombServer nextServer = lastServer; + if (!retryContext.trySameServer()) { + for (int i = 0; i < COUNT; i++) { + ServiceCombServer s = chosenLB.chooseServer(invocation); + if (s == null) { + break; + } + if (!s.equals(nextServer)) { + nextServer = s; + break; + } + } + } + + LOGGER.warn("operation failed {}, retry to instance [{}], last instance [{}], trace id {}", + invocation.getMicroserviceQualifiedName(), + nextServer == null ? "" : nextServer.getEndpoint().getEndpoint(), + lastServer == null ? "" : nextServer.getEndpoint().getEndpoint(), + invocation.getTraceId()); + invocation.addLocalContext(CONTEXT_KEY_LAST_SERVER, nextServer); + return nextServer; + } + + protected boolean isFailedResponse(Response resp) { + if (resp.isFailed()) { + if (resp.getResult() instanceof InvocationException) { + InvocationException e = resp.getResult(); + return e.getStatusCode() == ExceptionFactory.CONSUMER_INNER_STATUS_CODE + || e.getStatusCode() == Status.SERVICE_UNAVAILABLE.getStatusCode() + || e.getStatusCode() == Status.REQUEST_TIMEOUT.getStatusCode(); + } else { + return true; + } + } else { + return false; + } + } + + protected LoadBalancer getOrCreateLoadBalancer(Invocation invocation) { + DiscoveryContext context = new DiscoveryContext(); + context.setInputParameters(invocation); + VersionedCache serversVersionedCache = discoveryTree.discovery(context, + invocation.getAppId(), + invocation.getMicroserviceName()); + invocation.addLocalContext(CONTEXT_KEY_SERVER_LIST, serversVersionedCache.data()); + + 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(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 new file mode 100644 index 00000000000..d60581ff5f0 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalancer.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.loadbalance; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; + +/** + * A load balancer with RuleExt and ServerListFilterExt + */ +public class LoadBalancer { + private static final Logger LOGGER = LoggerFactory.getLogger(LoadBalancer.class); + + private static final AtomicInteger id = new AtomicInteger(0); + + private final RuleExt rule; + + private final String microServiceName; + + private List filters; + + public LoadBalancer(RuleExt rule, String microServiceName) { + this.microServiceName = microServiceName; + this.rule = rule; + // load new instances, because filters work on service information + this.filters = SPIServiceUtils.loadSortedService(ServerListFilterExt.class); + this.rule.setLoadBalancer(this); + this.filters.forEach((item) -> item.setLoadBalancer(this)); + } + + public ServiceCombServer chooseServer(Invocation invocation) { + List servers = invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST); + int serversCount = servers.size(); + for (ServerListFilterExt filterExt : filters) { + if (!filterExt.enabled()) { + continue; + } + servers = filterExt.getFilteredListOfServers(servers, invocation); + if (servers.isEmpty() && serversCount > 0) { + LOGGER.warn("There are not servers exist after filtered by {}.", filterExt.getClass()); + break; + } + } + ServiceCombServer server = rule.choose(servers, invocation); + if (null == server) { + return null; + } + return server; + } + + public String getMicroServiceName() { + return microServiceName; + } + + @VisibleForTesting + void setFilters(List filters) { + this.filters = filters; + } +} 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 new file mode 100644 index 00000000000..318b45f3839 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RandomRuleExt.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.loadbalance; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +import org.apache.servicecomb.core.Invocation; + +/** + * A random rule. + */ +public class RandomRuleExt implements RuleExt { + @Override + public ServiceCombServer choose(List servers, Invocation invocation) { + if (servers.isEmpty()) { + return null; + } + int index = ThreadLocalRandom.current().nextInt(servers.size()); + return servers.get(index); + } +} diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RoundRobinRuleExt.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RoundRobinRuleExt.java new file mode 100644 index 00000000000..70d467843f3 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RoundRobinRuleExt.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.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.core.Invocation; + +/** + * A round robin rule + */ +public class RoundRobinRuleExt implements RuleExt { + private final AtomicInteger counter = new AtomicInteger(0); + + @Override + public ServiceCombServer choose(List servers, Invocation invocation) { + if (servers.isEmpty()) { + return null; + } + int index = Math.abs(counter.getAndIncrement()) % servers.size(); + return servers.get(index); + } +} diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleExt.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleExt.java new file mode 100644 index 00000000000..a95ccbcd236 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleExt.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.loadbalance; + +import java.util.List; + +import org.apache.servicecomb.core.Invocation; + +/** + * Load balance rule to support invocation based filters. + */ +public interface RuleExt { + default void setLoadBalancer(LoadBalancer loadBalancer) { + } + + ServiceCombServer choose(List servers, Invocation invocation); +} 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/ServerListFilterExt.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServerListFilterExt.java new file mode 100644 index 00000000000..9d2d07ceb5f --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServerListFilterExt.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.loadbalance; + +import java.util.List; + +import org.apache.servicecomb.core.Invocation; + +/** + * Base interface for server list filters. + * + * Robin ServerListFilter can not support invocation based filter strategies, so we create a new one to + * support this. + */ +public interface ServerListFilterExt { + int ORDER_NORMAL = 0; + + default int getOrder() { + return ORDER_NORMAL; + } + + default boolean enabled() { + return true; + } + + default void setLoadBalancer(LoadBalancer loadBalancer) { + } + + List getFilteredListOfServers(List servers, Invocation invocation); +} 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 new file mode 100644 index 00000000000..edfdf322f6e --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombServer.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.loadbalance; + +import java.util.Objects; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.registry.api.DiscoveryInstance; + +/** + * Server object used for transports and load balancer. + * + */ +public class ServiceCombServer { + private final Endpoint endpoint; + + private final String microserviceName; + + private final ServerMetrics serverMetrics; + + public ServiceCombServer(String microserviceName, Endpoint endpoint) { + this.microserviceName = microserviceName; + this.endpoint = endpoint; + this.serverMetrics = new ServerMetrics(); + } + + public String getMicroserviceName() { + return this.microserviceName; + } + + public Endpoint getEndpoint() { + return endpoint; + } + + public DiscoveryInstance getInstance() { + return endpoint.getMicroserviceInstance(); + } + + @Override + public String toString() { + return endpoint.getEndpoint(); + } + + public String getHost() { + return endpoint.getEndpoint(); + } + + public ServerMetrics getServerMetrics() { + return serverMetrics; + } + + @Override + public boolean equals(Object o) { + if (o instanceof ServiceCombServer) { + return this.getInstance().getInstanceId() + .equals(((ServiceCombServer) o).getInstance().getInstanceId()) + && StringUtils.equals(endpoint.getEndpoint(), ((ServiceCombServer) o).getEndpoint().getEndpoint()); + } else { + return false; + } + } + + @Override + public int 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 new file mode 100644 index 00000000000..37d94bc0fa1 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/SessionStickinessRule.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.loadbalance; + +import java.util.List; + +import org.apache.servicecomb.core.Invocation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; + +/** + * 会话保持策略:优先选择上一次选中的服务器,保证请求都发送到同一个服务器上去。 + * 提供当会话过期或者失败次数超过限制后,轮询选择其他服务器的能力。 + * + */ +public class SessionStickinessRule implements RuleExt { + private static final Logger LOG = LoggerFactory.getLogger(SessionStickinessRule.class); + + private final Object lock = new Object(); + + // 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 volatile long lastAccessedTime = 0; + + private volatile boolean errorThresholdMet = false; + + private static final int MILLI_COUNT_IN_SECOND = 1000; + + private String microserviceName; + + public SessionStickinessRule() { + triggerRule = new RoundRobinRuleExt(); + } + + @Override + public void setLoadBalancer(LoadBalancer loadBalancer) { + this.microserviceName = loadBalancer.getMicroServiceName(); + } + + private ServiceCombServer chooseNextServer(List servers, Invocation invocation) { + lastServer = triggerRule.choose(servers, invocation); + lastAccessedTime = System.currentTimeMillis(); + return lastServer; + } + + private ServiceCombServer chooseInitialServer(List servers, Invocation invocation) { + synchronized (lock) { + if (lastServer == null) { + chooseNextServer(servers, invocation); + } + } + return lastServer; + } + + @VisibleForTesting + ServiceCombServer chooseServerWhenTimeout(List servers, Invocation invocation) { + synchronized (lock) { + if (isTimeOut()) { + chooseNextServer(servers, invocation); + } + } + return lastServer; + } + + private ServiceCombServer chooseServerErrorThresholdMet(List servers, Invocation invocation) { + synchronized (lock) { + if (errorThresholdMet) { + chooseNextServer(servers, invocation); + errorThresholdMet = false; + } + } + return lastServer; + } + + private boolean isTimeOut() { + return Configuration.INSTANCE.getSessionTimeoutInSeconds(microserviceName) > 0 + && System.currentTimeMillis() + - this.lastAccessedTime > ((long) Configuration.INSTANCE.getSessionTimeoutInSeconds(microserviceName) + * MILLI_COUNT_IN_SECOND); + } + + private boolean isErrorThresholdMet(ServiceCombServer server) { + int successiveFailedCount = server.getServerMetrics().getSnapshot().getNumberOfFailedCalls(); + return Configuration.INSTANCE.getSuccessiveFailedTimes(microserviceName) > 0 + && successiveFailedCount >= Configuration.INSTANCE.getSuccessiveFailedTimes(microserviceName); + } + + @Override + public ServiceCombServer choose(List servers, Invocation invocation) { + if (lastServer == null) { + return chooseInitialServer(servers, invocation); + } + + if (isTimeOut()) { + LOG.warn("session timeout. choose another server."); + return chooseServerWhenTimeout(servers, invocation); + } else { + this.lastAccessedTime = System.currentTimeMillis(); + } + + if (isErrorThresholdMet(lastServer)) { + LOG.warn("reached max error. choose another server."); + errorThresholdMet = true; + return chooseServerErrorThresholdMet(servers, invocation); + } + + if (!servers.contains(lastServer)) { + return chooseNextServer(servers, invocation); + } + + return lastServer; + } +} 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 new file mode 100644 index 00000000000..a6462876e93 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/WeightedResponseTimeRuleExt.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.core.Invocation; + +/** + * Rule based on response time. + */ +public class WeightedResponseTimeRuleExt extends RoundRobinRuleExt { + // when all servers are very fast(less than MIN_GAP), use round-robin rule. + private static final double MIN_GAP = 10d; + + // calculate stats once per RANDOM_PERCENT requests. + private static final int RANDOM_PERCENT = 1000; + + private final Object lock = new Object(); + + private final AtomicInteger counter = new AtomicInteger(0); + + // 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) { + 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()); + for (int i = 0; i < stats.size() - 1; i++) { + weights.add(finalTotal - stats.get(i)); + } + double ran = ThreadLocalRandom.current().nextDouble() * finalTotal * (servers.size() - 1); + for (int i = 0; i < weights.size(); i++) { + ran -= weights.get(i); + if (ran < 0) { + return servers.get(i); + } + } + return servers.get(servers.size() - 1); + } + return super.choose(servers, invocation); + } + + private static List doCalculateTotalWeights(List servers) { + List stats = new ArrayList<>(servers.size() + 1); + double totalWeights = 0; + boolean needRandom = false; + for (ServiceCombServer server : servers) { + // 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; + } + totalWeights += avgTime; + stats.add(avgTime); + } + stats.add(totalWeights); + if (needRandom) { + return stats; + } else { + return new ArrayList<>(); + } + } +} diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/exception/LoadbalanceExceptionUtils.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/exception/LoadbalanceExceptionUtils.java new file mode 100644 index 00000000000..3a4bde530c3 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/exception/LoadbalanceExceptionUtils.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.loadbalance.exception; + +import org.apache.servicecomb.core.exception.CseException; +import org.apache.servicecomb.core.exception.ExceptionUtils; + +public class LoadbalanceExceptionUtils extends ExceptionUtils { + + public static final String CSE_HANDLER_LB_WRONG_RULE = "servicecomb.handler.lb.wrong.rule"; + + static { + ERROR_DESC_MGR.register(CSE_HANDLER_LB_WRONG_RULE, "Configured rule name is wrong."); + } + + public static CseException createLoadbalanceException(String code, Throwable cause, Object... args) { + String msg = String.format(ERROR_DESC_MGR.ensureFindValue(code), args); + return new CseException(code, msg, cause); + } +} 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 new file mode 100644 index 00000000000..2b4c3eb6b20 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/InstancePropertyDiscoveryFilter.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.loadbalance.filter; + +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.discovery.AbstractDiscoveryFilter; +import org.apache.servicecomb.registry.discovery.DiscoveryContext; +import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; + +/** + * 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; + } + + @Override + public boolean enabled() { + + if (enabled == null) { + enabled = dynamicProperties.getBooleanProperty(SERVICECOMB_LOADBALANCE_FILTER_INSTANCE_PROPERTY_ENABLED, + value -> enabled = value, + true); + } + return enabled; + } + + @Override + public boolean isGroupingFilter() { + return false; + } + + @Override + protected void init(DiscoveryContext context, DiscoveryTreeNode parent) { + List matchedInstance = new ArrayList<>(); + Invocation invocation = context.getInputParameters(); + List instances = parent.data(); + Map filterOptions = + Configuration.INSTANCE.getFlowsplitFilterOptions(invocation.getMicroserviceName()); + instances.forEach((target) -> { + if (allowVisit(target, filterOptions)) { + matchedInstance.add(target); + } + }); + parent.child(MATCHED, new DiscoveryTreeNode() + .subName(parent, MATCHED) + .data(matchedInstance)); + } + + @Override + protected String findChildName(DiscoveryContext context, DiscoveryTreeNode parent) { + return MATCHED; + } + + protected boolean allowVisit(StatefulDiscoveryInstance instance, Map filterOptions) { + Map propertiesMap = instance.getProperties(); + for (Entry entry : filterOptions.entrySet()) { + if (!entry.getValue().equals(propertiesMap.get(entry.getKey()))) { + return false; + } + } + return true; + } +} 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 new file mode 100644 index 00000000000..d890b7ea229 --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilter.java @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.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 org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.core.Invocation; +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 jakarta.validation.constraints.NotNull; + +/** + * Instance property with priority filter + */ +public class PriorityInstancePropertyDiscoveryFilter extends AbstractDiscoveryFilter { + + private static final Logger LOGGER = LoggerFactory.getLogger(PriorityInstancePropertyDiscoveryFilter.class); + + 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 = dynamicProperties.getStringProperty(SERVICECOMB_LOADBALANCE_FILTER_PRIORITY_INSTANCE_PROPERTY_KEY, + value -> propertyKey = value, + "environment"); + + // group all instance by property + List instances = parent.data(); + Map> groupByProperty = new HashMap<>(); + for (StatefulDiscoveryInstance microserviceInstance : instances) { + String propertyValue = new PriorityInstanceProperty(propertyKey, microserviceInstance).getPropertyValue(); + groupByProperty.computeIfAbsent(propertyValue, key -> new ArrayList<>()) + .add(microserviceInstance); + } + Map children = new HashMap<>(); + for (Map.Entry> entry : groupByProperty.entrySet()) { + children.put(entry.getKey(), + new DiscoveryTreeNode().subName(parent, entry.getKey()).data(entry.getValue())); + } + children.put(ALL_INSTANCE, new DiscoveryTreeNode().subName(parent, ALL_INSTANCE).data(instances)); + parent.children(children); + } + + @Override + protected String findChildName(DiscoveryContext context, DiscoveryTreeNode parent) { + Invocation invocation = context.getInputParameters(); + + // context property has precedence over instance property + String initPropertyValue = invocation.getContext() + .computeIfAbsent("x-" + propertyKey, + key -> new PriorityInstanceProperty(propertyKey, + BootStrapProperties.readServiceProperties(environment).get(propertyKey)) + .getPropertyValue()); + + PriorityInstanceProperty currentProperty = context.getContextParameter(propertyKey); + // start with initial value, then search with priority + if (Objects.isNull(currentProperty)) { + currentProperty = new PriorityInstanceProperty(propertyKey, initPropertyValue); + while (!parent.children().containsKey(currentProperty.getPropertyValue()) + && currentProperty.hasChildren()) { + currentProperty = currentProperty.child(); + } + } else { + if (currentProperty.hasChildren()) { + currentProperty = currentProperty.child(); + } + } + LOGGER.debug("Discovery instance filter by {}", currentProperty); + context.putContextParameter(propertyKey, currentProperty); + + // stop push filter stack if property is empty + if (currentProperty.isEmpty()) { + return currentProperty.getPropertyValue(); + } + context.pushRerunFilter(); + return currentProperty.getPropertyValue(); + } + + @Override + public boolean enabled() { + + if (enabled == null) { + enabled = dynamicProperties.getBooleanProperty("servicecomb.loadbalance.filter.priorityInstanceProperty.enabled", + value -> enabled = value, + false); + } + return enabled; + } + + @Override + public int getOrder() { + return new InstancePropertyDiscoveryFilter().getOrder() + 1; + } + + static class PriorityInstanceProperty { + private static final int MAX_LENGTH = 10000; + + private static final String SEPARATOR = "."; + + private final String propertyKey; + + private final String propertyVal; + + /** + * Constructor + * + * @param key property key + * @param value property value + */ + public PriorityInstanceProperty(@NotNull String key, String value) { + propertyKey = key; + if (Objects.isNull(value)) { + value = StringUtils.EMPTY; + } + if (value.length() > MAX_LENGTH) { + throw new IllegalArgumentException("property value exceed max length"); + } + propertyVal = value; + } + + /** + * Constructor + * + * @param key property key + * @param microserviceInstance instance + */ + public PriorityInstanceProperty(@NotNull String key, @NotNull StatefulDiscoveryInstance microserviceInstance) { + this(key, Optional.ofNullable(microserviceInstance.getProperties().get(key)) + .orElse(StringUtils.EMPTY)); + } + + /** + * whether property is empty + * + * @return result + */ + public boolean isEmpty() { + return StringUtils.isEmpty(propertyVal); + } + + /** + * does property have lower priority children + * + * @return result + */ + public boolean hasChildren() { + return StringUtils.isNotEmpty(propertyVal); + } + + /** + * get lower priority child + * + * @return result + */ + public PriorityInstanceProperty child() { + if (propertyVal.contains(SEPARATOR)) { + return new PriorityInstanceProperty(propertyKey, StringUtils.substringBeforeLast(propertyVal, SEPARATOR)); + } + return new PriorityInstanceProperty(propertyKey, StringUtils.EMPTY); + } + + /** + * get property value + * + * @return propertyVal + */ + public String getPropertyValue() { + return propertyVal; + } + + @Override + public String toString() { + return "PriorityInstanceProperty{key=" + propertyKey + ", value=" + propertyVal + '}'; + } + } +} 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 new file mode 100644 index 00000000000..50baab772fb --- /dev/null +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ServerDiscoveryFilter.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.loadbalance.filter; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +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.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, + 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(), + 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/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/main/resources/config/cse.handler.xml b/handlers/handler-loadbalance/src/main/resources/config/cse.handler.xml deleted file mode 100644 index c447380704a..00000000000 --- a/handlers/handler-loadbalance/src/main/resources/config/cse.handler.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - \ No newline at end of file diff --git a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/MyServerListFilterExt.java b/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/MyServerListFilterExt.java deleted file mode 100644 index 2327e4dc308..00000000000 --- a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/MyServerListFilterExt.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import java.util.ArrayList; -import java.util.List; - -import com.netflix.loadbalancer.Server; - -import io.servicecomb.core.Invocation; - -/** - * @author l00168639 - * - */ -public class MyServerListFilterExt implements ServerListFilterExt { - private Invocation invocation; - - @Override - public List getFilteredListOfServers(List serverList) { - if (invocation.getAppId().equals("test")) { - return new ArrayList(); - } - return serverList; - } - - @Override - public void setInvocation(Invocation invocation) { - this.invocation = invocation; - } -} diff --git a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestConfiguration.java b/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestConfiguration.java deleted file mode 100644 index 9e201cd6f00..00000000000 --- a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestConfiguration.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import org.junit.Test; - -import mockit.Mock; -import mockit.MockUp; - -/** - * - */ -public class TestConfiguration { - - @Test - public void testConstants() { - - assertEquals("cse.loadbalance.", Configuration.PROP_ROOT); - assertEquals("NFLoadBalancerRuleClassName", Configuration.PROP_POLICY); - assertEquals("com.netflix.loadbalancer.RoundRobinRule", Configuration.PROP_POLICY_ROUNDROBIN); - assertEquals("ribbon.", Configuration.PROP_ROOT_20); - assertEquals("retryEnabled", Configuration.PROP_RETRY_ENABLED); - assertEquals("retryOnNext", Configuration.PROP_RETRY_ONNEXT); - assertEquals("retryOnSame", Configuration.PROP_RETRY_ONSAME); - assertEquals("SessionStickinessRule.successiveFailedTimes", Configuration.SUCCESSIVE_FAILED_TIMES); - - assertNotNull(Configuration.INSTANCE); - } - - @Test - public void testFullConfigurationWithArgsString() { - - Configuration.INSTANCE.getPolicy("test"); - Configuration.INSTANCE.getRetryOnNext("test"); - Configuration.INSTANCE.getRetryOnSame("test"); - Configuration.INSTANCE.isRetryEnabled("test"); - Configuration.INSTANCE.getSuccessiveFailedTimes(); - Configuration.INSTANCE.getSessionTimeoutInSeconds(); - - assertNotNull(Configuration.INSTANCE.getPolicy("test")); - assertNotNull(Configuration.INSTANCE.getRetryOnNext("test")); - assertNotNull(Configuration.INSTANCE.getRetryOnSame("test")); - assertNotNull(Configuration.INSTANCE.isRetryEnabled("test")); - assertNotNull(Configuration.INSTANCE.getSuccessiveFailedTimes()); - assertNotNull(Configuration.INSTANCE.getSessionTimeoutInSeconds()); - } - - @Test - public void testConfigurationWithGetpropertyReturnsStringChar() { - - new MockUp() { - @Mock - private String getProperty(String defaultValue, String... keys) { - return "tyt"; - } - }; - - Configuration.INSTANCE.getRetryOnNext("test"); - - assertNotNull(Configuration.INSTANCE.getRetryOnNext("test")); - } - - @Test - public void testConfigurationWithGetpropertyReturnsStringNum() { - - new MockUp() { - - @Mock - private String getProperty(String defaultValue, String... keys) { - return "1234"; - } - }; - - Configuration.INSTANCE.getRetryOnNext("test"); - - assertNotNull(Configuration.INSTANCE.getRetryOnNext("test")); - } - - @Test - public void testGetRetryOnSameWithGetpropertyReturnsStringChar() { - - new MockUp() { - @Mock - private String getProperty(String defaultValue, String... keys) { - return "tyt"; - } - }; - - Configuration.INSTANCE.getRetryOnSame("test"); - assertNotNull(Configuration.INSTANCE.getRetryOnSame("test")); - } - - @Test - public void testGetRetryOnSameWithGetpropertyReturnsStringNum() { - - new MockUp() { - - @Mock - private String getProperty(String defaultValue, String... keys) { - return "1234"; - } - }; - - Configuration.INSTANCE.getRetryOnSame("test"); - assertNotNull(Configuration.INSTANCE.getRetryOnSame("test")); - } - - @Test - public void testIsRetryEnabledWithGetpropertyReturnsStringChar() { - - new MockUp() { - @Mock - private String getProperty(String defaultValue, String... keys) { - return "tyt"; - } - }; - - Configuration.INSTANCE.isRetryEnabled("test"); - assertNotNull(Configuration.INSTANCE.isRetryEnabled("test")); - } - - @Test - public void testIsRetryEnabledWithGetpropertyReturnsStringNum() { - - new MockUp() { - - @Mock - private String getProperty(String defaultValue, String... keys) { - return "1234"; - } - }; - - Configuration.INSTANCE.isRetryEnabled("test"); - assertNotNull(Configuration.INSTANCE.isRetryEnabled("test")); - } - - @Test - public void testGetSuccessiveFailedTimes() { - - Configuration.INSTANCE.getSuccessiveFailedTimes(); - assertNotNull(Configuration.INSTANCE.getSuccessiveFailedTimes()); - } - - @Test - public void testGetSessionTimeoutInSeconds() { - - Configuration.INSTANCE.getSessionTimeoutInSeconds(); - assertNotNull(Configuration.INSTANCE.getSessionTimeoutInSeconds()); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestCseServer.java b/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestCseServer.java deleted file mode 100644 index 050c27fa9c9..00000000000 --- a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestCseServer.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import static org.junit.Assert.assertNotNull; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.Transport; -import io.servicecomb.serviceregistry.cache.CacheEndpoint; - -/** - * - * - */ -public class TestCseServer { - - private Transport transport = Mockito.mock(Transport.class); - - private CseServer cs = new CseServer(transport, new CacheEndpoint("abcd", null)); - - @Test - public void testCseServerObj() { - assertNotNull(cs); - } - - @Test - public void testGetEndpoint() { - cs.getEndpoint(); - assertNotNull(cs.getEndpoint()); - } - - @SuppressWarnings("unlikely-arg-type") - @Test - public void testEqualsMethod() { - Assert.assertFalse(cs.equals("abcd")); - - CseServer other = new CseServer(transport, new CacheEndpoint("1234", null)); - Assert.assertFalse(cs.equals(other)); - - other = new CseServer(transport, new CacheEndpoint("abcd", null)); - Assert.assertTrue(cs.equals(other)); - } - - @Test - public void testToStringMethod() { - cs.toString(); - assertNotNull(cs.toString()); - } - - @Test - public void testGetHost() { - cs.getHost(); - assertNotNull(cs.getHost()); - } - - @Test - public void testHashCodeMethod() { - cs.hashCode(); - assertNotNull(cs.hashCode()); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestCseServerList.java b/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestCseServerList.java deleted file mode 100644 index 30e11c1a9b7..00000000000 --- a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestCseServerList.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import static org.junit.Assert.assertNotNull; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.servicecomb.core.endpoint.AbstractEndpointsCache; -import io.servicecomb.serviceregistry.cache.InstanceCache; -import io.servicecomb.serviceregistry.cache.InstanceCacheManager; -import mockit.Mock; -import mockit.MockUp; - -public class TestCseServerList { - - private CseServerList instance = null; - - private void mockTestCases() { - new MockUp() { - @Mock - public InstanceCache getOrCreate(String appId, String microserviceName, String microserviceVersionRule) { - return null; - } - }; - } - - @Before - public void setUp() throws Exception { - mockTestCases(); - instance = new CseServerList("appId", "microserviceName", "microserviceVersionRule", "transportName"); - } - - @After - public void tearDown() throws Exception { - instance = null; - } - - @Test - public void testCseServerList() { - Assert.assertNotNull(instance); - } - - @SuppressWarnings("rawtypes") - @Test - public void testGetInitListServers() { - - @SuppressWarnings("unused") - ServerListCache serverListCache = new ServerListCache("test", "test", "test", "test"); - - List endpoints = new ArrayList<>(); - - ENDPOINT e = null; - endpoints.add(e); - - new MockUp() { - - @Mock - public List getLatestEndpoints() { - return endpoints; - } - }; - - instance.getInitialListOfServers(); - assertNotNull(instance.getInitialListOfServers()); - } - - @SuppressWarnings("rawtypes") - @Test - public void testGetUpdatedListOfServers() { - - @SuppressWarnings("unused") - ServerListCache serverListCache = new ServerListCache("test", "test", "test", "test"); - - List endpoints = new ArrayList<>(); - - ENDPOINT e = null; - endpoints.add(e); - - new MockUp() { - - @Mock - public List getLatestEndpoints() { - return endpoints; - } - }; - - instance.getUpdatedListOfServers(); - assertNotNull(instance.getUpdatedListOfServers()); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestLoadBalancer.java b/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestLoadBalancer.java deleted file mode 100644 index 48473ae6a52..00000000000 --- a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestLoadBalancer.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import static org.junit.Assert.assertNotNull; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.loadbalancer.AbstractLoadBalancer.ServerGroup; -import com.netflix.loadbalancer.IRule; -import com.netflix.loadbalancer.Server; - -import io.servicecomb.loadbalance.filter.SimpleTransactionControlFilter; -import io.servicecomb.loadbalance.filter.TransactionControlFilter; - -public class TestLoadBalancer { - - private CseServerList serverList = Mockito.mock(CseServerList.class); - - private IRule rule = Mockito.mock(IRule.class); - - private LoadBalancer loadBalancer = new LoadBalancer(serverList, rule); - - @Test - public void testLoadBalancerFullOperationWithoutException() { - - List newServers = new ArrayList(); - Server server = Mockito.mock(Server.class); - newServers.add(server); - - loadBalancer.chooseServer(); - - Object key = Mockito.mock(Object.class); - - loadBalancer.chooseServer(key); - loadBalancer.getAllServers(); - loadBalancer.getLoadBalancerStats(); - loadBalancer.getReachableServers(); - - assertNotNull(loadBalancer.getAllServers()); - } - - @Test - public void testAddServerException() { - boolean status = true; - List newServers = new ArrayList(); - Server server = Mockito.mock(Server.class); - - newServers.add(server); - - try { - - loadBalancer.addServers(newServers); - } catch (Exception e) { - - status = false; - - Assert.assertEquals("Not implemented.", e.getMessage()); - } - - Assert.assertFalse(status); - } - - @Test - public void testServerListException() { - boolean status = true; - List newServers = new ArrayList(); - Server server = Mockito.mock(Server.class); - - newServers.add(server); - - try { - - loadBalancer.getServerList(ServerGroup.ALL); - } catch (Exception e) { - - status = false; - - Assert.assertEquals("Not implemented.", e.getMessage()); - } - - Assert.assertFalse(status); - } - - @Test - public void testMarkServerDownException() { - boolean status = true; - List newServers = new ArrayList(); - Server server = Mockito.mock(Server.class); - - newServers.add(server); - - try { - - loadBalancer.markServerDown(server); - } catch (Exception e) { - - status = false; - - Assert.assertEquals("Not implemented.", e.getMessage()); - } - - Assert.assertFalse(status); - } - - @Test - public void testFilter() { - Assert.assertEquals(0, loadBalancer.getFilterSize()); - - TransactionControlFilter filter = new SimpleTransactionControlFilter(); - loadBalancer.putFilter("test", filter); - Assert.assertEquals(1, loadBalancer.getFilterSize()); - } - - @Test - public void testGetAllServers() { - List servers = new ArrayList(); - Server server = Mockito.mock(Server.class); - servers.add(server); - Mockito.when(serverList.getInitialListOfServers()).thenReturn(servers); - - TransactionControlFilter filter = Mockito.mock(TransactionControlFilter.class); - Mockito.when(filter.getFilteredListOfServers(servers)).thenReturn(servers); - Assert.assertEquals(servers, loadBalancer.getAllServers()); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestLoadbalanceClientConfig.java b/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestLoadbalanceClientConfig.java deleted file mode 100644 index a8fe46c62bf..00000000000 --- a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestLoadbalanceClientConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import static org.junit.Assert.assertNotNull; - -import org.junit.Test; - -/** - * - * - */ -public class TestLoadbalanceClientConfig { - - @Test - public void testLoadbalanceConstructor() { - - LoadbalanceClientConfig lbc = new LoadbalanceClientConfig("test"); - - assertNotNull(lbc); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestLoadbalanceHandler.java b/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestLoadbalanceHandler.java deleted file mode 100644 index 566df579ec1..00000000000 --- a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestLoadbalanceHandler.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.loadbalancer.IRule; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.reactive.ExecutionListener; - -import io.servicecomb.config.ConfigUtil; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.provider.consumer.SyncResponseExecutor; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.Response; -import mockit.Deencapsulation; -import mockit.Expectations; -import mockit.Injectable; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -/** - * - * - */ -public class TestLoadbalanceHandler { - - private CseServerList serverList = Mockito.mock(CseServerList.class); - - private IRule rule = Mockito.mock(IRule.class); - - private LoadBalancer lb = new LoadBalancer(serverList, rule); - - @AfterClass - public static void classTeardown() { - Deencapsulation.setField(ConfigurationManager.class, "instance", null); - Deencapsulation.setField(ConfigurationManager.class, "customConfigurationInstalled", false); - Deencapsulation.setField(DynamicPropertyFactory.class, "config", null); - } - - @BeforeClass - public static void beforeCls() { - ConfigUtil.installDynamicConfig(); - AbstractConfiguration configuration = - (AbstractConfiguration) DynamicPropertyFactory.getBackingConfigurationSource(); - configuration.addProperty("cse.loadbalance.test.transactionControl.policy", - "io.servicecomb.loadbalance.filter.SimpleTransactionControlFilter"); - configuration.addProperty("cse.loadbalance.test.transactionControl.options.tag0", "value0"); - configuration.addProperty("cse.loadbalance.test.isolation.enabled", "true"); - configuration.addProperty("cse.loadbalance.serverListFilters", "a"); - configuration.addProperty("cse.loadbalance.serverListFilter.a.className", - "io.servicecomb.loadbalance.MyServerListFilterExt"); - } - - @Test - public void testLoadBalancerWithFilterExtentions(final @Injectable Invocation invocation, - final @Injectable AsyncResponse asyncResp, - final @Mocked ServerListCache serverListCache) throws Exception { - final ArrayList servers = new ArrayList(); - servers.add(new Server("test")); - new Expectations() { - { - invocation.getConfigTransportName(); - result = "rest"; - serverListCache.getLatestEndpoints(); - result = new ArrayList(); - invocation.getAppId(); - result = "test"; - } - }; - LoadbalanceHandler lh = new LoadbalanceHandler(); - lh.handle(invocation, asyncResp); - - Map loadBalancerMap = Deencapsulation.getField(lh, "loadBalancerMap"); - LoadBalancer lb = loadBalancerMap.get("rest"); - Assert.assertEquals(lb.getFilterSize(), 2); - } - - @Test - public void testLoadbalanceHandlerHandleWithSend() throws Exception { - - boolean status = true; - - LoadbalanceHandler lh = new LoadbalanceHandler(); - - Invocation invocation = Mockito.mock(Invocation.class); - - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - - Mockito.when(invocation.getConfigTransportName()).thenReturn("baa"); - - Map loadBalancerMap = new ConcurrentHashMap(); - - loadBalancerMap.put("baa", lb); - - try { - Deencapsulation.setField(lh, "loadBalancerMap", loadBalancerMap); - - Deencapsulation.invoke(lh, "send", invocation, asyncResp, lb); - - lh.handle(invocation, asyncResp); - } catch (Exception e) { - - status = false; - } - - Assert.assertTrue(status); - } - - @Test - public void testLoadbalanceHandlerHandleWithSendWithRetry() throws Exception { - - boolean status = true; - - LoadbalanceHandler lh = new LoadbalanceHandler(); - - Invocation invocation = Mockito.mock(Invocation.class); - - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - - Mockito.when(invocation.getConfigTransportName()).thenReturn("baadshah"); - - Map loadBalancerMap = new ConcurrentHashMap(); - - loadBalancerMap.put("baadshah", lb); - - try { - - Deencapsulation.setField(lh, "loadBalancerMap", loadBalancerMap); - - Deencapsulation.invoke(lh, "sendWithRetry", invocation, asyncResp, lb); - - lh.handle(invocation, asyncResp); - } catch (Exception e) { - - status = false; - } - - Assert.assertTrue(status); - } - - @Test - public void testLoadbalanceHandlerHandleWithCseServer() throws Exception { - - boolean status = true; - - new MockUp() { - - @Mock - private LoadBalancer createLoadBalancer(String appId, String microserviceName, - String microserviceVersionRule, String transportName) { - - return lb; - } - }; - - LoadbalanceHandler lh = new LoadbalanceHandler(); - - Invocation invocation = Mockito.mock(Invocation.class); - - Mockito.when(invocation.getConfigTransportName()).thenReturn("baadshah"); - - Mockito.when(invocation.getMicroserviceVersionRule()).thenReturn("VERSION_RULE_LATEST"); - - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - - Mockito.when(invocation.getAppId()).thenReturn("test"); - - Mockito.when(invocation.getMicroserviceName()).thenReturn("test"); - - CseServer server = Mockito.mock(CseServer.class); - - Mockito.when((CseServer) lb.chooseServer()).thenReturn(server); - try { - - lh.handle(invocation, asyncResp); - } catch (Exception e) { - - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testLoadbalanceHandlerHandleWithLoadBalancerHandler() throws Exception { - - boolean status = true; - - new MockUp() { - - @Mock - private LoadBalancer createLoadBalancer(String appId, String microserviceName, - String microserviceVersionRule, - String transportName) { - - return lb; - } - }; - - LoadbalanceHandler lh = new LoadbalanceHandler(); - - Invocation invocation = Mockito.mock(Invocation.class); - - Mockito.when(invocation.getConfigTransportName()).thenReturn("baadshah"); - - Mockito.when(invocation.getMicroserviceVersionRule()).thenReturn("VERSION_RULE_LATEST"); - - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - - Mockito.when(invocation.getMicroserviceName()).thenReturn("test"); - - new MockUp() { - - @Mock - public boolean isRetryEnabled(String microservice) { - return true; - } - }; - - SyncResponseExecutor orginExecutor = new SyncResponseExecutor(); - - Mockito.when(invocation.getResponseExecutor()).thenReturn(orginExecutor); - - List> listeners = new ArrayList<>(0); - - @SuppressWarnings("unchecked") - ExecutionListener listener = Mockito.mock(ExecutionListener.class); - - ExecutionListener e = null; - listeners.add(e); - listeners.add(listener); - - try { - lh.handle(invocation, asyncResp); - } catch (Exception ex) { - - status = false; - } - - Assert.assertTrue(status); - } - - @Test - public void testSetIsolationFilter() { - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(invocation.getMicroserviceName()).thenReturn("test"); - LoadbalanceHandler lbHandler = new LoadbalanceHandler(); - LoadBalancer myLB = new LoadBalancer(serverList, rule); - lbHandler.setIsolationFilter(myLB, "abc"); - Assert.assertEquals(1, myLB.getFilterSize()); - - Mockito.when(invocation.getMicroserviceName()).thenReturn("abc"); - myLB = new LoadBalancer(serverList, rule); - lbHandler.setIsolationFilter(myLB, "abc"); - myLB.setInvocation(invocation); - - Assert.assertEquals(1, myLB.getFilterSize()); - Map filters = Deencapsulation.getField(myLB, "filters"); - List servers = new ArrayList<>(); - servers.add(new Server(null)); - Assert.assertEquals(servers.size(), - filters.get("io.servicecomb.loadbalance.filter.IsolationServerListFilter") - .getFilteredListOfServers(servers) - .size()); - } - - @Test - public void testSetTransactionControlFilter() { - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(invocation.getMicroserviceName()).thenReturn("test"); - LoadbalanceHandler lbHandler = new LoadbalanceHandler(); - LoadBalancer myLB = new LoadBalancer(serverList, rule); - lbHandler.setTransactionControlFilter(myLB, "test"); - Assert.assertEquals(1, myLB.getFilterSize()); - - lbHandler.setTransactionControlFilter(myLB, "test"); - Assert.assertEquals(1, myLB.getFilterSize()); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestServerListCache.java b/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestServerListCache.java deleted file mode 100644 index 0fc01b62528..00000000000 --- a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestServerListCache.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.loadbalancer.Server; - -import io.servicecomb.core.Transport; -import io.servicecomb.serviceregistry.cache.CacheEndpoint; -import io.servicecomb.serviceregistry.cache.InstanceCache; -import io.servicecomb.serviceregistry.cache.InstanceCacheManager; -import mockit.Mock; -import mockit.MockUp; - -public class TestServerListCache { - - private ServerListCache instance = null; - - private Transport transport = null; - - private void mockTestCases() { - new MockUp() { - @Mock - public InstanceCache getOrCreate(String appId, String microserviceName, String microserviceVersionRule) { - return null; - } - }; - - transport = Mockito.mock(Transport.class); - } - - @Before - public void setUp() throws Exception { - mockTestCases(); - instance = new ServerListCache("appId", "microserviceName", "microserviceVersionRule", "transportName"); - } - - @After - public void tearDown() throws Exception { - instance = null; - } - - @Test - public void testServerListCache() { - Assert.assertNotNull(instance); - } - - @Test - public void testCreateEndpointTransportString() { - Server server = instance.createEndpoint(transport, new CacheEndpoint("stringAddress", null)); - Assert.assertNotNull(server); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestSessionSticknessRule.java b/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestSessionSticknessRule.java deleted file mode 100644 index 01859eb0c4a..00000000000 --- a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/TestSessionSticknessRule.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.loadbalancer.LoadBalancerStats; -import com.netflix.loadbalancer.Server; - -import io.servicecomb.core.Transport; -import io.servicecomb.serviceregistry.cache.CacheEndpoint; -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); - CseServer mockedServer = new CseServer(transport, new CacheEndpoint("rest:127.0.0.1:8888", null)); - Object key = Mockito.mock(Object.class); - LoadBalancerStats stats = mock(LoadBalancerStats.class); - Mockito.when(mockedLb.getLoadBalancerStats()).thenReturn(stats); - Deencapsulation.invoke(rule, "chooseServerWhenTimeout", key); - mockedServer.setAlive(true); - mockedServer.setReadyToServe(true); - List allServers = Arrays.asList(mockedServer); - when(mockedLb.getReachableServers()).thenReturn(allServers); - when(mockedLb.getAllServers()).thenReturn(allServers); - - rule.setLoadBalancer(mockedLb); - Assert.assertEquals(rule.getLoadBalancer(), mockedLb); - Server s = rule.choose("default"); - Assert.assertEquals(s, mockedServer); - - s = rule.choose("default"); - Assert.assertEquals(s, mockedServer); - } - - @Test - public void testServerWithoutTimeoutAndWithThreshold() { - - boolean status = true; - - SessionStickinessRule ss = new SessionStickinessRule(); - - Object key = new Object(); - - Server s = new Server("test"); - - Deencapsulation.setField(ss, "lastServer", s); - - new MockUp() { - - @Mock - private boolean isTimeOut() { - return false; - } - }; - - new MockUp() { - - @Mock - private boolean isErrorThresholdMet() { - return true; - } - }; - - try { - ss.choose(key); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testServerWithTimeout() { - - boolean status = true; - - SessionStickinessRule ss = new SessionStickinessRule(); - - Object key = new Object(); - - Server s = new Server("test"); - - Deencapsulation.setField(ss, "lastServer", s); - - new MockUp() { - - @Mock - private boolean isTimeOut() { - return true; - } - }; - - try { - ss.choose(key); - } catch (Exception e) { - status = false; - } - - Assert.assertTrue(status); - } - - @Test - public void testServerWithoutTimeoutException() { - - boolean status = true; - - SessionStickinessRule ss = new SessionStickinessRule(); - - Object key = new Object(); - - Server s = new Server("test"); - - Deencapsulation.setField(ss, "lastServer", s); - - new MockUp() { - - @Mock - private boolean isTimeOut() { - return false; - } - }; - - try { - ss.choose(key); - } catch (Exception e) { - status = false; - } - Assert.assertFalse(status); - } - - @Test - public void testServerWithoutTimeoutAndThreshold() { - - boolean status = true; - - SessionStickinessRule ss = new SessionStickinessRule(); - - Object key = new Object(); - - Server s = new Server("test"); - - Deencapsulation.setField(ss, "lastServer", s); - - new MockUp() { - - @Mock - private boolean isTimeOut() { - return false; - } - }; - - new MockUp() { - - @Mock - private boolean isErrorThresholdMet() { - return false; - } - }; - - try { - ss.choose(key); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testServerWithActualServerObj() { - - boolean status = true; - SessionStickinessRule ss = new SessionStickinessRule(); - - Object key = new Object(); - - Server s = new Server("test"); - - Deencapsulation.setField(ss, "lastServer", s); - try { - ss.choose(key); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testServerWithKey() { - - boolean status = true; - SessionStickinessRule ss = new SessionStickinessRule(); - - Object key = new Object(); - try { - ss.choose(key); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/exception/TestLoadbalanceExceptionUtils.java b/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/exception/TestLoadbalanceExceptionUtils.java deleted file mode 100644 index 4a062f92037..00000000000 --- a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/exception/TestLoadbalanceExceptionUtils.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance.exception; - -import static org.junit.Assert.assertEquals; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.core.exception.CseException; - -/** - * - * - */ -public class TestLoadbalanceExceptionUtils { - - @Test - public void testLoadbalanceExceptionUtils() { - - assertEquals("cse.handler.lb.wrong.rule", LoadbalanceExceptionUtils.CSE_HANDLER_LB_WRONG_RULE); - CseException cseException = LoadbalanceExceptionUtils.createLoadbalanceException("cse.handler.lb.wrong.rule", - new Throwable(), - "ARGS"); - Assert.assertNotNull(cseException); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/filter/TestIsolationServerListFilter.java b/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/filter/TestIsolationServerListFilter.java deleted file mode 100644 index 508b89e0619..00000000000 --- a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/filter/TestIsolationServerListFilter.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.loadbalance.filter; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.loadbalancer.LoadBalancerStats; -import com.netflix.loadbalancer.Server; - -import io.servicecomb.config.ConfigUtil; -import io.servicecomb.core.Invocation; -import io.servicecomb.loadbalance.CseServer; -import mockit.Deencapsulation; - -public class TestIsolationServerListFilter { - - IsolationServerListFilter IsolationServerListFilter = null; - - LoadBalancerStats loadBalancerStats = null; - - @BeforeClass - public static void initConfig() throws Exception { - ConfigUtil.installDynamicConfig(); - } - - @AfterClass - public static void classTeardown() { - Deencapsulation.setField(ConfigurationManager.class, "instance", null); - Deencapsulation.setField(ConfigurationManager.class, "customConfigurationInstalled", false); - Deencapsulation.setField(DynamicPropertyFactory.class, "config", null); - } - - @Before - public void setUp() throws Exception { - IsolationServerListFilter = new IsolationServerListFilter(); - loadBalancerStats = new LoadBalancerStats("loadBalancer"); - - AbstractConfiguration configuration = - (AbstractConfiguration) DynamicPropertyFactory.getBackingConfigurationSource(); - configuration.clearProperty("cse.loadbalance.isolation.enabled"); - configuration.addProperty("cse.loadbalance.isolation.enabled", - "true"); - configuration.clearProperty("cse.loadbalance.isolation.enableRequestThreshold"); - configuration.addProperty("cse.loadbalance.isolation.enableRequestThreshold", - "3"); - } - - @After - public void tearDown() throws Exception { - IsolationServerListFilter = null; - loadBalancerStats = null; - } - - @Test - public void testSetLoadBalancerStats() { - IsolationServerListFilter.setLoadBalancerStats(loadBalancerStats); - Assert.assertNotNull(IsolationServerListFilter.getLoadBalancerStats()); - Assert.assertEquals(loadBalancerStats, IsolationServerListFilter.getLoadBalancerStats()); - } - - @Test - public void testSetMicroserviceName() { - IsolationServerListFilter.setMicroserviceName("microserviceName"); - Assert.assertNotNull(IsolationServerListFilter.getMicroserviceName()); - Assert.assertEquals("microserviceName", IsolationServerListFilter.getMicroserviceName()); - } - - @Test - public void testGetFilteredListOfServers() { - Invocation invocation = Mockito.mock(Invocation.class); - CseServer testServer = Mockito.mock(CseServer.class); - Mockito.when(invocation.getMicroserviceName()).thenReturn("microserviceName"); - Mockito.when(testServer.getLastVisitTime()).thenReturn(System.currentTimeMillis()); - - List serverList = new ArrayList(); - serverList.add(testServer); - IsolationServerListFilter.setLoadBalancerStats(loadBalancerStats); - IsolationServerListFilter.setInvocation(invocation); - List returnedServerList = IsolationServerListFilter.getFilteredListOfServers(serverList); - Assert.assertEquals(returnedServerList.size(), 1); - - loadBalancerStats.incrementNumRequests(testServer); - loadBalancerStats.incrementNumRequests(testServer); - loadBalancerStats.incrementNumRequests(testServer); - loadBalancerStats.incrementSuccessiveConnectionFailureCount(testServer); - returnedServerList = IsolationServerListFilter.getFilteredListOfServers(serverList); - Assert.assertEquals(returnedServerList.size(), 0); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/filter/TestSimpleTransactionControlFilter.java b/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/filter/TestSimpleTransactionControlFilter.java deleted file mode 100644 index c169e3b908b..00000000000 --- a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/filter/TestSimpleTransactionControlFilter.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.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.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.loadbalancer.Server; - -import io.servicecomb.core.Invocation; -import io.servicecomb.loadbalance.CseServer; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; - -public class TestSimpleTransactionControlFilter { - - private SimpleTransactionControlFilter filter; - - private CseServer server; - - @BeforeClass - public static void beforeCls() { - AbstractConfiguration configuration = new BaseConfiguration(); - configuration.addProperty("cse.loadbalance.test.flowsplitFilter.policy", - "io.servicecomb.loadbalance.filter.SimpleFlowsplitFilter"); - configuration.addProperty("cse.loadbalance.test.flowsplitFilter.options.tag0", "value0"); - } - - @Before - public void setUp() { - filter = new SimpleTransactionControlFilter(); - Map properties = new HashMap<>(); - properties.put("tag0", "value0"); - properties.put("tag1", "value1"); - MicroserviceInstance instance = new MicroserviceInstance(); - instance.setProperties(properties); - server = Mockito.mock(CseServer.class); - Mockito.when(server.getInstance()).thenReturn(instance); - } - - @Test - public void testAllowVisit() { - Map filterOptions = new HashMap<>(); - Assert.assertTrue(filter.allowVisit(server, filterOptions)); - - filterOptions.put("tag0", "value0"); - Assert.assertTrue(filter.allowVisit(server, filterOptions)); - - filterOptions.put("tag2", "value2"); - Assert.assertFalse(filter.allowVisit(server, filterOptions)); - - filterOptions.clear(); - filterOptions.put("tag0", "value1"); - Assert.assertFalse(filter.allowVisit(server, filterOptions)); - } - - @Test - public void testGetFilteredListOfServers() { - Invocation invocation = Mockito.mock(Invocation.class); - filter.setInvocation(invocation); - - List servers = new ArrayList<>(); - servers.add(server); - List filteredServers = filter.getFilteredListOfServers(servers); - Assert.assertEquals(1, filteredServers.size()); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/filter/TestZoneAwareServerListFilterExt.java b/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/filter/TestZoneAwareServerListFilterExt.java deleted file mode 100644 index 141e680e288..00000000000 --- a/handlers/handler-loadbalance/src/test/java/io/servicecomb/loadbalance/filter/TestZoneAwareServerListFilterExt.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.loadbalance.filter; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; - -import com.netflix.loadbalancer.Server; - -import io.servicecomb.loadbalance.CseServer; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.DataCenterInfo; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestZoneAwareServerListFilterExt { - - @Test - public void testZoneAwareServerListFilterExt(@Mocked RegistryUtils registryUtils) { - MicroserviceInstance myself = new MicroserviceInstance(); - DataCenterInfo info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region"); - info.setAvailableZone("test-zone"); - myself.setDataCenterInfo(info); - - MicroserviceInstance allmatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region"); - info.setAvailableZone("test-zone"); - allmatchInstance.setDataCenterInfo(info); - - MicroserviceInstance regionMatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region"); - info.setAvailableZone("test-zone2"); - regionMatchInstance.setDataCenterInfo(info); - - MicroserviceInstance noneMatchInstance = new MicroserviceInstance(); - info = new DataCenterInfo(); - info.setName("test"); - info.setRegion("test-Region2"); - info.setAvailableZone("test-zone2"); - noneMatchInstance.setDataCenterInfo(info); - - new Expectations() { - { - RegistryUtils.getMicroserviceInstance(); - result = myself; - } - }; - ZoneAwareServerListFilterExt filter = new ZoneAwareServerListFilterExt(); - List servers = new ArrayList<>(); - CseServer noneMatchServer = new MockUp() { - @Mock - public String toString() { - return "noneMatchServer"; - } - - @Mock - public String getHost() { - return "noneMatchServer"; - } - - @Mock - public MicroserviceInstance getInstance() { - return noneMatchInstance; - } - }.getMockInstance(); - CseServer regionMatchregionMatchServer = new MockUp() { - @Mock - public String toString() { - return "regionMatchregionMatchServer"; - } - - @Mock - public String getHost() { - return "regionMatchregionMatchServer"; - } - - @Mock - public MicroserviceInstance getInstance() { - return regionMatchInstance; - } - }.getMockInstance(); - - CseServer allmatchServer = new MockUp() { - @Mock - public String toString() { - return "allmatchServer"; - } - - @Mock - public String getHost() { - return "allmatchServer"; - } - - @Mock - public MicroserviceInstance getInstance() { - return allmatchInstance; - } - }.getMockInstance(); - - servers.add(noneMatchServer); - List result = filter.getFilteredListOfServers(servers); - Assert.assertEquals(result.size(), 1); - Assert.assertEquals(result.get(0), noneMatchServer); - - servers.add(regionMatchregionMatchServer); - result = filter.getFilteredListOfServers(servers); - Assert.assertEquals(result.size(), 1); - Assert.assertEquals(result.get(0), regionMatchregionMatchServer); - - servers.add(allmatchServer); - result = filter.getFilteredListOfServers(servers); - Assert.assertEquals(result.size(), 1); - Assert.assertEquals(result.get(0), allmatchServer); - } -} diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/MyServerListFilterExt.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/MyServerListFilterExt.java new file mode 100644 index 00000000000..a8a0ce96fda --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/MyServerListFilterExt.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.loadbalance; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.core.Invocation; + +/** + * @author l00168639 + * + */ +public class MyServerListFilterExt implements ServerListFilterExt { + @Override + public List getFilteredListOfServers(List serverList, Invocation invocation) { + if (invocation.getAppId().equals("test")) { + return new ArrayList<>(); + } + return serverList; + } +} 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 new file mode 100644 index 00000000000..81f24cd8b60 --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestConfiguration.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.apache.servicecomb.foundation.common.LegacyPropertyFactory; +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; + +/** + * + */ +public class TestConfiguration { + Environment environment = Mockito.mock(Environment.class); + + @Before + public void setUp() { + LegacyPropertyFactory.setEnvironment(environment); + } + + @After + public void after() { + } + + @Test + public void testConstants() { + + Assertions.assertEquals("servicecomb.loadbalance.", Configuration.ROOT); + Assertions.assertEquals("ribbon.", Configuration.ROOT_20); + Assertions.assertEquals("SessionStickinessRule.successiveFailedTimes", Configuration.SUCCESSIVE_FAILED_TIMES); + Assertions.assertEquals("maxSingleTestWindow", Configuration.FILTER_MAX_SINGLE_TEST_WINDOW); + + Assertions.assertNotNull(Configuration.INSTANCE); + } + + @Test + public void testFullConfigurationWithArgsString() { + Assertions.assertNotNull(Configuration.INSTANCE.getSuccessiveFailedTimes("test")); + Assertions.assertNotNull(Configuration.INSTANCE.getSessionTimeoutInSeconds("test")); + } + + @Test + public void testGetSuccessiveFailedTimes() { + Assertions.assertNotNull(Configuration.INSTANCE.getSuccessiveFailedTimes("test")); + } + + @Test + public void testGetSessionTimeoutInSeconds() { + Assertions.assertNotNull(Configuration.INSTANCE.getSessionTimeoutInSeconds("test")); + } + + @Test + public void testGetMaxSingleTestWindow() { + Assertions.assertEquals(60000, Configuration.INSTANCE.getMaxSingleTestWindow()); + 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 new file mode 100644 index 00000000000..2b159d38339 --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestExtensionsManager.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.loadbalance; + +import java.util.ArrayList; +import java.util.List; + +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() { + LegacyPropertyFactory.setEnvironment(environment); + } + + @AfterEach + public void tearDown() { + } + + @Test + public void testRuleName() { + List extensionsFactories = new ArrayList<>(); + extensionsFactories.add(new RuleNameExtensionsFactory()); + ExtensionsManager extensionsManager = new ExtensionsManager(extensionsFactories); + + Assertions.assertEquals(RoundRobinRuleExt.class.getName(), + extensionsManager.createLoadBalancerRule("RoundRobin").getClass().getName()); + Assertions.assertEquals(RandomRuleExt.class.getName(), + extensionsManager.createLoadBalancerRule("Random").getClass().getName()); + Assertions.assertEquals(WeightedResponseTimeRuleExt.class.getName(), + 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 new file mode 100644 index 00000000000..1ee1824c6d6 --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceCreator.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; + +import java.util.ArrayList; +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.Expectations; +import mockit.Injectable; + +public class TestLoadBalanceCreator { + @Test + public void testLoadBalanceWithRoundRobinRuleAndFilter(@Injectable Invocation invocation, + @Injectable Transport transport) { + // 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<>(); + + 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); + LoadBalancer lb = new LoadBalancer(rule, "test"); + + List filters = new ArrayList<>(); + + filters.add((serverList, invocation1) -> { + List filteredServers = new ArrayList<>(); + for (ServiceCombServer server1 : servers) { + if (server1.getHost().equals("host1")) { + continue; + } + filteredServers.add(server1); + } + return filteredServers; + }); + lb.setFilters(filters); + + new Expectations() { + { + invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST); + result = servers; + } + }; + 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 testLoadBalanceWithRandomRuleAndFilter(@Injectable Invocation invocation, + @Injectable Transport transport) { + // Robin components implementations require getReachableServers & getServerList have the same size, we add a test case for this. + RandomRuleExt rule = new RandomRuleExt(); + LoadBalancer lb = new LoadBalancer(rule, "service"); + + List servers = new ArrayList<>(); + + 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); + + List filters = new ArrayList<>(); + filters.add((serverList, invocation1) -> { + List filteredServers = new ArrayList<>(); + for (ServiceCombServer server1 : servers) { + if (server1.getHost().equals("host1")) { + continue; + } + filteredServers.add(server1); + } + return filteredServers; + }); + lb.setFilters(filters); + new Expectations() { + { + invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST); + result = servers; + } + }; + 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 testLoadBalanceWithWeightedResponseTimeRuleAndFilter(@Injectable Endpoint endpoint1, + @Injectable Endpoint endpoint2, @Injectable Invocation invocation) { + // Robin components implementations require getReachableServers & getServerList have the same size, we add a test case for this. + WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); + LoadBalancer lb = new LoadBalancer(rule, "service"); + + List servers = new ArrayList<>(); + + 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<>(); + filters.add((serverList, invocation1) -> { + List filteredServers = new ArrayList<>(); + for (ServiceCombServer server1 : servers) { + if (server1.getHost().equals("host1")) { + continue; + } + filteredServers.add(server1); + } + return filteredServers; + }); + lb.setFilters(filters); + new Expectations() { + { + invocation.getLocalContext(LoadBalanceFilter.CONTEXT_KEY_SERVER_LIST); + result = servers; + } + }; + 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); + } +} 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 new file mode 100644 index 00000000000..123f23ac514 --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.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.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.Injectable; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; + +/** + * + * + */ +public class TestLoadBalanceFilter { + static SCBEngine scbEngine; + + static TransportManager transportManager; + + String microserviceName = "ms"; + + LoadBalanceFilter handler; + + Map loadBalancerMap; + + @Injectable + Invocation invocation; + + @Mocked + Transport restTransport; + + Response sendResponse; + + @Before + 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.loadbalance.userDefinedEndpoint.enabled", + boolean.class, false)).thenReturn(false); + scbEngine.run(); + transportManager = scbEngine.getTransportManager(); + + new MockUp(invocation) { + @Mock + String getMicroserviceName() { + return microserviceName; + } + + @Mock + void next(AsyncResponse asyncResp) throws Exception { + asyncResp.handle(sendResponse); + } + + @Mock + public T getLocalContext(String key) { + return (T) null; + } + }; + + new MockUp(transportManager) { + @Mock + Transport findTransport(String transportName) { + return restTransport; + } + }; + + List extensionsFactories = new ArrayList<>(); + extensionsFactories.add(new RuleNameExtensionsFactory()); + ExtensionsManager extensionsManager = new ExtensionsManager(extensionsFactories); + + 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(); + } + + @Test + public void testIsFailedResponse() { + Assertions.assertFalse(handler.isFailedResponse(Response.create(400, "", ""))); + Assertions.assertFalse(handler.isFailedResponse(Response.create(500, "", ""))); + Assertions.assertTrue(handler.isFailedResponse(Response.create(490, "", ""))); + Assertions.assertTrue(handler.isFailedResponse(Response.consumerFailResp(new NullPointerException()))); + } +} 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 new file mode 100644 index 00000000000..4c2fb5f5b45 --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter2.java @@ -0,0 +1,581 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +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.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.common.LegacyPropertyFactory; +import org.apache.servicecomb.loadbalance.filter.ServerDiscoveryFilter; +import org.apache.servicecomb.loadbalance.filter.ZoneAwareDiscoveryFilter; +import org.apache.servicecomb.registry.DiscoveryManager; +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.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 io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; + +public class TestLoadBalanceFilter2 { + DynamicProperties dynamicProperties = Mockito.mock(DynamicProperties.class); + + Environment environment = Mockito.mock(Environment.class); + + @BeforeEach + public void setUp() { + + 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() { + 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.getTransport()).thenReturn("rest"); + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, new HashMap<>()); + TransportManager transportManager = Mockito.mock(TransportManager.class); + Transport transport = Mockito.mock(Transport.class); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); + Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); + + // set up data + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test-Region"); + myself.setAvailableZone("test-zone"); + + 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"); + + List data = new ArrayList<>(); + DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); + when(transportManager.findTransport("rest")).thenReturn(transport); + + LoadBalanceFilter handler; + LoadBalancer loadBalancer; + ServiceCombServer server; + + 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.add(noneMatchInstance); + parent.cacheVersion(1); + 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.add(regionMatchInstance); + parent.cacheVersion(parent.cacheVersion() + 1); + loadBalancer = handler.getOrCreateLoadBalancer(invocation); + server = loadBalancer.chooseServer(invocation); + Assertions.assertEquals("rest://localhost:9091", server.getEndpoint().getEndpoint()); + + data.add(allmatchInstance); + parent.cacheVersion(parent.cacheVersion() + 1); + loadBalancer = handler.getOrCreateLoadBalancer(invocation); + server = loadBalancer.chooseServer(invocation); + Assertions.assertEquals("rest://localhost:9090", server.getEndpoint().getEndpoint()); + } + + + @Test + 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.getTransport()).thenReturn("rest"); + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, new HashMap<>()); + TransportManager transportManager = Mockito.mock(TransportManager.class); + Transport transport = Mockito.mock(Transport.class); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); + Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); + + // set up data + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test"); + myself.setAvailableZone("test"); + + 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"); + Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); + Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("instance"); + + List data = new ArrayList<>(); + DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); + + when(transportManager.findTransport("rest")).thenReturn(transport); + + LoadBalanceFilter handler; + LoadBalancer loadBalancer; + ServiceCombServer server; + + 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.add(instance); + parent.cacheVersion(parent.cacheVersion() + 1); + loadBalancer = handler.getOrCreateLoadBalancer(invocation); + server = loadBalancer.chooseServer(invocation); + Assertions.assertEquals("rest://localhost:9090", server.getEndpoint().getEndpoint()); + } + + @Test + public void testZoneAwareFilterWorksEmptyInstanceProtectionEnabled() { + 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); + 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.getTransport()).thenReturn("rest"); + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, new HashMap<>()); + TransportManager transportManager = Mockito.mock(TransportManager.class); + Transport transport = Mockito.mock(Transport.class); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); + Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); + + // set up data + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test-Region"); + myself.setAvailableZone("test-zone"); + + 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"); + + List data = new ArrayList<>(); + DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); + + when(transportManager.findTransport("rest")).thenReturn(transport); + + LoadBalanceFilter handler; + LoadBalancer loadBalancer; + ServiceCombServer server; + + 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.add(noneMatchInstance); + parent.cacheVersion(1); + 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.add(regionMatchInstance); + parent.cacheVersion(parent.cacheVersion() + 1); + loadBalancer = handler.getOrCreateLoadBalancer(invocation); + server = loadBalancer.chooseServer(invocation); + Assertions.assertEquals("rest://localhost:9091", server.getEndpoint().getEndpoint()); + + data.add(allmatchInstance); + parent.cacheVersion(parent.cacheVersion() + 1); + loadBalancer = handler.getOrCreateLoadBalancer(invocation); + server = loadBalancer.chooseServer(invocation); + Assertions.assertEquals("rest://localhost:9090", server.getEndpoint().getEndpoint()); + } + + @Test + public void testZoneAwareFilterUsingMockedInvocationWorks() { + Invocation invocation = new NonSwaggerInvocation("testApp", "testMicroserviceName"); + TransportManager transportManager = Mockito.mock(TransportManager.class); + Transport transport = Mockito.mock(Transport.class); + 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 + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test-Region"); + myself.setAvailableZone("test-zone"); + + 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"); + 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"); + 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"); + Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); + Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); + Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); + + List data = new ArrayList<>(); + DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); + + when(transportManager.findTransport("rest")).thenReturn(transport); + + LoadBalanceFilter handler; + LoadBalancer loadBalancer; + ServiceCombServer server; + + 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.add(noneMatchInstance); + parent.cacheVersion(1); + 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.add(regionMatchInstance); + parent.cacheVersion(parent.cacheVersion() + 1); + loadBalancer = handler.getOrCreateLoadBalancer(invocation); + server = loadBalancer.chooseServer(invocation); + Assertions.assertEquals("rest://localhost:7091", server.getEndpoint().getEndpoint()); + + data.add(allmatchInstance); + parent.cacheVersion(parent.cacheVersion() + 1); + loadBalancer = handler.getOrCreateLoadBalancer(invocation); + server = loadBalancer.chooseServer(invocation); + Assertions.assertEquals("rest://localhost:7090", server.getEndpoint().getEndpoint()); + } + + @Test + public void testStatusFilterUsingMockedInvocationWorks() { + Invocation invocation = new NonSwaggerInvocation("testApp", "testMicroserviceName"); + TransportManager transportManager = Mockito.mock(TransportManager.class); + Transport transport = Mockito.mock(Transport.class); + SCBEngine scbEngine = Mockito.mock(SCBEngine.class); + Mockito.when(scbEngine.getTransportManager()).thenReturn(transportManager); + Mockito.when(scbEngine.getEnvironment()).thenReturn(environment); + + // set up data + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test-Region"); + myself.setAvailableZone("test-zone"); + + 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"); + 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"); + 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"); + Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); + Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); + Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); + + List data = new ArrayList<>(); + DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); + + when(transportManager.findTransport("rest")).thenReturn(transport); + + LoadBalanceFilter handler; + LoadBalancer loadBalancer; + ServiceCombServer server; + + 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.add(noneMatchInstance); + parent.cacheVersion(1); + 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.add(regionMatchInstance); + parent.cacheVersion(parent.cacheVersion() + 1); + loadBalancer = handler.getOrCreateLoadBalancer(invocation); + server = loadBalancer.chooseServer(invocation); + Assertions.assertEquals("rest://localhost:7091", server.getEndpoint().getEndpoint()); + + data.add(allmatchInstance); + parent.cacheVersion(parent.cacheVersion() + 1); + loadBalancer = handler.getOrCreateLoadBalancer(invocation); + server = loadBalancer.chooseServer(invocation); + Assertions.assertEquals("rest://localhost:7090", server.getEndpoint().getEndpoint()); + } +} 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 new file mode 100644 index 00000000000..ba17976e0ba --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalancer.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.loadbalance; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.core.Invocation; +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; + +public class TestLoadBalancer { + private final RuleExt rule = Mockito.mock(RuleExt.class); + + @Test + public void testLoadBalancerFullOperationWithoutException() { + List newServers = new ArrayList<>(); + ServiceCombServer server = Mockito.mock(ServiceCombServer.class); + Invocation invocation = Mockito.mock(Invocation.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(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.assertEquals("test", loadBalancer.getMicroServiceName()); + } +} 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 new file mode 100644 index 00000000000..d381703f445 --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestRoundRobinRuleExt.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.loadbalance; + +import java.util.ArrayList; +import java.util.List; +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; + +public class TestRoundRobinRuleExt { + @Test + public void testRoundRobin() { + RoundRobinRuleExt rule = new RoundRobinRuleExt(); + 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); + } + + AtomicInteger server1 = new AtomicInteger(0); + AtomicInteger server2 = new AtomicInteger(0); + for (int i = 0; i < 2000; i++) { + if (rule.choose(servers, invocation).toString().equals("server 0")) { + server1.incrementAndGet(); + } else { + server2.incrementAndGet(); + } + } + Assertions.assertEquals(server1.get(), server2.get()); + } + + @Test + public void testBenchmarkRobin() { + // less than 0.001ms + RoundRobinRuleExt rule = new RoundRobinRuleExt(); + 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); + } + long begin = System.currentTimeMillis(); + for (int i = 0; i < 10000; i++) { + rule.choose(servers, invocation); + } + long taken = System.currentTimeMillis() - begin; + System.out.println("taken " + taken); + Assertions.assertTrue(taken < 10 * 5, "actual token " + taken); // 5 * times make slow machine happy + } +} 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 new file mode 100644 index 00000000000..e914ae20c6a --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombServer.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.loadbalance; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Transport; +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; +import org.mockito.Mockito; + +/** + * + * + */ +public class TestServiceCombServer { + + private final Transport transport = Mockito.mock(Transport.class); + + private ServiceCombServer cs; + + @Before + public void setUp() { + 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 + public void testCseServerObj() { + Assertions.assertNotNull(cs); + } + + @Test + public void testGetEndpoint() { + cs.getEndpoint(); + Assertions.assertNotNull(cs.getEndpoint()); + } + + @Test + public void testEqualsMethod() { + Assertions.assertNotEquals(cs, (Object) "abcd"); + + 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); + + 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); + } + + @Test + public void testToStringMethod() { + cs.toString(); + Assertions.assertNotNull(cs.toString()); + } + + @Test + public void testGetHost() { + cs.getHost(); + Assertions.assertNotNull(cs.getHost()); + } + + @Test + public void testHashCodeMethod() { + cs.hashCode(); + Assertions.assertNotNull(cs.hashCode()); + } +} 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/TestWeightedResponseTimeRuleExt.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestWeightedResponseTimeRuleExt.java new file mode 100644 index 00000000000..e3d20d2eea2 --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestWeightedResponseTimeRuleExt.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 io.github.resilience4j.core.metrics.Metrics.Outcome; + +public class TestWeightedResponseTimeRuleExt { + @Test + public void testRoundRobin() { + WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); + 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); + serverMetrics.record(1, TimeUnit.MILLISECONDS, Outcome.SUCCESS); + } + + AtomicInteger server1 = new AtomicInteger(0); + AtomicInteger server2 = new AtomicInteger(0); + for (int i = 0; i < 2000; i++) { + if (rule.choose(servers, invocation).toString().equals("server 0")) { + server1.incrementAndGet(); + } else { + server2.incrementAndGet(); + } + } + Assertions.assertEquals(server1.get(), server2.get()); + } + + @Test + public void testWeighed() { + WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); + 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++) { + if (rule.choose(servers, invocation).toString().equals("server 0")) { + serverCounter1.incrementAndGet(); + } else { + serverCounter2.incrementAndGet(); + } + } + double percent = (double) serverCounter1.get() / (serverCounter2.get() + serverCounter1.get()); + System.out.println("percent" + percent); + Assertions.assertEquals(0.67d, percent, 0.1); + serverCounter1.set(0); + serverCounter2.set(0); + + 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++) { + if (rule.choose(servers, invocation).toString().equals("server 0")) { + serverCounter1.incrementAndGet(); + } else { + serverCounter2.incrementAndGet(); + } + } + percent = (double) serverCounter1.get() / (serverCounter2.get() + serverCounter1.get()); + System.out.println("percent" + percent); + Assertions.assertEquals(0.50d, percent, 0.1); + } + + @Test + 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(); + 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); + serverMetrics.record(i, TimeUnit.MILLISECONDS, Outcome.SUCCESS); + } + long begin = System.currentTimeMillis(); + for (int i = 0; i < 100000; i++) { + rule.choose(servers, invocation); + } + long taken = System.currentTimeMillis() - begin; + System.out.println("taken " + taken); + Assertions.assertTrue(taken < 1000 * 5, "actually taken: " + taken); // 5 * times make slow machine happy + } + + @Test + 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(); + 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); + serverMetrics.record(2, TimeUnit.MILLISECONDS, Outcome.SUCCESS); + } + long begin = System.currentTimeMillis(); + for (int i = 0; i < 100000; i++) { + rule.choose(servers, invocation); + } + long taken = System.currentTimeMillis() - begin; + System.out.println("taken " + taken); + Assertions.assertTrue(taken < 200 * 5, "actually taken: " + taken); // 5 * times make slow machine happy + } +} diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/exception/TestLoadbalanceExceptionUtils.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/exception/TestLoadbalanceExceptionUtils.java new file mode 100644 index 00000000000..71b929af53e --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/exception/TestLoadbalanceExceptionUtils.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.loadbalance.exception; + +import org.apache.servicecomb.core.exception.CseException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestLoadbalanceExceptionUtils { + + @Test + public void testLoadbalanceExceptionUtils() { + + Assertions.assertEquals("servicecomb.handler.lb.wrong.rule", LoadbalanceExceptionUtils.CSE_HANDLER_LB_WRONG_RULE); + CseException cseException = LoadbalanceExceptionUtils.createLoadbalanceException("servicecomb.handler.lb.wrong.rule", + new Throwable(), + "ARGS"); + Assertions.assertNotNull(cseException); + } +} 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 new file mode 100644 index 00000000000..7c78388dcdc --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilterTest.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 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.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; + +/** + * Test for PriorityInstancePropertyDiscoveryFilter + */ +public class PriorityInstancePropertyDiscoveryFilterTest { + + public static final String PROPERTY_KEY = "environment"; + + private PriorityInstancePropertyDiscoveryFilter filter; + + private List instances; + + StatefulDiscoveryInstance instance1; + + ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); + + EnumerablePropertySource propertySource; + + 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(); + 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() { + } + + @Test + public void testGetFilteredListOfServers() { + + //complete match + executeTest("", Sets.newHashSet("instance.empty")); + executeTest("local", Sets.newHashSet("instance.local")); + executeTest("local.feature1", Sets.newHashSet("instance.local.feature1")); + executeTest("local.feature1.sprint1", Sets.newHashSet("instance.local.feature1.sprint1")); + + //priority match + executeTest("test", Sets.newHashSet("instance.empty")); + executeTest("local.feature2", Sets.newHashSet("instance.local")); + executeTest("local.feature1.sprint2", Sets.newHashSet("instance.local.feature1")); + executeTest("local.feature2.sprint1", Sets.newHashSet("instance.local")); + executeTest("local.feature1.sprint2.temp", Sets.newHashSet("instance.local.feature1")); + + //none match + instances.remove(instance1); + executeTest("", Collections.emptySet()); + executeTest("foo", Collections.emptySet()); + 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); + + DiscoveryTreeNode parent = new DiscoveryTreeNode(); + parent.name("parent"); + parent.data(instances); + + DiscoveryTreeNode node = filter.discovery(discoveryContext, parent); + 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 new file mode 100644 index 00000000000..0c498d25de2 --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestInstancePropertyDiscoveryFilter.java @@ -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. + */ + +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.servicecomb.core.Invocation; +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.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; + + StatefulDiscoveryInstance instance; + + ConfigurableEnvironment environment; + + EnumerablePropertySource propertySource; + + @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"); + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + instance = new StatefulDiscoveryInstance(discoveryInstance); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("instance111"); + Mockito.when(discoveryInstance.getProperties()).thenReturn(properties); + } + + @Test + public void testAllowVisit() { + Map filterOptions = new HashMap<>(); + Assertions.assertTrue(filter.allowVisit(instance, filterOptions)); + + filterOptions.put("tag0", "value0"); + Assertions.assertTrue(filter.allowVisit(instance, filterOptions)); + + filterOptions.put("tag2", "value2"); + Assertions.assertFalse(filter.allowVisit(instance, filterOptions)); + + filterOptions.clear(); + filterOptions.put("tag0", "value1"); + Assertions.assertFalse(filter.allowVisit(instance, filterOptions)); + } + + @Test + @SuppressWarnings("unchecked") + 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, ((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 new file mode 100644 index 00000000000..a4aa3a5191a --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestServerDiscoveryFilter.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.loadbalance.filter; + +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.transport.TransportManager; +import org.apache.servicecomb.loadbalance.ServiceCombServer; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.registry.discovery.DiscoveryContext; +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 { + ServerDiscoveryFilter filter = new ServerDiscoveryFilter(); + + @Test + public void createEndpoint_TransportNotExist() { + 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() { + 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, CoreConst.RESTFUL, "rest://localhost:8080", instance); + Assertions.assertSame(instance, server.getInstance()); + 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/log4j.properties b/handlers/handler-loadbalance/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/handlers/handler-loadbalance/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/handlers/handler-loadbalance/src/test/resources/microservice.yaml b/handlers/handler-loadbalance/src/test/resources/microservice.yaml new file mode 100644 index 00000000000..5ff7ed9ae41 --- /dev/null +++ b/handlers/handler-loadbalance/src/test/resources/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: + filter-chains: + consumer: + default: empty + producer: + default: empty diff --git a/handlers/handler-publickey-auth/pom.xml b/handlers/handler-publickey-auth/pom.xml new file mode 100644 index 00000000000..9f13b26f232 --- /dev/null +++ b/handlers/handler-publickey-auth/pom.xml @@ -0,0 +1,66 @@ + + + + 4.0.0 + + org.apache.servicecomb + handlers + 3.4.0-SNAPSHOT + + + handler-publickey-auth + Java Chassis::Handlers::PublicKey Auth + + + UTF-8 + + + + + org.apache.servicecomb + java-chassis-core + + + org.apache.servicecomb + registry-local + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.servicecomb + foundation-test-scaffolding + test + + + org.mockito + mockito-inline + test + + + + 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 new file mode 100644 index 00000000000..71fd4e500fb --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationBootListener.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.authentication; + +import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.foundation.common.utils.KeyPairEntry; +import org.apache.servicecomb.foundation.common.utils.KeyPairUtils; +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())) { + return; + } + KeyPairEntry rsaKeyPairEntry = KeyPairUtils.generateALGKeyPair(); + Keypair4Auth.INSTANCE.setPrivateKey(rsaKeyPairEntry.getPrivateKey()); + Keypair4Auth.INSTANCE.setPublicKey(rsaKeyPairEntry.getPublicKey()); + Keypair4Auth.INSTANCE.setPublicKeyEncoded(rsaKeyPairEntry.getPublicKeyEncoded()); + 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 new file mode 100644 index 00000000000..a7f8ea308ee --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.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.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, + havingValue = "true") +public class AuthenticationConfiguration { + public static final String ACCESS_CONTROL_PREFIX = "servicecomb.publicKey.accessControl"; + + public static final String ACCESS_CONTROL_ENABLED = ACCESS_CONTROL_PREFIX + ".enabled"; + + @Bean + public ConsumerAuthFilter scbConsumerAuthFilter() { + return new ConsumerAuthFilter(); + } + + @Bean + public ProviderAuthFilter scbProviderAuthFilter() { + return new ProviderAuthFilter(); + } + + @Bean + 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/RSAAuthenticationToken.java b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/RSAAuthenticationToken.java new file mode 100644 index 00000000000..aa88091b70e --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/RSAAuthenticationToken.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + + +/** + * token 组成部分: + * token: instanceId@@generateTime@randomCode@sign(instanceId@@generateTime@randomCode) + * + */ +public class RSAAuthenticationToken { + + public static final long TOKEN_ACTIVE_TIME = 24 * 60 * 60 * 1000; + + private final String instanceId; + + private final String serviceId; + + private final long generateTime; + + private final String randomCode; + + private final String sign; + + private final String tokenFormat; + + private final String plainToken; + + public RSAAuthenticationToken(String instanceId, String serviceId, long generateTime, + String randomCode, String sign) { + this.instanceId = instanceId; + this.generateTime = generateTime; + this.randomCode = randomCode; + this.serviceId = serviceId; + this.sign = sign; + this.tokenFormat = String.format("%s@%s@%s@%s@%s", + instanceId, + serviceId, + generateTime, + randomCode, + sign); + this.plainToken = String.format("%s@%s@%s@%s", this.instanceId, this.serviceId, this.generateTime, this.randomCode); + } + + public String plainToken() { + return this.plainToken; + } + + public String getInstanceId() { + return instanceId; + } + + + public long getGenerateTime() { + return generateTime; + } + + + public String getSign() { + return sign; + } + + + public String format() { + return tokenFormat; + } + + public static RSAAuthenticationToken fromStr(String token) { + String[] tokenArr = token.split("@"); + if (tokenArr.length != 5) { + return null; + } + return new RSAAuthenticationToken(tokenArr[0], tokenArr[1], + Long.parseLong(tokenArr[2]), tokenArr[3], tokenArr[4]); + } + + public String getServiceId() { + return serviceId; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof RSAAuthenticationToken)) { + return false; + } + RSAAuthenticationToken token = (RSAAuthenticationToken) obj; + if (!token.plainToken().equals(this.plainToken())) { + return false; + } + return token.getSign().equals(this.sign); + } + + public int hashCode() { + return this.plainToken().hashCode() + this.sign.hashCode(); + } +} 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 new file mode 100644 index 00000000000..e26f24d936a --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerAuthFilter.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.authentication.consumer; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +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.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 extends AbstractFilter implements ConsumerFilter, EdgeFilter { + private ConsumerTokenManager authenticationTokenManager; + + @Autowired + public void setConsumerTokenManager(ConsumerTokenManager consumerTokenManager) { + this.authenticationTokenManager = consumerTokenManager; + } + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1010; + } + + @Override + public String getName() { + return "consumer-public-key"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + Optional token = Optional.ofNullable(authenticationTokenManager.getToken()); + if (!token.isPresent()) { + return CompletableFuture.failedFuture( + new InvocationException(Status.SERVICE_UNAVAILABLE, "auth token is not properly configured yet.")); + } + 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 new file mode 100644 index 00000000000..8da889abc02 --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerTokenManager.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.authentication.consumer; + +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +public class ConsumerTokenManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerTokenManager.class); + + private final Object lock = new Object(); + + private RSAAuthenticationToken token; + + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + public String getToken() { + + if (isExpired(token)) { + synchronized (lock) { + if (isExpired(token)) { + return createToken(); + } + } + } + return token.format(); + } + + public String createToken() { + PrivateKey privateKey = Keypair4Auth.INSTANCE.getPrivateKey(); + String instanceId = BootStrapProperties.readServiceName(environment); + String serviceId = BootStrapProperties.readApplication(environment); + + if (instanceId == null || serviceId == null) { + LOGGER.error("service not ready when create token."); + return null; + } + + @SuppressWarnings("deprecation") + String randomCode = org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric(128); + long generateTime = System.currentTimeMillis(); + try { + String plain = String.format("%s@%s@%s@%s", instanceId, serviceId, generateTime, randomCode); + String sign = KeyPairUtils.sign(plain, privateKey); + token = RSAAuthenticationToken.fromStr(String.format("%s@%s", plain, sign)); + } catch (Exception e) { + LOGGER.error("create token error", e); + return null; + } + return token.format(); + } + + /** + * the TTL of Token is 24 hours + * client token will expired 15 minutes early + */ + public boolean isExpired(RSAAuthenticationToken token) { + if (null == token) { + return true; + } + long generateTime = token.getGenerateTime(); + long expiredDate = generateTime + RSAAuthenticationToken.TOKEN_ACTIVE_TIME - 15 * 60 * 1000; + long now = System.currentTimeMillis(); + return now > expiredDate; + } +} 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 new file mode 100644 index 00000000000..b923b2939d7 --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/AccessController.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +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.google.common.eventbus.Subscribe; + +/** + * Add black / white list control to service access + */ +public class AccessController { + static class ConfigurationItem { + static final String CATEGORY_PROPERTY = "property"; + + String category; + + String propertyName; + + String rule; + } + + private static final Logger LOG = LoggerFactory.getLogger(AccessController.class); + + private static final String KEY_WHITE_LIST_PREFIX = "servicecomb.publicKey.accessControl.white"; + + private static final String KEY_BLACK_LIST_PREFIX = "servicecomb.publicKey.accessControl.black"; + + private static final String KEY_PROPERTY_NAME = "%s.%s.propertyName"; + + private static final String KEY_CATEGORY = "%s.%s.category"; + + private static final String KEY_RULE_POSTFIX = ".rule"; + + private final Environment environment; + + private Map whiteList = new HashMap<>(); + + private Map blackList = new HashMap<>(); + + public AccessController(Environment environment) { + this.environment = environment; + loadConfigurations(KEY_BLACK_LIST_PREFIX); + loadConfigurations(KEY_WHITE_LIST_PREFIX); + EventManager.register(this); + } + + public boolean isAllowed(DiscoveryInstance microservice) { + return whiteAllowed(microservice) && !blackDenied(microservice); + } + + private boolean whiteAllowed(DiscoveryInstance microservice) { + if (whiteList.isEmpty()) { + return true; + } + return matchFound(microservice, whiteList); + } + + private boolean blackDenied(DiscoveryInstance microservice) { + if (blackList.isEmpty()) { + return false; + } + return matchFound(microservice, blackList); + } + + private boolean matchFound(DiscoveryInstance microservice, Map ruleList) { + boolean matched = false; + for (ConfigurationItem item : ruleList.values()) { + if (ConfigurationItem.CATEGORY_PROPERTY.equals(item.category)) { + // we support to configure properties, e.g. serviceName, appId, environment, alias, version and so on, also support key in properties. + if (matchMicroserviceField(microservice, item) || matchMicroserviceProperties(microservice, item)) { + return true; + } + } + } + return matched; + } + + private boolean matchMicroserviceProperties(DiscoveryInstance microservice, ConfigurationItem item) { + Map properties = microservice.getProperties(); + for (Entry entry : properties.entrySet()) { + if (!entry.getKey().equals(item.propertyName)) { + continue; + } + return isPatternMatch(entry.getValue(), item.rule); + } + return false; + } + + 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 isPatternMatch(fieldValue, item.rule); + } + + private boolean isPatternMatch(String value, String pattern) { + if (pattern.startsWith("*")) { + return value.endsWith(pattern.substring(1)); + } + if (pattern.endsWith("*")) { + return value.startsWith(pattern.substring(0, pattern.length() - 1)); + } + return value.equals(pattern); + } + + @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(String prefix) { + Map configurations = new HashMap<>(); + Set configsItems = ConfigUtil.propertiesWithPrefix((ConfigurableEnvironment) environment, prefix); + for (String pathKey : configsItems) { + if (pathKey.endsWith(KEY_RULE_POSTFIX)) { + ConfigurationItem configurationItem = new ConfigurationItem(); + 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 = environment.getProperty(String.format(KEY_PROPERTY_NAME, prefix, pathKeyItem)); + if (StringUtils.isEmpty(configurationItem.propertyName)) { + continue; + } + configurationItem.category = environment.getProperty(String.format(KEY_CATEGORY, prefix, pathKeyItem)); + if (StringUtils.isEmpty(configurationItem.category)) { + continue; + } + configurations.put(pathKeyItem, configurationItem); + } + } + + if (KEY_WHITE_LIST_PREFIX.equals(prefix)) { + this.whiteList = configurations; + logConfigurations(configurations, true); + } else { + this.blackList = configurations; + logConfigurations(configurations, false); + } + } + + 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)); + } +} 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 new file mode 100644 index 00000000000..f525ae88cf8 --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.authentication.provider; + +import java.util.concurrent.CompletableFuture; + +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.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 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() { + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER + 1010; + } + + @Override + public String getName() { + return "provider-public-key"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + 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); + } + return CompletableFuture.failedFuture( + new InvocationException(Status.UNAUTHORIZED, "public key authorization failed.")); + } +} 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 new file mode 100644 index 00000000000..ebedacb7bf2 --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderTokenManager.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.authentication.RSAAuthenticationToken; +import org.apache.servicecomb.foundation.common.utils.KeyPairUtils; +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; +import com.google.common.cache.CacheBuilder; + +public class ProviderTokenManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(ProviderTokenManager.class); + + private final Cache validatedToken = CacheBuilder.newBuilder() + .expireAfterAccess(getExpiredTime(), TimeUnit.MILLISECONDS) + .build(); + + 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 { + RSAAuthenticationToken rsaToken = RSAAuthenticationToken.fromStr(token); + if (null == rsaToken) { + LOGGER.error("token format is error, perhaps you need to set auth handler at consumer"); + return false; + } + if (tokenExpired(rsaToken)) { + LOGGER.error("token is expired"); + return false; + } + + if (validatedToken.asMap().containsKey(rsaToken)) { + 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(), rsaToken.getInstanceId())); + } + return false; + } catch (InvalidKeyException | NoSuchAlgorithmException | InvalidKeySpecException | SignatureException e) { + LOGGER.error("verify error", e); + return false; + } + } + + public boolean isValidToken(RSAAuthenticationToken rsaToken) + throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException { + String sign = rsaToken.getSign(); + String content = rsaToken.plainToken(); + String publicKey = getPublicKeyFromInstance(rsaToken.getInstanceId(), rsaToken.getServiceId()); + return KeyPairUtils.verify(publicKey, sign, content); + } + + protected int getExpiredTime() { + return 60 * 60 * 1000; + } + + private boolean tokenExpired(RSAAuthenticationToken rsaToken) { + long generateTime = rsaToken.getGenerateTime(); + long expired = generateTime + RSAAuthenticationToken.TOKEN_ACTIVE_TIME + 15 * 60 * 1000; + long now = System.currentTimeMillis(); + return now > expired; + } + + private String getPublicKeyFromInstance(String instanceId, String serviceId) { + DiscoveryInstance instances = microserviceInstanceCache.getOrCreate(serviceId, instanceId); + if (instances != null) { + return instances.getProperties().get(DefinitionConst.INSTANCE_PUBKEY_PRO); + } else { + LOGGER.error("not instance found {}-{}, maybe attack", instanceId, serviceId); + return ""; + } + } + + @VisibleForTesting + Cache getValidatedToken() { + return validatedToken; + } +} 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 new file mode 100644 index 00000000000..0c6c6afd346 --- /dev/null +++ b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestAccessController.java @@ -0,0 +1,279 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.authentication.provider.AccessController; +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 { + ConfigurableEnvironment environment; + + EnumerablePropertySource propertySource; + + @BeforeEach + public void tearDown() { + environment = Mockito.mock(ConfigurableEnvironment.class); + propertySource = Mockito.mock(EnumerablePropertySource.class); + } + + @Test + public void testIsValidOfWhiteByServiceName() { + 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*"); + + AccessController controller = new AccessController(environment); + DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("trustCustomer"); + Assertions.assertTrue(controller.isAllowed(service)); + + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("nottrustCustomer"); + Assertions.assertFalse(controller.isAllowed(service)); + + 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 = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("Customer_trust_not"); + Assertions.assertFalse(controller.isAllowed(service)); + + 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 = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("Customer_trust"); + Assertions.assertFalse(controller.isAllowed(service)); + } + + @Test + public void testIsValidOfBlackByServiceName() { + 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*"); + + AccessController controller = new AccessController(environment); + + DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("trustCustomer"); + Assertions.assertFalse(controller.isAllowed(service)); + + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("nottrustCustomer"); + Assertions.assertTrue(controller.isAllowed(service)); + + 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 = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("Customer_trust_not"); + Assertions.assertTrue(controller.isAllowed(service)); + + 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 = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("Customer_trust"); + Assertions.assertTrue(controller.isAllowed(service)); + } + + @Test + public void testIsValidOfBlackAndWhiteByServiceName() { + 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"); + + AccessController controller = new AccessController(environment); + DiscoveryInstance service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("trustCustomer"); + Assertions.assertTrue(controller.isAllowed(service)); + + service = Mockito.mock(DiscoveryInstance.class); + Mockito.when(service.getServiceName()).thenReturn("trustCustomerhacker"); + Assertions.assertFalse(controller.isAllowed(service)); + } + + @Test + public void testIsValidOfBlackByProperties() { + 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); + + Assertions.assertFalse(controller.isAllowed(service)); + + map.put("tag", "testa"); + Mockito.when(service.getProperties()).thenReturn(map); + Assertions.assertTrue(controller.isAllowed(service)); + } + + @Test + public void testIsValidOfWhiteByProperties() { + 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"); + Mockito.when(service.getProperties()).thenReturn(map); + Assertions.assertTrue(controller.isAllowed(service)); + + map.put("tag", "testa"); + Mockito.when(service.getProperties()).thenReturn(map); + Assertions.assertFalse(controller.isAllowed(service)); + } + + @Test + public void testIsValidOfBlackAndWhiteByServiceNameAndVersion() { + 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 new file mode 100644 index 00000000000..28cb8d48d2d --- /dev/null +++ b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestAuthenticationBootListener.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.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 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.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.token.Keypair4Auth; +import org.apache.servicecomb.registry.RegistrationManager; +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() { + 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(); + } + + @Test + public void testGenerateRSAKey() { + 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); + Assertions.assertNotNull(Keypair4Auth.INSTANCE.getPrivateKey()); + Assertions.assertNotNull(Keypair4Auth.INSTANCE.getPublicKey()); + } + + @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); + + 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 new file mode 100644 index 00000000000..e9ec7e2b2a9 --- /dev/null +++ b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/TestRSAAuthenticationToken.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.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() + throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException { + String tokenstr = + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; + RSAAuthenticationToken token = RSAAuthenticationToken.fromStr(tokenstr); + String contents = token.plainToken(); + Assertions.assertEquals( + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ", + contents); + String sign = token.getSign(); + Assertions.assertEquals( + "WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk=", + sign); + String pubKey = + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxKl5TNUTec7fL2degQcCk6vKf3c0wsfNK5V6elKzjWxm0MwbRj/UeR20VSnicBmVIOWrBS9LiERPPvjmmWUOSS2vxwr5XfhBhZ07gCAUNxBOTzgMo5nE45DhhZu5Jzt5qSV6o10Kq7+fCCBlDZ1UoWxZceHkUt5AxcrhEDulFjQIDAQAB"; + Assertions.assertTrue(KeyPairUtils.verify(pubKey, sign, contents)); + } + + + @Test + public void testRSAAuthenticationTokenError() { + String tokenstr = + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; + RSAAuthenticationToken token = RSAAuthenticationToken.fromStr(tokenstr); + Assertions.assertNull(token); + } + + + @Test + public void testEqual() { + String tokenstr = + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; + RSAAuthenticationToken token = RSAAuthenticationToken.fromStr(tokenstr); + Assertions.assertNotEquals(token, null); + RSAAuthenticationToken token2 = RSAAuthenticationToken.fromStr( + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk"); + Assertions.assertNotEquals(token2, token); + + RSAAuthenticationToken token3 = RSAAuthenticationToken.fromStr( + "e8a0a4b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk"); + Assertions.assertNotEquals(token3, token); + + RSAAuthenticationToken token4 = RSAAuthenticationToken.fromStr( + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="); + Assertions.assertEquals(token4, token); + } +} 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 new file mode 100644 index 00000000000..801f91e697d --- /dev/null +++ b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestProviderTokenManager.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.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.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.token.Keypair4Auth; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.registry.definition.DefinitionConst; +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() { + } + + @AfterEach + public void teardown() { + } + + @Test + public void testTokenExpired() { + String tokenStr = + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; + ProviderTokenManager tokenManager = new ProviderTokenManager(); + DiscoveryInstance microserviceInstance = Mockito.mock(DiscoveryInstance.class); + Map properties = new HashMap<>(); + Mockito.when(microserviceInstance.getProperties()).thenReturn(properties); + properties.put(DefinitionConst.INSTANCE_PUBKEY_PRO, + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxKl5TNUTec7fL2degQcCk6vKf3c0wsfNK5V6elKzjWxm0MwbRj/UeR20VSnicBmVIOWrBS9LiERPPvjmmWUOSS2vxwr5XfhBhZ07gCAUNxBOTzgMo5nE45DhhZu5Jzt5qSV6o10Kq7+fCCBlDZ1UoWxZceHkUt5AxcrhEDulFjQIDAQAB"); + Assertions.assertFalse(tokenManager.valid(tokenStr)); + } + + @Test + @SuppressWarnings("unchecked") + public void testTokenExpiredRemoveInstance() throws Exception { + + String tokenStr = + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; + RSAAuthenticationToken token = Mockito.spy(RSAAuthenticationToken.fromStr(tokenStr)); + ProviderTokenManager tokenManager = Mockito.spy(new ProviderTokenManager() { + @Override + protected int getExpiredTime() { + return 500; + } + }); + 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(); + Assertions.assertTrue(cache.asMap().containsKey(token)); + + Thread.sleep(1000); + Assertions.assertFalse(cache.asMap().containsKey(token)); + } + } + + @Test + public void testTokenFromValidatePool() { + KeyPairEntry keyPairEntry = KeyPairUtils.generateALGKeyPair(); + Keypair4Auth.INSTANCE.setPrivateKey(keyPairEntry.getPrivateKey()); + Keypair4Auth.INSTANCE.setPublicKey(keyPairEntry.getPublicKey()); + Keypair4Auth.INSTANCE.setPublicKeyEncoded(keyPairEntry.getPublicKeyEncoded()); + String serviceId = "test"; + String instanceId = "test"; + ConsumerTokenManager consumerTokenManager = new ConsumerTokenManager(); + + 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<>(); + Mockito.when(microserviceInstance.getProperties()).thenReturn(properties); + properties.put(DefinitionConst.INSTANCE_PUBKEY_PRO, keyPairEntry.getPublicKeyEncoded()); + 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()); + 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 new file mode 100644 index 00000000000..5ff7ed9ae41 --- /dev/null +++ b/handlers/handler-publickey-auth/src/test/resources/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: + filter-chains: + consumer: + default: empty + producer: + default: empty diff --git a/handlers/handler-router/pom.xml b/handlers/handler-router/pom.xml new file mode 100644 index 00000000000..0068002eb7d --- /dev/null +++ b/handlers/handler-router/pom.xml @@ -0,0 +1,62 @@ + + + + + + handlers + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + handler-router + Java Chassis::Handlers::Router + + + + org.springframework + spring-core + + + org.springframework + spring-beans + + + com.google.guava + guava + + + org.yaml + snakeyaml + + + org.apache.servicecomb + common-rest + + + org.apache.servicecomb + servicecomb-governance + + + org.apache.servicecomb + handler-loadbalance + + + 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 new file mode 100644 index 00000000000..fdae82e1445 --- /dev/null +++ b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.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.router.custom; + +import java.util.List; + +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.loadbalance.ServerListFilterExt; +import org.apache.servicecomb.loadbalance.ServiceCombServer; +import org.apache.servicecomb.router.RouterFilter; +import org.apache.servicecomb.router.distribute.RouterDistributor; + +public class RouterServerListFilter implements ServerListFilterExt { + + private static final String ENABLE = "servicecomb.router.type"; + + private static final String TYPE_ROUTER = "router"; + + @SuppressWarnings("unchecked") + private final RouterDistributor routerDistributor = BeanUtils + .getBean(RouterDistributor.class); + + private final RouterFilter routerFilter = BeanUtils.getBean(RouterFilter.class); + + @Override + public boolean enabled() { + return LegacyPropertyFactory.getStringProperty(ENABLE, "") + .equals(TYPE_ROUTER); + } + + @Override + public List getFilteredListOfServers(List list, + Invocation invocation) { + String targetServiceName = invocation.getMicroserviceName(); + return routerFilter.getFilteredListOfServers(list, targetServiceName, + MatchType.createGovHttpRequest(invocation), routerDistributor); + } +} 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 new file mode 100644 index 00000000000..ff6952d24c8 --- /dev/null +++ b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.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.router.custom; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnProperty(value = ServiceCombRouterConfiguration.ROUTER_ENABLED, + havingValue = "true", matchIfMissing = true) +public class ServiceCombRouterConfiguration { + public static final String ROUTER_PREFIX = "servicecomb.router"; + + public static final String ROUTER_ENABLED = ROUTER_PREFIX + ".enabled"; + + @Bean + 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.loadbalance.ServerListFilterExt b/handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt new file mode 100644 index 00000000000..0e6f5d9a65b --- /dev/null +++ b/handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt @@ -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.RouterServerListFilter 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 5c474121027..3644d209bab 100644 --- a/handlers/handler-tracing-zipkin/pom.xml +++ b/handlers/handler-tracing-zipkin/pom.xml @@ -1,12 +1,13 @@ - - - - - diff --git a/handlers/handler-tracing-zipkin/src/test/java/io/servicecomb/tracing/zipkin/ZipkinTracingHandlerTest.java b/handlers/handler-tracing-zipkin/src/test/java/io/servicecomb/tracing/zipkin/ZipkinTracingHandlerTest.java deleted file mode 100644 index 9106345b64f..00000000000 --- a/handlers/handler-tracing-zipkin/src/test/java/io/servicecomb/tracing/zipkin/ZipkinTracingHandlerTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.tracing.zipkin; - -import static com.seanyinx.github.unit.scaffolding.AssertUtils.expectFailing; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; - -import brave.Span; -import brave.Tracing; -import io.servicecomb.core.Invocation; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.Response; - -public class ZipkinTracingHandlerTest { - - private final Invocation invocation = Mockito.mock(Invocation.class); - - private final Response response = Mockito.mock(Response.class); - - private final AsyncResponse asyncResponse = Mockito.mock(AsyncResponse.class); - - private final ZipkinTracingDelegate delegate = Mockito.mock(ZipkinTracingDelegate.class); - - private final Tracing tracing = Tracing.newBuilder().build(); - - private final Span span = Mockito.mock(Span.class); - - private final RuntimeException exception = new RuntimeException("oops"); - - private ZipkinTracingHandler tracingHandler; - - @Before - public void setUp() throws Exception { - when(delegate.tracer()).thenReturn(tracing); - when(delegate.createSpan(invocation)).thenReturn(span); - when(delegate.name()).thenReturn("delegate"); - - tracingHandler = new ZipkinTracingHandler(delegate); - } - - @Test - public void delegatesResponseAndHandleResponse() throws Exception { - ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(AsyncResponse.class); - - tracingHandler.handle(invocation, asyncResponse); - - verify(invocation).next(argumentCaptor.capture()); - - argumentCaptor.getValue().handle(response); - - verify(asyncResponse).handle(response); - verify(delegate).onResponse(span, response, null); - } - - @Test - public void delegatesErrorResponseAndHandleResponse() throws Exception { - when(response.isFailed()).thenReturn(true); - when(response.getResult()).thenReturn(exception); - - ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(AsyncResponse.class); - - tracingHandler.handle(invocation, asyncResponse); - - verify(invocation).next(argumentCaptor.capture()); - - argumentCaptor.getValue().handle(response); - - verify(asyncResponse).handle(response); - verify(delegate).onResponse(span, response, exception); - } - - @Test - public void delegatesErrorOnInvocationFailure() throws Exception { - doThrow(exception).when(invocation).next(any(AsyncResponse.class)); - - try { - tracingHandler.handle(invocation, asyncResponse); - expectFailing(RuntimeException.class); - } catch (Exception e) { - assertThat(e, is(exception)); - } - - verify(delegate).onResponse(span, null, exception); - } -} diff --git a/handlers/pom.xml b/handlers/pom.xml index 8b4842358ed..e74e2c95134 100644 --- a/handlers/pom.xml +++ b/handlers/pom.xml @@ -1,12 +1,13 @@ + + + huawei-cloud + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + darklaunch + Java Chassis::darklaunch + + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + handler-loadbalance + + + 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 new file mode 100644 index 00000000000..988cd9e401f --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRule.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.darklaunch; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.darklaunch.oper.ConditionFactory; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DarklaunchRule { + private static final Logger LOG = LoggerFactory.getLogger(DarklaunchRule.class); + + public static final String PROP_VERSION = "version"; + + public static final String PROP_TAG = "tag"; + + public static final String PROP_PERCENT = "rate"; + + private PolicyType policyType; + + private final List ruleItems = new ArrayList<>(); + + public static DarklaunchRule parse(String ruleStr) { + if (StringUtils.isEmpty(ruleStr)) { + return null; + } + + try { + DarklaunchRuleJson ruleJson = JsonUtils + .readValue(ruleStr.getBytes(StandardCharsets.UTF_8), DarklaunchRuleJson.class); + DarklaunchRule rule = new DarklaunchRule(ruleJson.getPolicyType()); + for (DarklaunchRuleItemJson itemJson : ruleJson.getRuleItems()) { + DarklaunchRuleItem item = new DarklaunchRuleItem(itemJson.getGroupName()); + item.setGroupCondition( + ConditionFactory.buildCondition(itemJson.getGroupCondition(), itemJson.isCaseInsensitive()) + ); + if (rule.getPolicyType().equals(PolicyType.RULE)) { + item.setPolicyCondition( + ConditionFactory.buildCondition(itemJson.getPolicyCondition(), itemJson.isCaseInsensitive())); + } else { + item.setPolicyCondition(ConditionFactory.buildRateCondition(itemJson.getPolicyCondition())); + } + rule.addRuleItem(item); + } + return rule; + } catch (Exception e) { + LOG.warn("Invalid configuration: rule={},message={}", ruleStr, e.getMessage()); + } + return null; + } + + public DarklaunchRule(PolicyType policyType) { + this.policyType = policyType; + } + + public PolicyType getPolicyType() { + return policyType; + } + + public void setPolicyType(PolicyType policyType) { + this.policyType = policyType; + } + + public List getRuleItems() { + return ruleItems; + } + + public void addRuleItem(DarklaunchRuleItem ruleItem) { + this.ruleItems.add(ruleItem); + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRuleItem.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRuleItem.java new file mode 100644 index 00000000000..a6ad1e8ccb1 --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRuleItem.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.darklaunch; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.darklaunch.oper.Condition; +import org.apache.servicecomb.loadbalance.ServiceCombServer; + +public class DarklaunchRuleItem { + private String groupName; + + private Condition groupCondition; + + private Condition policyCondition; + + private final List servers = new ArrayList<>(); + + public DarklaunchRuleItem(String groupName) { + this.groupName = groupName; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public Condition getGroupCondition() { + return groupCondition; + } + + public void setGroupCondition(Condition groupCondition) { + this.groupCondition = groupCondition; + } + + public Condition getPolicyCondition() { + return policyCondition; + } + + public void setPolicyCondition(Condition policyCondition) { + this.policyCondition = policyCondition; + } + + public List getServers() { + return this.servers; + } + + public void addServer(ServiceCombServer server) { + this.servers.add(server); + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRuleItemJson.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRuleItemJson.java new file mode 100644 index 00000000000..47dce445ce9 --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRuleItemJson.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.darklaunch; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class DarklaunchRuleItemJson { + private String groupName; + + private String groupCondition; + + private String policyCondition; + + private boolean caseInsensitive; + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public String getGroupCondition() { + return groupCondition; + } + + public void setGroupCondition(String groupCondition) { + this.groupCondition = groupCondition; + } + + public String getPolicyCondition() { + return policyCondition; + } + + public void setPolicyCondition(String policyCondition) { + this.policyCondition = policyCondition; + } + + public boolean isCaseInsensitive() { + return caseInsensitive; + } + + public void setCaseInsensitive(boolean caseInsensitive) { + this.caseInsensitive = caseInsensitive; + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRuleJson.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRuleJson.java new file mode 100644 index 00000000000..4c1255218e5 --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRuleJson.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.darklaunch; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class DarklaunchRuleJson { + private PolicyType policyType; + + private List ruleItems = new ArrayList<>(); + + public PolicyType getPolicyType() { + return policyType; + } + + public void setPolicyType(PolicyType policyType) { + this.policyType = policyType; + } + + public List getRuleItems() { + return ruleItems; + } + + public void setRuleItems(List ruleItems) { + this.ruleItems = ruleItems; + } + + public void addRuleItem(DarklaunchRuleItemJson 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 new file mode 100644 index 00000000000..d380a8f254c --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchServerListFilter.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.darklaunch; + +import java.util.ArrayList; +import java.util.List; +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; + +public class DarklaunchServerListFilter implements ServerListFilterExt { + + private static final String POLICY_SERVICE_COMB = "servicecomb.darklaunch.policy.%s"; + + private static final int HUNDRED = 100; + + private final Random random = new Random(); + + public DarklaunchServerListFilter() { + } + + @Override + public int getOrder() { + return HUNDRED; + } + + @Override + public boolean enabled() { + return true; + } + + @Override + public List getFilteredListOfServers(List serverList, Invocation invocation) { + DarklaunchRule rule = DarklaunchRule.parse(LegacyPropertyFactory + .getStringProperty(String.format(POLICY_SERVICE_COMB, invocation.getMicroserviceName()))); + if (rule == null) { + return serverList; + } + List defaultGroup = new ArrayList<>(); + divideServerGroup(serverList, rule, defaultGroup); + + if (rule.getPolicyType() == PolicyType.RULE) { + for (DarklaunchRuleItem item : rule.getRuleItems()) { + List ruleServers = getRuleServers(invocation, item, defaultGroup); + if (ruleServers != null) { + return ruleServers; + } + } + } else { + int rate = random.nextInt(HUNDRED); + for (DarklaunchRuleItem item : rule.getRuleItems()) { + item.getPolicyCondition().setActual(DarklaunchRule.PROP_PERCENT, rate); + if (item.getPolicyCondition().match()) { + if (item.getServers().isEmpty()) { + return defaultGroup; + } + return item.getServers(); + } + rate = rate - Integer.parseInt(item.getPolicyCondition().expected()); + } + } + return defaultGroup; + } + + private List getRuleServers(Invocation invocation, DarklaunchRuleItem item, + List defaultGroup) { + invocation.getSwaggerArguments().forEach((k, v) -> item.getPolicyCondition().setActual(k, v)); + for (String key : invocation.getContext().keySet()) { + item.getPolicyCondition().setActual(key, invocation.getContext(key)); + } + if (item.getPolicyCondition().match()) { + if (item.getServers().isEmpty()) { + return defaultGroup; + } + return item.getServers(); + } + return null; + } + + private void divideServerGroup(List serverList, DarklaunchRule rule, + List defaultGroup) { + for (ServiceCombServer server : serverList) { + boolean hasGroup = false; + for (DarklaunchRuleItem item : rule.getRuleItems()) { + item.getGroupCondition() + .setActual(DarklaunchRule.PROP_VERSION, server.getInstance().getVersion()); + if (item.getGroupCondition().match()) { + item.addServer(server); + hasGroup = true; + } + } + if (!hasGroup) { + defaultGroup.add(server); + } + } + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/PolicyType.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/PolicyType.java new file mode 100644 index 00000000000..fcabf5b45e6 --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/PolicyType.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.darklaunch; + +public enum PolicyType { + RULE, + RATE +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/AbstractCondition.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/AbstractCondition.java new file mode 100644 index 00000000000..e257aa76333 --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/AbstractCondition.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.darklaunch.oper; + +public abstract class AbstractCondition implements Condition { + private final String key; + + private final String expected; + + private Object actual; + + private SupportedType type = SupportedType.UNKNOWN; + + public AbstractCondition(String key, String expected) { + assertValueNotNull(key, expected); + this.key = key; + this.expected = expected; + } + + @Override + public String key() { + return this.key; + } + + @Override + public String expected() { + return this.expected; + } + + @Override + public void setActual(String key, Object actual) { + assertValueNotNull(key, ""); + if (this.key.equals(key)) { + if (actual instanceof String) { + this.type = SupportedType.STRING; + } else if (actual instanceof Number) { + this.type = SupportedType.NUMBER; + } + this.actual = actual; + } + } + + protected void assertValueNotNull(String key, Object value) { + if (key == null) { + throw new IllegalArgumentException("Key can not be null."); + } + if (value == null) { + throw new IllegalArgumentException("Argument can not be null. key = " + key); + } + } + + public static int compareNum(Object num, String anotherNum) { + try { + if (num instanceof Integer) { + return Integer.compare((Integer) num, Integer.parseInt(anotherNum)); + } + if (num instanceof Long) { + return Long.compare((Long) num, Long.parseLong(anotherNum)); + } + if (num instanceof Double) { + return Double.compare((Double) num, Double.parseDouble(anotherNum)); + } + if (num instanceof Float) { + return Float.compare((Float) num, Float.parseFloat(anotherNum)); + } + } catch (NumberFormatException e) { + return 1; + } + return 1; + } + + public Object getActual() { + return actual; + } + + public SupportedType getType() { + return type; + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/AndCondition.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/AndCondition.java new file mode 100644 index 00000000000..ccb033ef422 --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/AndCondition.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.darklaunch.oper; + +public class AndCondition extends AbstractCondition { + private final Condition[] conditions; + + public AndCondition(Condition... conditions) { + super("and", "and"); + this.conditions = conditions; + } + + @Override + public void setActual(String key, Object actual) { + for (Condition condition : this.conditions) { + condition.setActual(key, actual); + } + } + + @Override + public boolean match() { + for (Condition condition : this.conditions) { + if (!condition.match()) { + return false; + } + } + return true; + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/CaseInsensitiveCondition.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/CaseInsensitiveCondition.java new file mode 100644 index 00000000000..e47e17241bf --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/CaseInsensitiveCondition.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.darklaunch.oper; + +public class CaseInsensitiveCondition extends AbstractCondition { + private final Condition condition; + + public CaseInsensitiveCondition(Condition condition) { + super(condition.key(), condition.expected()); + this.condition = condition; + } + + @Override + public void setActual(String key, Object actual) { + if (null == actual) { + condition.setActual(key, null); + return; + } + actual = actual.toString().toLowerCase(); + condition.setActual(key, actual); + } + + @Override + public boolean match() { + return condition.match(); + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/Condition.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/Condition.java new file mode 100644 index 00000000000..bb8489a4828 --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/Condition.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.darklaunch.oper; + + +public interface Condition { + String key(); + + String expected(); + + void setActual(String key, Object actual); + + boolean match(); +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/ConditionFactory.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/ConditionFactory.java new file mode 100644 index 00000000000..d7b410c35b8 --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/ConditionFactory.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.oper; + +import java.util.Arrays; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.darklaunch.DarklaunchRule; + +public class ConditionFactory { + public static final String OP_AND = "&&"; + + public static final String OP_OR = "||"; + + public static final String OP_OR_ESCAPE = "\\|\\|"; + + private static final String[] OP_LIST = {">=", "<=", "!=", "=", ">", "<", "~"}; + + public static final String SEP_COLON = ","; + + private static String[] split2Part(String str, String sep) { + int index = str.indexOf(sep); + if (index > 0) { + return new String[] { + str.substring(0, index), str.substring(index + sep.length()) + }; + } else { + return new String[] {str}; + } + } + + private static String[] split(String str, String sep) { + return Arrays.stream(str.split(sep)) + .filter(s -> !StringUtils.isEmpty(s)).toArray(String[]::new); + } + + public static Condition buildRateCondition(String strCondition) { + return new LessCondition(DarklaunchRule.PROP_PERCENT, strCondition); + } + + public static Condition buildCondition(String strCondition, boolean caseInsensitive) { + if (strCondition.contains(OP_AND)) { + String[] rules = split(strCondition, OP_AND); + Condition[] conditions = new Condition[rules.length]; + for (int i = 0; i < conditions.length; i++) { + conditions[i] = buildGroupConditionItem(rules[i], caseInsensitive); + } + return new AndCondition(conditions); + } else if (strCondition.contains(OP_OR)) { + String[] rules = split(strCondition, OP_OR_ESCAPE); + Condition[] conditions = new Condition[rules.length]; + for (int i = 0; i < conditions.length; i++) { + conditions[i] = buildGroupConditionItem(rules[i], caseInsensitive); + } + return new OrCondition(conditions); + } else { + return buildGroupConditionItem(strCondition, caseInsensitive); + } + } + + private static Condition buildGroupConditionItem(String groupCondition, boolean caseInsensitive) { + for (int index = 0; index < OP_LIST.length; index++) { + if (groupCondition.contains(OP_LIST[index])) { + String[] pairs = split2Part(groupCondition, OP_LIST[index]); + if (pairs[1].contains(SEP_COLON)) { + String[] values = split(pairs[1], SEP_COLON); + Condition[] conditions = new Condition[values.length]; + for (int i = 0; i < values.length; i++) { + conditions[i] = buildCondition(index, pairs[0], values[i], caseInsensitive); + } + return new OrCondition(conditions); + } else { + return buildCondition(index, pairs[0], pairs[1], caseInsensitive); + } + } + } + throw new IllegalArgumentException(groupCondition); + } + + private static Condition buildCondition(int index, String key, String value, boolean caseInsensitive) { + value = caseInsensitive ? value.toLowerCase() : value; + Condition condition = buildCondition(index, key, value); + if (caseInsensitive) { + return new CaseInsensitiveCondition(condition); + } + return condition; + } + + private static Condition buildCondition(int index, String key, String value) { + switch (index) { + case 0: + return new GreaterOrEqualCondition(key, value); + case 1: + return new LessOrEqualCondition(key, value); + case 2: + return new NotEqualCondition(key, value); + case 3: + return new EqualCondition(key, value); + case 4: + return new GreaterCondition(key, value); + case 5: + return new LessCondition(key, value); + default: + return new LikeCondition(key, value); + } + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/EqualCondition.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/EqualCondition.java new file mode 100644 index 00000000000..1073c10ff9f --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/EqualCondition.java @@ -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. + */ + +package org.apache.servicecomb.darklaunch.oper; + +public class EqualCondition extends AbstractCondition { + public EqualCondition(String key, String expected) { + super(key, expected); + } + + @Override + public boolean match() { + SupportedType type = this.getType(); + if (type == SupportedType.NUMBER) { + return compareNum(this.getActual(), this.expected()) == 0; + } else if (type == SupportedType.STRING) { + return ((String) this.getActual()).compareTo(this.expected()) == 0; + } else { + return false; + } + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/GreaterCondition.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/GreaterCondition.java new file mode 100644 index 00000000000..6709c21975c --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/GreaterCondition.java @@ -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. + */ + +package org.apache.servicecomb.darklaunch.oper; + +public class GreaterCondition extends AbstractCondition { + public GreaterCondition(String key, String expected) { + super(key, expected); + } + + @Override + public boolean match() { + SupportedType type = this.getType(); + if (type == SupportedType.NUMBER) { + return compareNum(this.getActual(), this.expected()) > 0; + } else if (type == SupportedType.STRING) { + return ((String) this.getActual()).compareTo(this.expected()) > 0; + } else { + return false; + } + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/GreaterOrEqualCondition.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/GreaterOrEqualCondition.java new file mode 100644 index 00000000000..28139bb5c12 --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/GreaterOrEqualCondition.java @@ -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. + */ + +package org.apache.servicecomb.darklaunch.oper; + +public class GreaterOrEqualCondition extends AbstractCondition { + public GreaterOrEqualCondition(String key, String expected) { + super(key, expected); + } + + @Override + public boolean match() { + SupportedType type = this.getType(); + if (type == SupportedType.NUMBER) { + return compareNum(this.getActual(), this.expected()) >= 0; + } else if (type == SupportedType.STRING) { + return ((String) this.getActual()).compareTo(this.expected()) >= 0; + } else { + return false; + } + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/LessCondition.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/LessCondition.java new file mode 100644 index 00000000000..6acce8e34c8 --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/LessCondition.java @@ -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. + */ + +package org.apache.servicecomb.darklaunch.oper; + +public class LessCondition extends AbstractCondition { + public LessCondition(String key, String expected) { + super(key, expected); + } + + @Override + public boolean match() { + SupportedType type = this.getType(); + if (type == SupportedType.NUMBER) { + return compareNum(this.getActual(), this.expected()) < 0; + } else if (type == SupportedType.STRING) { + return ((String) this.getActual()).compareTo(this.expected()) < 0; + } else { + return false; + } + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/LessOrEqualCondition.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/LessOrEqualCondition.java new file mode 100644 index 00000000000..b7eea4eaaff --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/LessOrEqualCondition.java @@ -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. + */ + +package org.apache.servicecomb.darklaunch.oper; + +public class LessOrEqualCondition extends AbstractCondition { + public LessOrEqualCondition(String key, String expected) { + super(key, expected); + } + + @Override + public boolean match() { + SupportedType type = this.getType(); + if (type == SupportedType.NUMBER) { + return compareNum(this.getActual(), this.expected()) <= 0; + } else if (type == SupportedType.STRING) { + return ((String) this.getActual()).compareTo(this.expected()) <= 0; + } else { + return false; + } + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/LikeCondition.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/LikeCondition.java new file mode 100644 index 00000000000..790ba9a57ad --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/LikeCondition.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.darklaunch.oper; + +import java.util.regex.Pattern; + +public class LikeCondition extends AbstractCondition { + private final Pattern pattern; + + public LikeCondition(String key, String expected) { + super(key, expected); + + char[] cs = expected.toCharArray(); + StringBuilder regExp = new StringBuilder(); + int lastPos = 0; + for (int i = 0; i < cs.length; i++) { + if ((cs[i]) == '*') { + regExp.append(Pattern.quote(new String(cs, lastPos, i - lastPos))); + regExp.append(".*"); + lastPos = i + 1; + } else if (cs[i] == '?') { + regExp.append(Pattern.quote(new String(cs, lastPos, i - lastPos))); + regExp.append("."); + lastPos = i = 1; + } + } + regExp.append(Pattern.quote(new String(cs, lastPos, cs.length - lastPos))); + pattern = Pattern.compile(regExp.toString()); + } + + @Override + public boolean match() { + SupportedType type = this.getType(); + if (type == SupportedType.STRING) { + return this.pattern.matcher((String) this.getActual()).matches(); + } else { + return false; + } + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/NotEqualCondition.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/NotEqualCondition.java new file mode 100644 index 00000000000..e5cde3724ee --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/NotEqualCondition.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.darklaunch.oper; + +public class NotEqualCondition extends AbstractCondition { + + public NotEqualCondition(String key, String expected) { + super(key, expected); + } + + @Override + public boolean match() { + SupportedType type = this.getType(); + if (type == SupportedType.NUMBER) { + return compareNum(this.getActual(), this.expected()) != 0; + } else if (type == SupportedType.STRING) { + return ((String) this.getActual()).compareTo(this.expected()) != 0; + } else { + return false; + } + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/OrCondition.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/OrCondition.java new file mode 100644 index 00000000000..8c1374f8cc9 --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/OrCondition.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.darklaunch.oper; + +public class OrCondition extends AbstractCondition { + private final Condition[] conditions; + + public OrCondition(Condition... conditions) { + super("or", "or"); + this.conditions = conditions; + } + + @Override + public void setActual(String key, Object actual) { + for (Condition condition : this.conditions) { + condition.setActual(key, actual); + } + } + + @Override + public boolean match() { + for (Condition condition : this.conditions) { + if (condition.match()) { + return true; + } + } + return false; + } +} diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/SupportedType.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/SupportedType.java new file mode 100644 index 00000000000..2308ad45921 --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/oper/SupportedType.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.darklaunch.oper; + +public enum SupportedType { + STRING, + NUMBER, + UNKNOWN +} 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 new file mode 100644 index 00000000000..1104043c1cc --- /dev/null +++ b/huawei-cloud/darklaunch/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt @@ -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.darklaunch.DarklaunchServerListFilter diff --git a/huawei-cloud/dashboard/pom.xml b/huawei-cloud/dashboard/pom.xml new file mode 100644 index 00000000000..015a43a240a --- /dev/null +++ b/huawei-cloud/dashboard/pom.xml @@ -0,0 +1,55 @@ + + + + + huawei-cloud + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + dashboard + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + java-chassis-core + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + dashboard-client + + + + + org.apache.servicecomb + metrics-core + provided + + + + 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 new file mode 100644 index 00000000000..4889683c131 --- /dev/null +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DataFactory.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.huaweicloud.dashboard.monitor; + +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +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; + +public class DataFactory { + private static final Logger LOGGER = LoggerFactory.getLogger(DataFactory.class); + + private static final int CORE_SIZE = 1; + + private boolean hasStart = false; + + @Autowired + private List dataProviders; + + @Autowired + private MonitorDataPublisher publisher; + + @Autowired + private MonitorConstant monitorConstant; + + private ScheduledExecutorService executorService = null; + + + public DataFactory() { + ThreadFactory threadFactory = new DefaultThreadFactory("monitor-datafactory"); + executorService = Executors.newScheduledThreadPool(CORE_SIZE, threadFactory); + } + + public void setMonitorDataProviders(List dataProviders) { + this.dataProviders = dataProviders; + } + + public void setMonitorDataPublisher(MonitorDataPublisher publisher) { + this.publisher = publisher; + } + + void start() { + if (!hasStart) { + publisher.init(); + + StringBuilder sb = new StringBuilder(); + sb.append("Monitor data sender started. Configured data providers is {"); + for (MonitorDataProvider provider : dataProviders) { + sb.append(provider.getClass().getName()); + sb.append(","); + } + sb.append("}"); + LOGGER.info(sb.toString()); + + executorService.scheduleWithFixedDelay(() -> { + try { + sendData(); + } catch (Throwable e) { + LOGGER.error("send monitor data error.", e); + } + }, monitorConstant.getInterval(), monitorConstant.getInterval(), TimeUnit.MILLISECONDS); + hasStart = true; + } + } + + void sendData() { + for (MonitorDataProvider provider : this.dataProviders) { + if (provider.enabled()) { + this.publisher.publish(provider); + } + } + } +} 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 new file mode 100644 index 00000000000..1209d9d8532 --- /dev/null +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.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.huaweicloud.dashboard.monitor; + +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.ConfigUtil; +import org.apache.servicecomb.dashboard.client.DashboardAddressManager; +import org.apache.servicecomb.dashboard.client.DashboardClient; +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.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(); + + RequestConfig.Builder requestBuilder = HttpTransportFactory.defaultRequestConfig(); + requestBuilder.setConnectionRequestTimeout(1000); + requestBuilder.setSocketTimeout(10000); + + HttpTransport httpTransport = createHttpTransport(addressManager, requestBuilder.build(), + environment); + + dashboardClient = new DashboardClient(addressManager, httpTransport); + } + + @SuppressWarnings("unchecked") + private DashboardAddressManager createDashboardAddressManager() { + 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(), region, availableZone); + } + + private HttpTransport createHttpTransport(DashboardAddressManager addressManager, RequestConfig requestConfig, + Environment environment) { + List authHeaderProviders = SPIServiceUtils.getOrLoadSortedService(AuthHeaderProvider.class); + + if (monitorConstant.isProxyEnable()) { + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). + setDefaultRequestConfig(requestConfig); + 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())); + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + + return HttpTransportFactory + .createHttpTransport( + TransportUtils + .createSSLProperties(addressManager.sslEnabled(), environment, SSL_KEY), + getRequestAuthHeaderProvider(authHeaderProviders), httpClientBuilder); + } + + return HttpTransportFactory + .createHttpTransport( + TransportUtils + .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(host))); + return headers; + }; + } + + @Override + public void publish(MonitorDataProvider provider) { + dashboardClient.sendData(provider.getURL(), provider.getData()); + } +} 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 new file mode 100644 index 00000000000..ed54eef341e --- /dev/null +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java @@ -0,0 +1,242 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 java.util.HashMap; +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.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 io.micrometer.core.instrument.Meter; + +/** + * Monitor data based on metrics-core module. + */ +public class MetricsMonitorDataProvider implements MonitorDataProvider { + + public static final String CODE_SUCCESS = "2[0-9]{2}"; + + public static final String CODE_TIMEOUT = "408"; + + public static final String NAME_PROVIDER = "Provider."; + + public static final String NAME_CONSUMER = "Consumer."; + + 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 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 + public void extractInterfaceInfo(MonitorData monitorData) { + if (meters == null) { + return; + } + PublishModelFactory factory = new PublishModelFactory(meters); + DefaultPublishModel model = factory.createDefaultPublishModel(); + + Map combinedResults = new HashMap<>(); + extractProviderInfo(model, combinedResults); + extractConsumerInfo(model, combinedResults); + extractEdgeInfo(model, combinedResults); + combinedResults.forEach((k, v) -> { + v.setFailureRate(v.getTotal() == 0 ? 0 : v.getFailure() / (double) v.getTotal()); + monitorData.addInterfaceInfo(v); + }); + } + + private void extractProviderInfo(DefaultPublishModel model, Map combinedResults) { + OperationPerfGroups producerPerf = model.getProducer().getOperationPerfGroups(); + if (producerPerf == null) { + return; + } + + for (Map statusMap : producerPerf.getGroups().values()) { + for (OperationPerfGroup perfGroup : statusMap.values()) { + for (int i = 0; i < perfGroup.getOperationPerfs().size(); i++) { + OperationPerf operationPerf = perfGroup.getOperationPerfs().get(i); + PerfInfo stageTotal = operationPerf.findStage(InvocationStageTrace.STAGE_TOTAL); + String name = NAME_PROVIDER + operationPerf.getOperation(); + InterfaceInfo interfaceInfo = combinedResults.computeIfAbsent(name, + k -> { + InterfaceInfo obj = new InterfaceInfo(); + obj.setName(name); + return obj; + }); + // dashboard calculates the latest 10 seconds, different with metrics cycle. + interfaceInfo.setTotal( + doubleToInt(interfaceInfo.getTotal() + stageTotal.getTotalRequests())); + if (perfGroup.getStatus().matches(CODE_SUCCESS)) { + interfaceInfo.setQps(stageTotal.getTotalRequests() / 10); + interfaceInfo.setLatency(doubleToInt(stageTotal.calcMsLatency())); + } else { + interfaceInfo.setFailure( + doubleToInt(interfaceInfo.getTotal() + stageTotal.getTotalRequests())); + if (perfGroup.getStatus().equals(CODE_TIMEOUT)) { + interfaceInfo.setCountTimeout( + doubleToInt(interfaceInfo.getCountTimeout() + stageTotal.getTotalRequests())); + } + } + } + } + } + } + + private void extractEdgeInfo(DefaultPublishModel model, Map combinedResults) { + OperationPerfGroups edgePerf = model.getEdge().getOperationPerfGroups(); + if (edgePerf == null) { + return; + } + for (Map statusMap : edgePerf.getGroups().values()) { + for (OperationPerfGroup perfGroup : statusMap.values()) { + for (int i = 0; i < perfGroup.getOperationPerfs().size(); i++) { + OperationPerf operationPerf = perfGroup.getOperationPerfs().get(i); + PerfInfo stageTotal = operationPerf.findStage(InvocationStageTrace.STAGE_TOTAL); + String name = NAME_CONSUMER + operationPerf.getOperation(); + InterfaceInfo interfaceInfo = combinedResults.computeIfAbsent(name, + k -> { + InterfaceInfo obj = new InterfaceInfo(); + obj.setName(name); + return obj; + }); + // dashboard calculates the latest 10 seconds, different with metrics cycle. + interfaceInfo.setTotal( + doubleToInt(interfaceInfo.getTotal() + stageTotal.getTotalRequests())); + if (perfGroup.getStatus().matches(CODE_SUCCESS)) { + interfaceInfo.setQps(stageTotal.getTotalRequests() / 10); + interfaceInfo.setLatency(doubleToInt(stageTotal.calcMsLatency())); + } else { + interfaceInfo.setFailure( + doubleToInt(interfaceInfo.getTotal() + stageTotal.getTotalRequests())); + if (perfGroup.getStatus().equals(CODE_TIMEOUT)) { + interfaceInfo.setCountTimeout( + doubleToInt( + interfaceInfo.getCountTimeout() + stageTotal.getTotalRequests())); + } + } + } + } + } + } + + private void extractConsumerInfo(DefaultPublishModel model, Map combinedResults) { + OperationPerfGroups consumerPerf = model.getConsumer().getOperationPerfGroups(); + if (consumerPerf == null) { + return; + } + for (Map statusMap : consumerPerf.getGroups().values()) { + for (OperationPerfGroup perfGroup : statusMap.values()) { + for (int i = 0; i < perfGroup.getOperationPerfs().size(); i++) { + OperationPerf operationPerf = perfGroup.getOperationPerfs().get(i); + PerfInfo stageTotal = operationPerf.findStage(InvocationStageTrace.STAGE_TOTAL); + String name = NAME_CONSUMER + operationPerf.getOperation(); + InterfaceInfo interfaceInfo = combinedResults.computeIfAbsent(name, + k -> { + InterfaceInfo obj = new InterfaceInfo(); + obj.setName(name); + return obj; + }); + // dashboard calculates the latest 10 seconds, different with metrics cycle. + interfaceInfo.setTotal( + doubleToInt(interfaceInfo.getTotal() + stageTotal.getTotalRequests())); + if (perfGroup.getStatus().matches(CODE_SUCCESS)) { + interfaceInfo.setQps(stageTotal.getTotalRequests() / 10); + interfaceInfo.setLatency(doubleToInt(stageTotal.calcMsLatency())); + } else { + interfaceInfo.setFailure( + doubleToInt(interfaceInfo.getTotal() + stageTotal.getTotalRequests())); + if (perfGroup.getStatus().equals(CODE_TIMEOUT)) { + interfaceInfo.setCountTimeout( + doubleToInt( + interfaceInfo.getCountTimeout() + stageTotal.getTotalRequests())); + } + } + } + } + } + } + + private int doubleToInt(Double d) { + return d.intValue(); + } + + @Subscribe + public void onPolledEvent(PolledEvent event) { + this.meters = event.getMeters(); + } +} diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorBootListener.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorBootListener.java new file mode 100644 index 00000000000..06dd4aa6d63 --- /dev/null +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorBootListener.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.huaweicloud.dashboard.monitor; + +import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; +import org.springframework.beans.factory.annotation.Autowired; + +public class MonitorBootListener implements BootListener { + + @Autowired + private DataFactory dataFactory; + + @Autowired + private MonitorConstant monitorConstant; + + @Override + public void onBootEvent(BootEvent event) { + if (!monitorConstant.isMonitorEnabled()) { + return; + } + + if (EventType.AFTER_REGISTRY == event.getEventType()) { + dataFactory.start(); + } + } + + public DataFactory getDataFactory() { + return dataFactory; + } + + public void setDataFactory(DataFactory dataFactory) { + this.dataFactory = dataFactory; + } +} 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 new file mode 100644 index 00000000000..ef8b03a1368 --- /dev/null +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MonitorInformationCollector.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.huaweicloud.dashboard.monitor; + +import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.core.bootup.BootUpInformationCollector; +import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; +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: " + + ConfigUtil.parseArrayValue( + environment.getProperty(MonitorConstant.SYSTEM_KEY_DASHBOARD_SERVICE, "")); + } + + @Override + public int getOrder() { + return 100; + } +} 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 new file mode 100644 index 00000000000..d58482d23af --- /dev/null +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/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.huaweicloud.dashboard.monitor; + +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/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/data/CPUMonitorCalc.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/data/CPUMonitorCalc.java new file mode 100644 index 00000000000..6db0a859e47 --- /dev/null +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/data/CPUMonitorCalc.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.huaweicloud.dashboard.monitor.data; + + +import java.lang.management.ManagementFactory; +import java.lang.management.OperatingSystemMXBean; +import java.lang.management.ThreadMXBean; + +public class CPUMonitorCalc { + private static final int PERCENTAGE = 100; + + private static final CPUMonitorCalc instance = new CPUMonitorCalc(); + + private final OperatingSystemMXBean osMxBean; + + private final ThreadMXBean threadMXBean; + + private long preTime = System.nanoTime(); + + private long preUsedTime = 0; + + private CPUMonitorCalc() { + osMxBean = ManagementFactory.getOperatingSystemMXBean(); + threadMXBean = ManagementFactory.getThreadMXBean(); + } + + public static CPUMonitorCalc getInstance() { + return instance; + } + + public double getProcessCpu() { + long totalTime = 0; + + for (long id : threadMXBean.getAllThreadIds()) { + totalTime += threadMXBean.getThreadCpuTime(id); + } + + long curtime = System.nanoTime(); + long usedTime = totalTime - preUsedTime; + long totalPassedTime = curtime - preTime; + preTime = curtime; + preUsedTime = totalTime; + return (((double) usedTime) / totalPassedTime / osMxBean.getAvailableProcessors()) * PERCENTAGE; + } +} 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 new file mode 100644 index 00000000000..19b20787843 --- /dev/null +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/data/MonitorConstant.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.huaweicloud.dashboard.monitor.data; + +import org.springframework.core.env.Environment; + +public class MonitorConstant { + public static final String SYSTEM_KEY_DASHBOARD_SERVICE = "DashboardService"; + + public static final String MONITOR_URI = "servicecomb.monitor.client.serverUri"; + + public static final String PROXY_ENABLE = "servicecomb.proxy.enable"; + + public static final String PROXY_HOST = "servicecomb.proxy.host"; + + public static final String PROXY_PORT = "servicecomb.proxy.port"; + + public static final String PROXY_USERNAME = "servicecomb.proxy.username"; + + public static final String PROXY_PASSWD = "servicecomb.proxy.passwd"; + + public static final int MIN_INTERVAL_MILLISECONDS = 5000; + + public static final int DEFAULT_TIMEOUT = 5000; + + public static final int DEFAULT_INTERVAL = 10000; + + private final String DOMAIN_NAME; + + private final String CURRENT_VERSION; + + private final String VERSION_V1; + + private final String PREFIX_V2; + + private final String monitorsUri; + + private Environment environment; + + 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(); + + if (VERSION_V1.equals(CURRENT_VERSION)) { + monitorsUri = "/csemonitor/v1/metric?service=%s"; + } else { + monitorsUri = PREFIX_V2 + "/metric?service=%s"; + } + } + + public String getMonitorUri() { + return this.monitorsUri; + } + + public String getDomainName() { + return environment.getProperty("servicecomb.config.client.domainName", "default"); + } + + public String getApiVersion() { + return environment.getProperty("servicecomb.monitor.client.api.version", "v2"); + } + + public String getServerUrl() { + return environment.getProperty("servicecomb.monitor.client.serverUri"); + } + + public boolean sslEnabled() { + return environment.getProperty("servicecomb.monitor.client.sslEnabled", boolean.class, true); + } + + public boolean isMonitorEnabled() { + return environment.getProperty("servicecomb.monitor.client.enabled", boolean.class, false); + } + + public int getConnectionTimeout() { + return environment.getProperty("servicecomb.monitor.client.timeout", int.class, DEFAULT_TIMEOUT); + } + + public int getInterval() { + return Math.max(environment.getProperty("servicecomb.monitor.client.interval", int.class, DEFAULT_INTERVAL), + MIN_INTERVAL_MILLISECONDS); + } + + public Boolean isProxyEnable() { + return Boolean.parseBoolean(getProperty("false", PROXY_ENABLE)); + } + + public String getProxyHost() { + return getProperty("127.0.0.1", PROXY_HOST); + } + + public int getProxyPort() { + return Integer.parseInt(getProperty("8080", PROXY_PORT)); + } + + public String getProxyUsername() { + return getProperty(null, PROXY_USERNAME); + } + + public String getProxyPasswd() { + return getProperty(null, PROXY_PASSWD); + } + + private String getProperty(String defaultValue, String... keys) { + String property = null; + for (String key : keys) { + property = environment.getProperty(key); + if (property != null) { + break; + } + } + + if (property != null) { + return property; + } + return defaultValue; + } +} 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 new file mode 100644 index 00000000000..4f88a5a4492 --- /dev/null +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/model/MonitorDataProvider.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.huaweicloud.dashboard.monitor.model; + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryUsage; +import java.lang.management.OperatingSystemMXBean; +import java.lang.management.RuntimeMXBean; +import java.lang.management.ThreadMXBean; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.dashboard.client.model.MonitorData; +import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.CPUMonitorCalc; + +public interface MonitorDataProvider { + boolean enabled(); + + String getURL(); + + void extractServiceInfo(MonitorData monitorData); + + default void exactProcessInfo(MonitorData monitorData) { + MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); + MemoryUsage memoryHeapUsage = memoryMXBean.getHeapMemoryUsage(); + MemoryUsage memoryNonHeapUsage = memoryMXBean.getNonHeapMemoryUsage(); + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); + RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); + int threadCount = threadMXBean.getThreadCount(); + OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); + + double cpu = operatingSystemMXBean.getSystemLoadAverage(); + monitorData.setCpu(CPUMonitorCalc.getInstance().getProcessCpu()); + monitorData.setLoadAverage(cpu); + monitorData.setThreadCount(threadCount); + monitorData.setUptime(runtimeMXBean.getUptime()); + + Map memoryInfo = new HashMap<>(); + memoryInfo.put("heapInit", memoryHeapUsage.getInit()); + memoryInfo.put("headMax", memoryHeapUsage.getMax()); + memoryInfo.put("heapCommit", memoryHeapUsage.getCommitted()); + memoryInfo.put("heapUsed", memoryHeapUsage.getUsed()); + memoryInfo.put("nonHeapInit", memoryNonHeapUsage.getInit()); + memoryInfo.put("nonHeapCommit", memoryNonHeapUsage.getCommitted()); + memoryInfo.put("nonHeapUsed", memoryNonHeapUsage.getUsed()); + monitorData.setMemory(memoryInfo); + } + + void extractInterfaceInfo(MonitorData monitorData); + + default MonitorData getData() { + MonitorData monitorData = new MonitorData(); + extractServiceInfo(monitorData); + exactProcessInfo(monitorData); + extractInterfaceInfo(monitorData); + return monitorData; + } +} diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/model/MonitorDataPublisher.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/model/MonitorDataPublisher.java new file mode 100644 index 00000000000..7876c2e3f64 --- /dev/null +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/model/MonitorDataPublisher.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.huaweicloud.dashboard.monitor.model; + +public interface MonitorDataPublisher { + void publish(MonitorDataProvider provider); + + default void init() { + + } +} 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 new file mode 100644 index 00000000000..008bc6a1526 --- /dev/null +++ b/huawei-cloud/dashboard/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector @@ -0,0 +1,17 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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.MonitorInformationCollector 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/test/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProviderTest.java b/huawei-cloud/dashboard/src/test/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProviderTest.java new file mode 100644 index 00000000000..c5411febdb8 --- /dev/null +++ b/huawei-cloud/dashboard/src/test/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProviderTest.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.huaweicloud.dashboard.monitor; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class MetricsMonitorDataProviderTest { + @Test + public void testCodeMatch() { + Assertions.assertTrue("200".matches(MetricsMonitorDataProvider.CODE_SUCCESS)); + Assertions.assertTrue("202".matches(MetricsMonitorDataProvider.CODE_SUCCESS)); + Assertions.assertFalse("2002".matches(MetricsMonitorDataProvider.CODE_SUCCESS)); + Assertions.assertFalse("400".matches(MetricsMonitorDataProvider.CODE_SUCCESS)); + } +} diff --git a/huawei-cloud/dashboard/src/test/resources/microservice.yaml b/huawei-cloud/dashboard/src/test/resources/microservice.yaml new file mode 100644 index 00000000000..363e3369fa0 --- /dev/null +++ b/huawei-cloud/dashboard/src/test/resources/microservice.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. +## --------------------------------------------------------------------------- +# spring boot configurations +server: + port: 9093 # should be same with servicecomb.rest.address to use web container + +# override common configurations in common module +servicecomb-config-order: 10 +instance_description: + environment: production + +servicecomb: + service: + environment: development + application: demo-java-chassis-dashboard + name: test-dashboard + version: 0.0.1 + registry: + address: http://127.0.0.1:30100 + instance: + watch: false + kie: + serverUri: http://127.0.0.1:30110 + customLabel: public + + rest: + address: 0.0.0.0:9097 # should be same with server.port to use web container diff --git a/huawei-cloud/pom.xml b/huawei-cloud/pom.xml new file mode 100644 index 00000000000..eb70052ed01 --- /dev/null +++ b/huawei-cloud/pom.xml @@ -0,0 +1,39 @@ + + + + + org.apache.servicecomb + java-chassis-parent + 3.4.0-SNAPSHOT + ../parents/default + + + huawei-cloud + Java Chassis::Huawei Cloud + pom + 4.0.0 + + + servicestage + dashboard + darklaunch + + + diff --git a/huawei-cloud/servicestage/pom.xml b/huawei-cloud/servicestage/pom.xml new file mode 100644 index 00000000000..d92064951f9 --- /dev/null +++ b/huawei-cloud/servicestage/pom.xml @@ -0,0 +1,50 @@ + + + + + huawei-cloud + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + servicestage + Java Chassis::Huawei Cloud::ServiceStage + + + + org.apache.servicecomb + java-chassis-core + + + org.apache.servicecomb + registry-service-center + + + org.apache.servicecomb + foundation-test-scaffolding + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + + 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 new file mode 100644 index 00000000000..e80e867cab4 --- /dev/null +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/AKSKAuthHeaderProvider.java @@ -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. + */ + +package org.apache.servicecomb.huaweicloud.servicestage; + +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; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.lang3.StringUtils; +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, BootStrapService { + private static final Logger LOGGER = LoggerFactory.getLogger(AKSKAuthHeaderProvider.class); + + private static final String CONFIG_AKSK_ENABLED = "servicecomb.credentials.akskEnabled"; + + private static final String CONFIG_ACCESS_KEY = "servicecomb.credentials.accessKey"; + + private static final String CONFIG_SECRET_KEY = "servicecomb.credentials.secretKey"; + + private static final String CONFIG_CIPHER = "servicecomb.credentials.akskCustomCipher"; + + private static final String CONFIG_PROJECT = "servicecomb.credentials.project"; + + private static final String VALUE_DEFAULT_PROJECT = "default"; + + private static final String VALUE_DEFAULT_CIPHER = "default"; + + private static final String X_SERVICE_AK = "X-Service-AK"; + + private static final String X_SERVICE_SHAAKSK = "X-Service-ShaAKSK"; + + private static final String X_SERVICE_PROJECT = "X-Service-Project"; + + private static Environment environment; + + private final Map headers = new HashMap<>(); + + private boolean loaded = false; + + public AKSKAuthHeaderProvider() { + } + + public Map authHeaders() { + 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); + return Collections.emptyMap(); + } + + if (!loaded) { + load(); + } + return headers; + } + + private synchronized void load() { + if (!loaded) { + headers.put(X_SERVICE_AK, getAccessKey()); + headers.put(X_SERVICE_SHAAKSK, getSecretKey()); + headers.put(X_SERVICE_PROJECT, getProject()); + loaded = true; + } + } + + private String getAccessKey() { + return environment.getProperty(CONFIG_ACCESS_KEY, ""); + } + + private String getCipher() { + return environment.getProperty(CONFIG_CIPHER, VALUE_DEFAULT_CIPHER); + } + + private String getSecretKey() { + String secretKey = environment.getProperty(CONFIG_SECRET_KEY, ""); + String decodedSecretKey = new String(findCipher().decrypt(secretKey.toCharArray())); + + // ShaAKSKCipher 不解密, 认证的时候不处理;其他算法解密为 plain,需要 encode 为 ShaAKSKCipher 去认证。 + if (ShaAKSKCipher.CIPHER_NAME.equalsIgnoreCase(getCipher())) { + return decodedSecretKey; + } else { + return sha256Encode(decodedSecretKey, getAccessKey()); + } + } + + private String getProject() { + String project = environment.getProperty(CONFIG_PROJECT, VALUE_DEFAULT_PROJECT); + if (StringUtils.isEmpty(project)) { + return project; + } + return URLEncoder.encode(project, StandardCharsets.UTF_8); + } + + private Cipher findCipher() { + if (DefaultCipher.CIPHER_NAME.equals(getCipher())) { + return DefaultCipher.getInstance(); + } + + List ciphers = SPIServiceUtils.getOrLoadSortedService(Cipher.class); + return ciphers.stream().filter(c -> c.name().equals(getCipher())).findFirst() + .orElseThrow(() -> new IllegalArgumentException("failed to find cipher named " + getCipher())); + } + + public static String sha256Encode(String key, String data) { + try { + Mac sha256HMAC = Mac.getInstance("HmacSHA256"); + SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), + "HmacSHA256"); + sha256HMAC.init(secretKey); + return Hex.encodeHexString(sha256HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8))); + } catch (Exception e) { + 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 new file mode 100644 index 00000000000..baadb927b40 --- /dev/null +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/CasEnvConfig.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.commons.lang3.StringUtils.EMPTY; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; + +public class CasEnvConfig { + private static final String APPLICATION_ID = "CAS_APPLICATION_ID"; + + private static final String COMPONENT_NAME = "CAS_COMPONENT_NAME"; + + private static final String INSTANCE_VERSION = "CAS_INSTANCE_VERSION"; + + private static final String INSTANCE_ID = "CAS_INSTANCE_ID"; + + private static final String ENVIRONMENT_ID = "CAS_ENVIRONMENT_ID"; + + private static final String SERVICE_PROPS = "SERVICECOMB_SERVICE_PROPS"; + + private static final String INSTANCE_PROPS = "SERVICECOMB_INSTANCE_PROPS"; + + public static final CasEnvConfig INSTANCE = new CasEnvConfig(); + + private Map parseProps(String value) { + Map rs = new HashMap<>(); + if (StringUtils.isEmpty(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])); + } + + public Map getNonEmptyInstanceProperties() { + Map map = new HashMap<>(); + + 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)); + + instanceProps.putAll(parseProps(LegacyPropertyFactory + .getStringProperty(INSTANCE_PROPS, EMPTY))); + + return instanceProps; + } + + public Map getNonEmptyServiceProperties() { + 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 new file mode 100644 index 00000000000..41cfc848df6 --- /dev/null +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/CasEnvVariablesAdapter.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.huaweicloud.servicestage; + +import java.util.Map; + +import org.apache.servicecomb.registry.RegistrationManager; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; + +public class CasEnvVariablesAdapter implements InitializingBean { + + private RegistrationManager registrationManager; + + @Autowired + public void setRegistrationManager(RegistrationManager registrationManager) { + this.registrationManager = registrationManager; + } + + @Override + 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/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenAuthHeaderProvider.java b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenAuthHeaderProvider.java new file mode 100644 index 00000000000..19bd550602b --- /dev/null +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenAuthHeaderProvider.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.huaweicloud.servicestage; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; + +public class TokenAuthHeaderProvider implements AuthHeaderProvider { + @Override + public Map authHeaders(String host) { + String token = TokenCacheManager.getInstance().getToken(host); + if (StringUtils.isEmpty(token)) { + return new HashMap<>(); + } + + HashMap header = new HashMap<>(); + header.put("Authorization", "Bearer " + token); + return Collections.unmodifiableMap(header); + } +} 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 new file mode 100644 index 00000000000..a78d2c2e33b --- /dev/null +++ b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.AuthHeaderProvider @@ -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.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 new file mode 100644 index 00000000000..8d0efa25aef --- /dev/null +++ b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.Cipher @@ -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.foundation.auth.ShaAKSKCipher diff --git a/huawei-cloud/servicestage/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 new file mode 100644 index 00000000000..90c339ba065 --- /dev/null +++ b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.bootstrap.BootStrapService @@ -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.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 new file mode 100644 index 00000000000..2aa5b486121 --- /dev/null +++ b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.adapter.EnvAdapter @@ -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.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 new file mode 100644 index 00000000000..2b82672c77c --- /dev/null +++ b/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestAKSKAuthHeaderProvider.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.huaweicloud.servicestage; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +public class TestAKSKAuthHeaderProvider { + @Test + public void test_project_name_properly_encoded_en() { + 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(); + provider.startup(environment); + Assertions.assertEquals("hello", provider.authHeaders().get("X-Service-Project")); + } + + @Test + public void test_project_name_properly_encoded_cn() { + 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/TestEnvVariablesAdapter.java b/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestEnvVariablesAdapter.java new file mode 100644 index 00000000000..5fc8bea722f --- /dev/null +++ b/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestEnvVariablesAdapter.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.huaweicloud.servicestage; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TestEnvVariablesAdapter { + + @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 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")); + } + + @AfterAll + public static void destroy() { + System.getProperties().remove("CAS_APPLICATION_ID"); + System.getProperties().remove("CAS_ENVIRONMENT_ID"); + System.getProperties().remove("SERVICECOMB_SERVICE_PROPS"); + System.getProperties().remove("SERVICECOMB_INSTANCE_PROPS"); + } +} diff --git a/huawei-cloud/servicestage/src/test/resources/log4j2.xml b/huawei-cloud/servicestage/src/test/resources/log4j2.xml new file mode 100644 index 00000000000..8cee07220ec --- /dev/null +++ b/huawei-cloud/servicestage/src/test/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/integration-tests/jaxrs-tests/pom.xml b/integration-tests/jaxrs-tests/pom.xml deleted file mode 100644 index 78a372aa110..00000000000 --- a/integration-tests/jaxrs-tests/pom.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - integration-tests - io.servicecomb.tests - 0.3.0-SNAPSHOT - - 4.0.0 - - jaxrs-tests - - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-jaxrs - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - test-jar - - - - - - - - \ No newline at end of file diff --git a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/JaxrsIntegrationTestBase.java b/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/JaxrsIntegrationTestBase.java deleted file mode 100644 index a90bfd09749..00000000000 --- a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/JaxrsIntegrationTestBase.java +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.tests; - -import static java.time.temporal.ChronoUnit.SECONDS; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.springframework.http.HttpHeaders.CONTENT_TYPE; -import static org.springframework.http.HttpMethod.GET; -import static org.springframework.http.HttpMethod.POST; -import static org.springframework.http.HttpMethod.PUT; -import static org.springframework.http.HttpStatus.ACCEPTED; -import static org.springframework.http.HttpStatus.OK; -import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE; -import static org.springframework.http.MediaType.APPLICATION_JSON; - -import java.io.IOException; -import java.time.ZonedDateTime; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Ignore; -import org.junit.Test; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; - -import io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.demo.compute.Person; -import io.servicecomb.demo.server.User; - -@Ignore -public class JaxrsIntegrationTestBase { - - private final String baseUrl = "http://127.0.0.1:8080/"; - - private final RestTemplate restTemplate = new RestTemplate(); - - private final String codeFirstUrl = baseUrl + "codeFirstJaxrs/"; - - private final String schemaFirstUrl = baseUrl + "schemaFirstJaxrs/"; - - private final String[] urls = {codeFirstUrl, schemaFirstUrl}; - - @Test - public void ableToQueryAtRootBasePath() { - ResponseEntity responseEntity = restTemplate - .getForEntity(baseUrl + "sayHi?name=Mike", String.class); - - assertThat(responseEntity.getStatusCode(), is(OK)); - assertThat(responseEntity.getBody(), is("Hi Mike")); - } - - @Test - public void ableToQueryAtRootPath() { - ResponseEntity responseEntity = restTemplate - .getForEntity(baseUrl, String.class); - - assertThat(responseEntity.getStatusCode(), is(OK)); - assertThat(responseEntity.getBody(), is("Welcome home")); - } - - @Test - public void ablePostMap() { - Map users = new HashMap<>(); - users.put("user1", userOfNames("name11", "name12")); - users.put("user2", userOfNames("name21", "name22")); - - ParameterizedTypeReference> reference = new ParameterizedTypeReference>() { - }; - for (String url : urls) { - ResponseEntity> responseEntity = restTemplate.exchange(url + "testUserMap", - POST, - jsonRequest(users), - reference); - - assertThat(responseEntity.getStatusCode(), is(OK)); - - Map body = responseEntity.getBody(); - assertArrayEquals(body.get("user1").getNames(), new String[] {"name11", "name12"}); - assertArrayEquals(body.get("user2").getNames(), new String[] {"name21", "name22"}); - } - } - - private User userOfNames(String... names) { - User user1 = new User(); - user1.setNames(names); - return user1; - } - - @Test - public void ableToConsumeTextPlain() { - String body = "a=1"; - - for (String url : urls) { - ResponseEntity responseEntity = restTemplate.postForEntity( - url + "textPlain", - body, - String.class); - - assertEquals(body, jsonBodyOf(responseEntity, String.class)); - } - } - - @Test - public void ableToPostBytes() throws IOException { - byte[] body = new byte[] {0, 1, 2}; - - for (String url : urls) { - byte[] result = restTemplate.postForObject( - url + "bytes", - jsonRequest(RestObjectMapper.INSTANCE.writeValueAsBytes(body)), - byte[].class); - - result = RestObjectMapper.INSTANCE.readValue(result, byte[].class); - - assertEquals(1, result[0]); - assertEquals(1, result[1]); - assertEquals(2, result[2]); - assertEquals(3, result.length); - } - } - - @Test - public void getsJaxrsResponse() { - // String srcMicroserviceName = RegistryUtils.getMicroservice().getServiceName(); - // String context = String.format("{x-cse-src-microservice=%s}", srcMicroserviceName); - - ResponseEntity responseEntity = restTemplate - .getForEntity(codeFirstUrl + "response", User.class); - - assertEquals(202, responseEntity.getStatusCode().value()); - assertEquals("User [name=nameA, age=100, index=0]", responseEntity.getBody().toString()); - // assertEquals("h1v " + context, responseEntity.getHeaders().getFirst("h1")); - // assertEquals("h2v " + context, responseEntity.getHeaders().getFirst("h2")); - } - - @Test - public void ableToPostDate() throws Exception { - ZonedDateTime date = ZonedDateTime.now().truncatedTo(SECONDS); - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("date", RestObjectMapper.INSTANCE.convertToString(Date.from(date.toInstant()))); - - HttpHeaders headers = new HttpHeaders(); - headers.add(CONTENT_TYPE, APPLICATION_FORM_URLENCODED_VALUE); - - int seconds = 1; - for (String url : urls) { - Date result = restTemplate.postForObject(url + "addDate?seconds={seconds}", - new HttpEntity<>(body, headers), - Date.class, - seconds); - - assertEquals(Date.from(date.plusSeconds(seconds).toInstant()), result); - } - } - - @Test - public void ableToDeleteWithQueryString() { - for (String url : urls) { - ResponseEntity responseEntity = restTemplate.exchange(url + "addstring?s=a&s=b", - HttpMethod.DELETE, - null, - String.class); - - assertEquals("ab", responseEntity.getBody()); - } - } - - @Test - public void ableToGetBoolean() { - for (String url : urls) { - boolean result = restTemplate.getForObject(url + "istrue", boolean.class); - assertEquals(true, result); - } - } - - @Test - public void putsEndWithPathParam() { - for (String url : urls) { - ResponseEntity responseEntity = restTemplate - .exchange(url + "sayhi/{name}", PUT, null, String.class, "world"); - - assertThat(responseEntity.getStatusCode(), is(ACCEPTED)); - assertEquals("world sayhi", jsonBodyOf(responseEntity, String.class)); - } - } - - @Test - public void putsContainingPathParam() { - for (String url : urls) { - ResponseEntity responseEntity = restTemplate - .exchange(url + "sayhi/{name}/v2", PUT, null, String.class, "world"); - - assertEquals("world sayhi 2", jsonBodyOf(responseEntity, String.class)); - } - } - - @Test - public void ableToPostWithHeader() { - Person person = new Person(); - person.setName("person name"); - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(APPLICATION_JSON); - headers.add("prefix", "prefix prefix"); - - HttpEntity requestEntity = new HttpEntity<>(person, headers); - for (String url : urls) { - ResponseEntity responseEntity = restTemplate - .postForEntity(url + "saysomething", requestEntity, String.class); - - assertEquals("prefix prefix person name", jsonBodyOf(responseEntity, String.class)); - } - } - - @Test - public void ableToPostObjectAsJson() { - Map personFieldMap = new HashMap<>(); - personFieldMap.put("name", "person name from map"); - - for (String url : urls) { - Person person = restTemplate - .postForObject(url + "sayhello", jsonRequest(personFieldMap), Person.class); - assertEquals("hello person name from map", person.toString()); - - Person input = new Person(); - input.setName("person name from Object"); - person = restTemplate.postForObject(url + "sayhello", jsonRequest(input), Person.class); - - assertEquals("hello person name from Object", person.toString()); - } - } - - @Test - public void ableToPostForm() { - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("a", "5"); - params.add("b", "3"); - - HttpHeaders headers = new HttpHeaders(); - headers.add(CONTENT_TYPE, APPLICATION_FORM_URLENCODED_VALUE); - for (String url : urls) { - int result = restTemplate - .postForObject(url + "add", new HttpEntity<>(params, headers), Integer.class); - - assertEquals(8, result); - } - } - - @Test - public void ableToExchangeCookie() { - Map params = new HashMap<>(); - params.put("a", "5"); - - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.COOKIE, "b=3"); - - HttpEntity requestEntity = new HttpEntity<>(headers); - ResponseEntity result = restTemplate.exchange( - codeFirstUrl + "reduce?a={a}", - GET, - requestEntity, - Integer.class, - params); - - assertThat(result.getBody(), is(2)); - } - - @Test - public void ableToExchangeRequestContext() { - Map params = new HashMap<>(); - params.put("a", "5"); - params.put("b", "3"); - - ResponseEntity result = restTemplate.getForEntity( - schemaFirstUrl + "reduce?a={a}&b={b}", - Integer.class, - params); - - assertThat(result.getBody(), is(2)); - } - - @Test - public void ableToGetAtDefaultPath() { - for (String url : urls) { - int result = restTemplate.getForObject(url, Integer.class); - - assertThat(result, is(100)); - } - } - - private HttpEntity jsonRequest(T body) { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(APPLICATION_JSON); - return new HttpEntity<>(body, headers); - } - - private T jsonBodyOf(ResponseEntity entity, Class aClass) { - try { - return RestObjectMapper.INSTANCE.readValue(entity.getBody(), aClass); - } catch (IOException e) { - throw new IllegalStateException("Failed to read JSON from response " + entity.getBody(), e); - } - } -} diff --git a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/JaxrsTestMain.java b/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/JaxrsTestMain.java deleted file mode 100644 index eee3c62d0eb..00000000000 --- a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/JaxrsTestMain.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.tests; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class JaxrsTestMain { - - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/RawJaxrsIntegrationTest.java b/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/RawJaxrsIntegrationTest.java deleted file mode 100644 index 62d888d84ea..00000000000 --- a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/RawJaxrsIntegrationTest.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.tests; - -import org.junit.BeforeClass; - -public class RawJaxrsIntegrationTest extends JaxrsIntegrationTestBase { - - @BeforeClass - public static void setUp() throws Exception { - JaxrsTestMain.main(new String[0]); - } -} diff --git a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/CodeFirstJaxrs.java b/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/CodeFirstJaxrs.java deleted file mode 100644 index 1ff580f6887..00000000000 --- a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/CodeFirstJaxrs.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.tests.endpoints; - -import static javax.ws.rs.core.MediaType.TEXT_PLAIN; - -import java.util.List; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.CookieParam; -import javax.ws.rs.DELETE; -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.Response.Status; - -import io.servicecomb.demo.server.User; -import io.servicecomb.provider.rest.common.RestSchema; -import io.servicecomb.swagger.extend.annotations.ResponseHeaders; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.context.ContextUtils; -import io.servicecomb.swagger.invocation.context.InvocationContext; -import io.servicecomb.swagger.invocation.response.Headers; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ResponseHeader; - -@RestSchema(schemaId = "codeFirst") -@Path("/codeFirstJaxrs") -public class CodeFirstJaxrs extends SomeAbstractJaxrsRestEndpoint { - - @ApiResponse(code = 200, response = User.class, message = "") - @ResponseHeaders({@ResponseHeader(name = "h1", response = String.class), - @ResponseHeader(name = "h2", response = String.class)}) - @Path("/response") - @GET - public Response response(InvocationContext c1) { - Response response = Response.createSuccess(Status.ACCEPTED, new User()); - Headers headers = response.getHeaders(); - headers.addHeader("h1", "h1v " + c1.getContext().toString()); - - InvocationContext c2 = ContextUtils.getInvocationContext(); - headers.addHeader("h2", "h2v " + c2.getContext().toString()); - - return response; - } - - @Path("/reduce") - @GET - @ApiImplicitParams({ - @ApiImplicitParam(name = "a", dataType = "integer", format = "int32", paramType = "query")}) - public int reduce(HttpServletRequest request, @CookieParam("b") int b) { - int a = Integer.parseInt(request.getParameter("a")); - return a - b; - } - - @Path("/addstring") - @DELETE - @Produces(TEXT_PLAIN) - public String addString(@QueryParam("s") List s) { - String result = ""; - for (String x : s) { - result += x; - } - return result; - } -} diff --git a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/EnglishGreetingRestEndpoint.java b/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/EnglishGreetingRestEndpoint.java deleted file mode 100644 index 3c7800daf91..00000000000 --- a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/EnglishGreetingRestEndpoint.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.tests.endpoints; - -import static javax.ws.rs.core.MediaType.TEXT_PLAIN; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; - -import io.servicecomb.provider.rest.common.RestSchema; - -@RestSchema(schemaId = "englishGreetingRestEndpoint") -@Path("/") -public class EnglishGreetingRestEndpoint { - - @GET - @Path("/sayHi") - @Produces(TEXT_PLAIN) - public String sayHi(@QueryParam("name") String name) { - return "Hi " + name; - } - - @GET - @Path("") - @Produces(TEXT_PLAIN) - public String home() { - return "Welcome home"; - } -} diff --git a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/SchemaFirstJaxrs.java b/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/SchemaFirstJaxrs.java deleted file mode 100644 index 881ba6df3db..00000000000 --- a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/SchemaFirstJaxrs.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.tests.endpoints; - -import java.util.Date; -import java.util.Map; - -import io.servicecomb.demo.compute.Person; -import io.servicecomb.demo.server.User; - -public interface SchemaFirstJaxrs { - - int add(int a, int b); - - int reduce(int a, int b); - - Person sayHello(Person user); - - String testRawJsonString(String jsonInput); - - String saySomething(String prefix, Person user); - - String sayHi(String name); - - String sayHi2(String name); - - boolean isTrue(); - - String addString(String[] s); - - Map testUserMap(Map userMap); - - String textPlain(String body); - - byte[] bytes(byte[] input); - - Date addDate(Date date, long seconds); - - int defaultPath(); -} diff --git a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/SchemaFirstJaxrsImpl.java b/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/SchemaFirstJaxrsImpl.java deleted file mode 100644 index fc1f798ef39..00000000000 --- a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/SchemaFirstJaxrsImpl.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.tests.endpoints; - -import static javax.ws.rs.core.MediaType.TEXT_PLAIN; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.DELETE; -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.Context; - -import io.servicecomb.provider.rest.common.RestSchema; - -@RestSchema(schemaId = "schemaFirst") -@Path("/schemaFirstJaxrs") -public class SchemaFirstJaxrsImpl extends SomeAbstractJaxrsRestEndpoint { - - @Path("/reduce") - @GET - public int reduce(@Context HttpServletRequest request) { - int a = Integer.parseInt(request.getParameter("a")); - int b = Integer.parseInt(request.getParameter("b")); - return a - b; - } - - @Path("/addstring") - @DELETE - @Produces(TEXT_PLAIN) - public String addString(@QueryParam("s") String[] s) { - String result = ""; - for (String x : s) { - result += x; - } - return result; - } -} diff --git a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/SomeAbstractJaxrsRestEndpoint.java b/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/SomeAbstractJaxrsRestEndpoint.java deleted file mode 100644 index e6c50c4c521..00000000000 --- a/integration-tests/jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/endpoints/SomeAbstractJaxrsRestEndpoint.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.tests.endpoints; - -import static javax.ws.rs.core.MediaType.TEXT_PLAIN; - -import java.util.Date; -import java.util.Map; - -import javax.ws.rs.Consumes; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; -import javax.ws.rs.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 io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.demo.compute.Person; -import io.servicecomb.demo.server.User; -import io.servicecomb.swagger.invocation.context.ContextUtils; - -@Produces(MediaType.APPLICATION_JSON) -public class SomeAbstractJaxrsRestEndpoint { - - @Path("/testUserMap") - @POST - public Map testUserMap(Map userMap) { - return userMap; - } - - @Path("/textPlain") - @POST - @Consumes(TEXT_PLAIN) - public String textPlain(String body) { - return body; - } - - @Path("/bytes") - @POST - public byte[] bytes(byte[] input) { - input[0] = (byte) (input[0] + 1); - return input; - } - - @Path("/addDate") - @POST - public Date addDate(@FormParam("date") Date date, @QueryParam("seconds") long seconds) { - return new Date(date.getTime() + seconds * 1000); - } - - @GET - public int defaultPath() { - return 100; - } - - @Path("/add") - @POST - public int add(@FormParam("a") int a, @FormParam("b") int b) { - return a + b; - } - - @Path("/sayhello") - @POST - public Person sayHello(Person user) { - user.setName("hello " + user.getName()); - return user; - } - - @SuppressWarnings("unchecked") - @Path("/testrawjson") - @POST - public String testRawJsonString(String jsonInput) { - Map person; - try { - person = RestObjectMapper.INSTANCE.readValue(jsonInput.getBytes(), Map.class); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - return "hello " + person.get("name"); - } - - @Path("/saysomething") - @POST - public String saySomething(@HeaderParam("prefix") String prefix, Person user) { - return prefix + " " + user.getName(); - } - - @Path("/sayhi/{name}") - @PUT - public String sayHi(@PathParam("name") String name) { - ContextUtils.getInvocationContext().setStatus(202); - return name + " sayhi"; - } - - @Path("/sayhi/{name}/v2") - @PUT - public String sayHi2(@PathParam("name") String name) { - return name + " sayhi 2"; - } - - @Path("/istrue") - @GET - public boolean isTrue() { - return true; - } -} diff --git a/integration-tests/jaxrs-tests/src/test/resources/microservice.yaml b/integration-tests/jaxrs-tests/src/test/resources/microservice.yaml deleted file mode 100644 index f3ecd1ff787..00000000000 --- a/integration-tests/jaxrs-tests/src/test/resources/microservice.yaml +++ /dev/null @@ -1,14 +0,0 @@ -APPLICATION_ID: jaxrstest-it -service_description: - name: jaxrs - version: 0.0.2 -cse: - service: - registry: - address: http://127.0.0.1:9980 - rest: - address: 0.0.0.0:8080 - handler: - chain: - Provider: - default: bizkeeper-provider diff --git a/integration-tests/jaxrs-tests/src/test/resources/microservices/jaxrs/schemaFirst.yaml b/integration-tests/jaxrs-tests/src/test/resources/microservices/jaxrs/schemaFirst.yaml deleted file mode 100644 index 6659182570c..00000000000 --- a/integration-tests/jaxrs-tests/src/test/resources/microservices/jaxrs/schemaFirst.yaml +++ /dev/null @@ -1,255 +0,0 @@ -swagger: '2.0' -info: - title: "swagger definition for io.servicecomb.demo.jaxrs.tests.endpoints.SchemaFirstJaxrsImpl" - version: 1.0.0 - x-java-interface: io.servicecomb.demo.jaxrs.tests.endpoints.SchemaFirstJaxrs -basePath: "/schemaFirstJaxrs" -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 - 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 - /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 - - /testUserMap: - post: - operationId: testUserMap - parameters: - - name: userMap - in: body - required: false - schema: - type: object - additionalProperties: - $ref: '#/definitions/User' - responses: - 200: - description: response of 200 - schema: - type: object - additionalProperties: - $ref: '#/definitions/User' - /addDate: - post: - operationId: addDate - parameters: - - name: date - in: formData - required: false - type: string - format: date-time - - name: seconds - in: query - required: false - type: integer - format: int64 - responses: - 200: - description: response of 200 - schema: - type: string - format: date-time - /textPlain: - post: - operationId: textPlain - consumes: - - text/plain - parameters: - - name: body - in: body - required: false - schema: - type: string - responses: - 200: - description: response of 200 - schema: - type: string - /: - get: - operationId: defaultPath - parameters: [] - responses: - 200: - description: response of 200 - schema: - type: integer - format: int32 - /bytes: - post: - operationId: bytes - parameters: - - name: input - in: body - required: false - schema: - type: string - format: byte - responses: - 200: - description: response of 200 - schema: - type: string - format: byte -definitions: - User: - type: object - properties: - name: - type: string - age: - type: integer - format: int32 - index: - type: integer - format: int32 - names: - type: array - items: - type: string - x-java-class: io.servicecomb.demo.server.User - Person: - type: object - properties: - name: - type: string - x-java-class: io.servicecomb.demo.compute.Person diff --git a/integration-tests/pojo-test/pom.xml b/integration-tests/pojo-test/pom.xml deleted file mode 100644 index ec4e163de83..00000000000 --- a/integration-tests/pojo-test/pom.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - integration-tests - io.servicecomb.tests - 0.3.0-SNAPSHOT - - 4.0.0 - - pojo-test - - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-pojo - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - test-jar - - - - - - - \ No newline at end of file diff --git a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/PojoIntegrationTestBase.java b/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/PojoIntegrationTestBase.java deleted file mode 100644 index fce41bbc2a2..00000000000 --- a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/PojoIntegrationTestBase.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package io.servicecomb.demo.pojo.test; - -import static io.servicecomb.serviceregistry.client.LocalServiceRegistryClientImpl.LOCAL_REGISTRY_FILE_KEY; -import static org.hamcrest.Matchers.both; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.lessThan; -import static org.hamcrest.Matchers.nullValue; -import static org.hamcrest.collection.IsIterableContainingInOrder.contains; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; - -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Ignore; -import org.junit.Test; - -import io.servicecomb.demo.compute.Person; -import io.servicecomb.demo.server.TestRequest; -import io.servicecomb.demo.server.User; -import io.servicecomb.demo.smartcare.Application; -import io.servicecomb.demo.smartcare.Group; -import io.servicecomb.demo.smartcare.Response; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -@Ignore -public class PojoIntegrationTestBase { - - protected static void setUpLocalRegistry() { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - URL resource = loader.getResource("registry.yaml"); - System.setProperty(LOCAL_REGISTRY_FILE_KEY, resource.getPath()); - } - - @Test - public void remoteHelloPojo_sayHello() { - String result = PojoService.hello.SayHello("whatever"); - assertThat(result, is("Hello Message fast")); - } - - @Test - public void remoteHelloPojo_sayHelloAgain() { - long startTime = System.currentTimeMillis(); - String result = PojoService.hello.SayHelloAgain("whatever"); - long stopTime = System.currentTimeMillis(); - long elapsedTime = stopTime - startTime; - - assertThat(result, is("Hello Message slow")); - assertThat(elapsedTime, - is(both(greaterThan(5000L)).and(lessThan(6000L)))); - } - - @Test - public void remoteSmartCarePojo_addApplication() { - Group group = new Group(); - group.setName("group0"); - Application application = new Application(); - application.setName("app0"); - application.setDefaultGroup("group0"); - application.setVersion("v1"); - application.setDynamicFlag(true); - List groups = new ArrayList<>(); - groups.add(group); - application.setGroups(groups); - - Response result = PojoService.smartCare.addApplication(application); - assertThat(result.getResultCode(), is(0)); - assertThat(result.getResultMessage(), is("add application app0 success")); - } - - @Test - public void remoteSmartCarePojo_delApplication() { - Response result = PojoService.smartCare.delApplication("app0"); - - assertThat(result.getResultCode(), is(1)); - assertThat(result.getResultMessage(), is("delete application app0 failed")); - } - - @Test - public void remoteTestPojo_testStringArray() { - String result = PojoService.test.testStringArray(new String[] {"a", "b"}); - assertThat(result, is("arr is '[a, b]'")); - } - - @Test - public void remoteTestPojo_getTestString() { - // test empty string - String result = PojoService.test.getTestString(""); - assertThat(result, is("code is ''")); - - // test null - result = PojoService.test.getTestString(null); - assertThat(result, is("code is 'null'")); - - // test Chinese - result = PojoService.test.getTestString("测试"); - assertThat(result, is("code is '测试'")); - - // test String with space - result = PojoService.test.getTestString("a b"); - assertThat(result, is("code is 'a b'")); - } - - @Test - public void remoteTestPojo_postTestStatic() { - String result = PojoService.test.postTestStatic(1); - assertThat(result, is(nullValue())); - } - - @Test - public void remoteTestPojo_testException() { - // when code is 200 - String result = PojoService.test.testException(200); - assertThat(result, is("200")); - - // when code is 456 - try { - PojoService.test.testException(456); - fail("Exception expected, but threw nothing"); - } catch (InvocationException e) { - assertThat(e.getErrorData(), is("456 error")); - } catch (Exception e) { - fail("InvocationException expected, but threw " + e); - } - - // when code is 556 - try { - PojoService.test.testException(556); - fail("InvocationException expected, but threw nothing"); - } catch (InvocationException e) { - assertThat(e.getStatusCode(), is(556)); - assertThat(e.getErrorData().toString(), is("[556 error]")); - } catch (Exception e) { - fail("InvocationException expected, but threw " + e); - } - - // when code is 557 - try { - PojoService.test.testException(557); - fail("InvocationException expected, but threw nothing"); - } catch (InvocationException e) { - assertThat(e.getStatusCode(), is(557)); - assertThat(e.getErrorData().toString(), is("[[557 error]]")); - } catch (Exception e) { - fail("InvocationException expected, but threw " + e); - } - - // when code is 123(other number, the default case) - result = PojoService.test.testException(123); - assertThat(result, is("not expected")); - } - - @Test - public void remoteTestPojo_splitParam() { - User result = PojoService.test.splitParam(1, new User()); - assertThat(result.toString(), - is("User [name=nameA, users count:0, age=100, index=1]")); - } - - @Test - public void remoteTestPojo_wrapParam() { - // when request is null - User result = PojoService.test.wrapParam(null); - assertThat(result, is(nullValue())); - - // when request is not null - User user = new User(); - byte[] buffer = new byte[1024]; - - TestRequest request = new TestRequest(); - request.setUser(user); - request.setIndex(0); - request.setData(buffer); - request.getUsers().add(user); - - result = PojoService.test.wrapParam(request); - assertThat(result.toString(), - is("User [name=nameA, users count:1, age=100, index=0]")); - } - - @Test - public void remoteTestPojo_addString() { - String result = PojoService.test.addString(new String[] {"a", "b"}); - assertThat(result, is("[a, b]")); - } - - @Test - public void remoteCodeFirstPojo_testUserMap() { - User user1 = new User(); - user1.setNames(new String[] {"u1", "u2"}); - - User user2 = new User(); - user2.setNames(new String[] {"u3", "u4"}); - - Map userMap = new HashMap<>(); - userMap.put("u1", user1); - userMap.put("u2", user2); - Map result = PojoService.codeFirst.testUserMap(userMap); - - assertThat(result.get("u1").getNames()[0], is("u1")); - assertThat(result.get("u1").getNames()[1], is("u2")); - assertThat(result.get("u2").getNames()[0], is("u3")); - assertThat(result.get("u2").getNames()[1], is("u4")); - } - - @Test - public void remoteCodeFirstPojo_testUserArray() { - User user1 = new User(); - user1.setNames(new String[] {"u1", "u2"}); - - User user2 = new User(); - user2.setNames(new String[] {"u3", "u4"}); - - User[] users = new User[] {user1, user2}; - List result = PojoService.codeFirst.testUserArray(Arrays.asList(users)); - - assertThat(result.get(0).getNames()[0], is("u1")); - assertThat(result.get(0).getNames()[1], is("u2")); - assertThat(result.get(1).getNames()[0], is("u3")); - assertThat(result.get(1).getNames()[1], is("u4")); - } - - @Test - public void remoteCodeFirstPojo_testStrings() { - String[] result = PojoService.codeFirst.testStrings(new String[] {"a", "b"}); - assertThat(Arrays.asList(result), contains("aa0", "b")); - } - - @Test - public void remoteCodeFirstPojo_testBytes() { - byte[] input = new byte[] {0, 1, 2}; - byte[] result = PojoService.codeFirst.testBytes(input); - - assertEquals(3, result.length); - assertEquals(1, result[0]); - assertEquals(1, result[1]); - assertEquals(2, result[2]); - } - - @Test - public void remoteCodeFirstPojo_reduce() { - int result = PojoService.codeFirst.reduce(5, 3); - assertThat(result, is(2)); - } - - @Test - public void remoteCodeFirstPojo_addDate() { - Date date = new Date(); - int seconds = 1; - Date result = PojoService.codeFirst.addDate(date, seconds); - - assertThat(result, equalTo(new Date(date.getTime() + seconds * 1000))); - } - - @Test - public void remoteCodeFirstPojo_sayHello() { - Person input = new Person(); - input.setName("person name"); - - Person result = PojoService.codeFirst.sayHello(input); - assertThat(result.getName(), is("hello person name")); - } - - @Test - public void remoteCodeFirstPojo_saySomething() { - Person person = new Person(); - person.setName("person name"); - - String result = PojoService.codeFirst.saySomething("prefix prefix", person); - assertThat(result, is("prefix prefix person name")); - } - - @Test - public void remoteCodeFirstPojo_sayHi() { - String result = PojoService.codeFirst.sayHi("world"); - assertThat(result, is("world sayhi")); - } - - @Test - public void remoteCodeFirstPojo_sayHi2() { - String result = PojoService.codeFirst.sayHi2("world"); - assertThat(result, is("world sayhi 2")); - } - - @Test - public void remoteCodeFirstPojo_isTrue() { - boolean result = PojoService.codeFirst.isTrue(); - assertThat(result, is(true)); - } - - @Test - public void remoteCodeFirstPojo_addString() { - String result = PojoService.codeFirst.addString(Arrays.asList("a", "b")); - assertThat(result, is("ab")); - } -} diff --git a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/PojoService.java b/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/PojoService.java deleted file mode 100644 index 9d07d296aae..00000000000 --- a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/PojoService.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package io.servicecomb.demo.pojo.test; - -import javax.inject.Inject; - -import org.springframework.stereotype.Component; - -import io.servicecomb.demo.CodeFirstPojoIntf; -import io.servicecomb.demo.helloworld.greeter.Hello; -import io.servicecomb.demo.server.Test; -import io.servicecomb.demo.smartcare.SmartCare; - -@Component -public class PojoService { - - static Hello hello; - - static SmartCare smartCare; - - static io.servicecomb.demo.server.Test test; - - static CodeFirstPojoIntf codeFirst; - - @Inject - public void setHello(Hello hello) { - PojoService.hello = hello; - } - - @Inject - public void setSmartCare(SmartCare smartCare) { - PojoService.smartCare = smartCare; - } - - @Inject - public void setTest(Test test) { - PojoService.test = test; - } - - @Inject - public void setCodeFirst(CodeFirstPojoIntf codeFirst) { - PojoService.codeFirst = codeFirst; - } -} diff --git a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/PojoTestMain.java b/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/PojoTestMain.java deleted file mode 100644 index 93abde1b013..00000000000 --- a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/PojoTestMain.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package io.servicecomb.demo.pojo.test; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class PojoTestMain { - - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} \ No newline at end of file diff --git a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/RawPojoIntegrationTest.java b/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/RawPojoIntegrationTest.java deleted file mode 100644 index 3cfaf72868d..00000000000 --- a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/RawPojoIntegrationTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package io.servicecomb.demo.pojo.test; - -import org.junit.BeforeClass; - -public class RawPojoIntegrationTest extends PojoIntegrationTestBase { - - @BeforeClass - public static void setUp() throws Exception { - setUpLocalRegistry(); - PojoTestMain.main(new String[0]); - } -} diff --git a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/SomePojoConfig.java b/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/SomePojoConfig.java deleted file mode 100644 index 0418a86e0ff..00000000000 --- a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/SomePojoConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package io.servicecomb.demo.pojo.test; - -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; - -import io.servicecomb.demo.CodeFirstPojoIntf; -import io.servicecomb.demo.helloworld.greeter.Hello; -import io.servicecomb.demo.server.Test; -import io.servicecomb.demo.smartcare.SmartCare; -import io.servicecomb.provider.pojo.RpcReference; - -@Component -public class SomePojoConfig { - - @RpcReference(microserviceName = "pojo", schemaId = "helloworld.Greeter") - private Hello hello; - - @RpcReference(microserviceName = "pojo", schemaId = "smartcare") - private SmartCare smartCare; - - @RpcReference(microserviceName = "pojo", schemaId = "server") - private Test test; - - @RpcReference(microserviceName = "pojo", schemaId = "codeFirst") - private CodeFirstPojoIntf codeFirst; - - @Bean - Hello hello() { - return hello; - } - - @Bean - SmartCare smartCare() { - return smartCare; - } - - @Bean - Test test() { - return test; - } - - @Bean - CodeFirstPojoIntf codeFirst() { - return codeFirst; - } -} diff --git a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/CodeFirstPojo.java b/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/CodeFirstPojo.java deleted file mode 100644 index 27e79ad230e..00000000000 --- a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/CodeFirstPojo.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.test.endpoints; - -import java.util.Date; -import java.util.List; -import java.util.Map; - -import io.servicecomb.demo.CodeFirstPojoIntf; -import io.servicecomb.demo.compute.Person; -import io.servicecomb.demo.server.User; -import io.servicecomb.provider.pojo.RpcSchema; -import io.servicecomb.swagger.invocation.context.ContextUtils; - -@RpcSchema(schemaId = "codeFirst") -public class CodeFirstPojo implements CodeFirstPojoIntf { - - @Override - public Map testUserMap(Map userMap) { - return userMap; - } - - @Override - public List testUserArray(List users) { - return users; - } - - public String[] testStrings(String[] input) { - input[0] += input[0] + "0"; - return input; - } - - public byte[] testBytes(byte[] input) { - input[0] = (byte) (input[0] + 1); - return input; - } - - public int reduce(int a, int b) { - return a - b; - } - - public Date addDate(Date date, long second) { - return new Date(date.getTime() + second * 1000); - } - - public Person sayHello(Person user) { - user.setName("hello " + user.getName()); - return user; - } - - public String saySomething(String prefix, Person user) { - return prefix + " " + user.getName(); - } - - public String sayHi(String name) { - ContextUtils.getInvocationContext().setStatus(202); - return name + " sayhi"; - } - - public String sayHi2(String name) { - return name + " sayhi 2"; - } - - public boolean isTrue() { - return true; - } - - public String addString(List s) { - String result = ""; - for (String x : s) { - result += x; - } - return result; - } -} diff --git a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/HelloImpl.java b/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/HelloImpl.java deleted file mode 100644 index aa540dabb8f..00000000000 --- a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/HelloImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.test.endpoints; - -import io.servicecomb.demo.helloworld.greeter.Hello; -import io.servicecomb.provider.pojo.RpcSchema; - -@RpcSchema(schemaId = "helloworld.Greeter") -public class HelloImpl implements Hello { - - @Override - public String SayHello(String name) { - return "Hello Message fast"; - } - - @Override - public String SayHelloAgain(String name) { - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return "Hello Message slow"; - } -} diff --git a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/SmartCareImpl.java b/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/SmartCareImpl.java deleted file mode 100644 index 8236e2d572f..00000000000 --- a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/SmartCareImpl.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.test.endpoints; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.demo.smartcare.Application; -import io.servicecomb.demo.smartcare.Response; -import io.servicecomb.demo.smartcare.SmartCare; -import io.servicecomb.provider.pojo.RpcSchema; - -@RpcSchema(schemaId = "smartcare") -public class SmartCareImpl implements SmartCare { - - private static final Logger LOG = LoggerFactory.getLogger(SmartCareImpl.class); - - @Override - public Response addApplication(Application application) { - // TODO: add application - LOG.info(application.toString()); - - Response resp = new Response(); - resp.setResultCode(0); - resp.setResultMessage("add application " + application.getName() + " success"); - return resp; - } - - @Override - public Response delApplication(String appName) { - // TODO: delete application - LOG.info(appName); - - try { - System.out.println(5 / 0); - } catch (Exception e) { - Response resp = new Response(); - resp.setResultCode(1); - resp.setResultMessage("delete application " + appName + " failed"); - return resp; - } - - return null; - } -} diff --git a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/TestImpl.java b/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/TestImpl.java deleted file mode 100644 index e5ed1125e6d..00000000000 --- a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/TestImpl.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.test.endpoints; - -import java.util.Arrays; -import java.util.List; - -import io.servicecomb.demo.server.Test; -import io.servicecomb.demo.server.TestRequest; -import io.servicecomb.demo.server.User; -import io.servicecomb.provider.pojo.RpcSchema; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -@RpcSchema(schemaId = "server") -public class TestImpl implements Test { - - @Override - public String testStringArray(String[] arr) { - return String.format("arr is '%s'", Arrays.toString(arr)); - } - - @Override - public String getTestString(String code) { - return String.format("code is '%s'", String.valueOf(code)); - } - - @Override - public String postTestStatic(int code) { - return null; - } - - private User doTest(int index, User user, List users, byte[] data) { - if (user == null) { - user = new User(); - } - - user.setIndex(index); - - int userCount = (users == null) ? 0 : users.size(); - user.setName(user.getName() + ", users count:" + userCount); - return user; - } - - @Override - public String testException(int code) { - String strCode = String.valueOf(code); - switch (code) { - case 200: - return strCode; - case 456: - throw new InvocationException(code, strCode, strCode + " error"); - case 556: - throw new InvocationException(code, strCode, Arrays.asList(strCode + " error")); - case 557: - throw new InvocationException(code, strCode, Arrays.asList(Arrays.asList(strCode + " error"))); - default: - break; - } - - return "not expected"; - } - - @Override - public User splitParam(int index, User user) { - return doTest(index, user, null, null); - } - - @Override - public User wrapParam(TestRequest request) { - if (request == null) { - return null; - } - return doTest(request.getIndex(), request.getUser(), request.getUsers(), request.getData()); - } - - @Override - public String addString(String[] strArr) { - String result = Arrays.toString(strArr); - System.out.println("addString: " + result); - return result; - } -} diff --git a/integration-tests/pojo-test/src/test/resources/log4j.properties b/integration-tests/pojo-test/src/test/resources/log4j.properties deleted file mode 100644 index cce5ffe8eae..00000000000 --- a/integration-tests/pojo-test/src/test/resources/log4j.properties +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -log4j.rootLogger=INFO, stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n diff --git a/integration-tests/pojo-test/src/test/resources/microservice.yaml b/integration-tests/pojo-test/src/test/resources/microservice.yaml deleted file mode 100644 index 7bdcfcdb101..00000000000 --- a/integration-tests/pojo-test/src/test/resources/microservice.yaml +++ /dev/null @@ -1,10 +0,0 @@ -APPLICATION_ID: pojotest-it -service_description: - name: pojo - version: 0.0.4 -cse: - service: - registry: - address: http://127.0.0.1:9980 - highway: - address: 0.0.0.0:7070 \ No newline at end of file diff --git a/integration-tests/pojo-test/src/test/resources/registry.yaml b/integration-tests/pojo-test/src/test/resources/registry.yaml deleted file mode 100644 index 16112e775fd..00000000000 --- a/integration-tests/pojo-test/src/test/resources/registry.yaml +++ /dev/null @@ -1,7 +0,0 @@ -pojo-local-service: - - id: "001" - version: "0.0.4" - appid: pojotest-it - instances: - - endpoints: - - highway:127.0.0.1:7070?login=true diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml deleted file mode 100644 index 8bc8238ed9b..00000000000 --- a/integration-tests/pom.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - io.servicecomb - java-chassis-parent - 0.3.0-SNAPSHOT - ../parent - - 4.0.0 - - io.servicecomb.tests - integration-tests - pom - - - jaxrs-tests - springmvc-tests - tracing-tests - test-common - spring-jaxrs-tests - pojo-test - spring-springmvc-tests - spring-zuul-tracing-tests - spring-pojo-tests - - - - - - io.servicecomb.demo - demo-schema - 0.3.0-SNAPSHOT - - - io.servicecomb.tests - test-common - 0.3.0-SNAPSHOT - test-jar - - - io.zipkin.java - zipkin-junit - 1.24.0 - test - - - com.squareup.okhttp3 - okhttp - 3.6.0 - test - - - - - - io.servicecomb - handler-bizkeeper - - - io.servicecomb - handler-loadbalance - - - io.servicecomb - transport-highway - - - javax.servlet - javax.servlet-api - - - io.servicecomb - handler-flowcontrol-qps - - - io.servicecomb - swagger-generator-springmvc - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.0.2 - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - true - - true - - - - - diff --git a/integration-tests/spring-jaxrs-tests/pom.xml b/integration-tests/spring-jaxrs-tests/pom.xml deleted file mode 100644 index 814b6da151d..00000000000 --- a/integration-tests/spring-jaxrs-tests/pom.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - integration-tests - io.servicecomb.tests - 0.3.0-SNAPSHOT - - 4.0.0 - - spring-jaxrs-tests - - - - io.servicecomb.tests - jaxrs-tests - 0.3.0-SNAPSHOT - test-jar - - - io.servicecomb - provider-jaxrs - - - io.servicecomb - spring-boot-starter-provider - - - org.springframework.boot - spring-boot-starter-test - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter - - - org.slf4j - * - - - ch.qos.logback - * - - - - - - - \ No newline at end of file diff --git a/integration-tests/spring-jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/JaxrsSpringIntegrationTest.java b/integration-tests/spring-jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/JaxrsSpringIntegrationTest.java deleted file mode 100644 index 00e80fb9de3..00000000000 --- a/integration-tests/spring-jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/JaxrsSpringIntegrationTest.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.tests; - -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = JaxrsSpringMain.class) -public class JaxrsSpringIntegrationTest extends JaxrsIntegrationTestBase { - -} diff --git a/integration-tests/spring-jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/JaxrsSpringMain.java b/integration-tests/spring-jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/JaxrsSpringMain.java deleted file mode 100644 index 7b14de5a2c7..00000000000 --- a/integration-tests/spring-jaxrs-tests/src/test/java/io/servicecomb/demo/jaxrs/tests/JaxrsSpringMain.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.jaxrs.tests; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import io.servicecomb.springboot.starter.provider.EnableServiceComb; - -@SpringBootApplication -@EnableServiceComb -public class JaxrsSpringMain { - public static void main(final String[] args) throws Exception { - SpringApplication.run(JaxrsSpringMain.class, args); - } -} diff --git a/integration-tests/spring-jaxrs-tests/src/test/resources/log4j.properties b/integration-tests/spring-jaxrs-tests/src/test/resources/log4j.properties deleted file mode 100644 index bea22aca731..00000000000 --- a/integration-tests/spring-jaxrs-tests/src/test/resources/log4j.properties +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n diff --git a/integration-tests/spring-pojo-tests/pom.xml b/integration-tests/spring-pojo-tests/pom.xml deleted file mode 100644 index ffe160abdec..00000000000 --- a/integration-tests/spring-pojo-tests/pom.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - integration-tests - io.servicecomb.tests - 0.3.0-SNAPSHOT - - 4.0.0 - - spring-pojo-tests - - - - io.servicecomb.tests - pojo-test - 0.3.0-SNAPSHOT - test-jar - - - org.springframework.boot - spring-boot-autoconfigure - - - io.servicecomb - spring-boot-starter-provider - - - org.springframework.boot - spring-boot-test - - - org.springframework - spring-test - - - org.hibernate - hibernate-validator - - - - - - \ No newline at end of file diff --git a/integration-tests/spring-pojo-tests/src/test/java/io/servicecomb/demo/pojo/test/PojoSpringIntegrationTest.java b/integration-tests/spring-pojo-tests/src/test/java/io/servicecomb/demo/pojo/test/PojoSpringIntegrationTest.java deleted file mode 100644 index ec79092eef5..00000000000 --- a/integration-tests/spring-pojo-tests/src/test/java/io/servicecomb/demo/pojo/test/PojoSpringIntegrationTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.test; - -import org.junit.BeforeClass; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = PojoSpringMain.class) -public class PojoSpringIntegrationTest extends PojoIntegrationTestBase { - - @BeforeClass - public static void setUpClass() { - setUpLocalRegistry(); - } -} diff --git a/integration-tests/spring-pojo-tests/src/test/java/io/servicecomb/demo/pojo/test/PojoSpringMain.java b/integration-tests/spring-pojo-tests/src/test/java/io/servicecomb/demo/pojo/test/PojoSpringMain.java deleted file mode 100644 index 65d7e53cfad..00000000000 --- a/integration-tests/spring-pojo-tests/src/test/java/io/servicecomb/demo/pojo/test/PojoSpringMain.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.pojo.test; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import io.servicecomb.springboot.starter.provider.EnableServiceComb; - -@SpringBootApplication -@EnableServiceComb -public class PojoSpringMain { - - public static void main(final String[] args) throws Exception { - SpringApplication.run(PojoSpringMain.class, args); - } -} diff --git a/integration-tests/spring-springmvc-tests/pom.xml b/integration-tests/spring-springmvc-tests/pom.xml deleted file mode 100644 index e4f1dbc0bbf..00000000000 --- a/integration-tests/spring-springmvc-tests/pom.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - integration-tests - io.servicecomb.tests - 0.3.0-SNAPSHOT - - 4.0.0 - - spring-springmvc-tests - - - io.servicecomb.tests - springmvc-tests - 0.3.0-SNAPSHOT - test-jar - - - io.servicecomb - spring-boot-starter-provider - - - org.springframework.boot - spring-boot-test - - - org.springframework - spring-test - - - org.hibernate - hibernate-validator - - - - - \ No newline at end of file diff --git a/integration-tests/spring-springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/SpringMvcSpringIntegrationTest.java b/integration-tests/spring-springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/SpringMvcSpringIntegrationTest.java deleted file mode 100644 index 78eaaf07614..00000000000 --- a/integration-tests/spring-springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/SpringMvcSpringIntegrationTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package io.servicecomb.demo.springmvc.tests; - -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = SpringMvcSpringMain.class) -public class SpringMvcSpringIntegrationTest extends SpringMvcIntegrationTestBase { - -} diff --git a/integration-tests/spring-springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/SpringMvcSpringMain.java b/integration-tests/spring-springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/SpringMvcSpringMain.java deleted file mode 100644 index a09ccad7f09..00000000000 --- a/integration-tests/spring-springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/SpringMvcSpringMain.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package io.servicecomb.demo.springmvc.tests; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -import io.servicecomb.springboot.starter.provider.EnableServiceComb; - -@SpringBootApplication -@EnableServiceComb -public class SpringMvcSpringMain { - - public static void main(final String[] args) throws Exception { - SpringApplication.run(SpringMvcSpringMain.class, args); - } -} diff --git a/integration-tests/spring-springmvc-tests/src/test/resources/log4j.properties b/integration-tests/spring-springmvc-tests/src/test/resources/log4j.properties deleted file mode 100644 index 618f15a3040..00000000000 --- a/integration-tests/spring-springmvc-tests/src/test/resources/log4j.properties +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -log4j.rootLogger=INFO, stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n \ No newline at end of file diff --git a/integration-tests/spring-zuul-tracing-tests/pom.xml b/integration-tests/spring-zuul-tracing-tests/pom.xml deleted file mode 100644 index d55dd43a2e2..00000000000 --- a/integration-tests/spring-zuul-tracing-tests/pom.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - integration-tests - io.servicecomb.tests - 0.3.0-SNAPSHOT - - 4.0.0 - - spring-zuul-tracing-tests - - - - io.servicecomb.tests - test-common - test-jar - - - org.slf4j - slf4j-api - - - org.slf4j - slf4j-log4j12 - - - org.springframework.cloud - spring-cloud-starter-zuul - - - javax.ws.rs - jsr311-api - - - - - io.servicecomb - spring-boot-starter-provider - - - io.servicecomb - spring-boot-starter-discovery - - - io.servicecomb - spring-cloud-zuul-zipkin - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter - - - ch.qos.logback - * - - - - - org.springframework.boot - spring-boot-starter-test - - - io.zipkin.java - zipkin-junit - - - com.squareup.okhttp3 - okhttp - test - - - \ No newline at end of file diff --git a/integration-tests/spring-zuul-tracing-tests/src/test/java/io/servicecomb/spring/cloud/zuul/tracing/LoggingFilterConfig.java b/integration-tests/spring-zuul-tracing-tests/src/test/java/io/servicecomb/spring/cloud/zuul/tracing/LoggingFilterConfig.java deleted file mode 100644 index f5314fe532b..00000000000 --- a/integration-tests/spring-zuul-tracing-tests/src/test/java/io/servicecomb/spring/cloud/zuul/tracing/LoggingFilterConfig.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.spring.cloud.zuul.tracing; - -import static javax.servlet.DispatcherType.ASYNC; -import static javax.servlet.DispatcherType.ERROR; -import static javax.servlet.DispatcherType.FORWARD; -import static javax.servlet.DispatcherType.INCLUDE; -import static javax.servlet.DispatcherType.REQUEST; - -import java.io.IOException; -import java.lang.invoke.MethodHandles; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; - -@Configuration -class LoggingFilterConfig { - - @Bean - DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean() { - DelegatingFilterProxyRegistrationBean bean = new DelegatingFilterProxyRegistrationBean("traceLoggingFilter"); - - bean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, REQUEST); - bean.setOrder(Ordered.LOWEST_PRECEDENCE); - return bean; - } - - @Bean - LoggingFilter traceLoggingFilter() { - return new LoggingFilter(); - } - - private static class LoggingFilter implements Filter { - - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - } - - @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) - throws IOException, ServletException { - logger.info("logged tracing filter"); - filterChain.doFilter(servletRequest, servletResponse); - } - - @Override - public void destroy() { - } - } -} diff --git a/integration-tests/spring-zuul-tracing-tests/src/test/java/io/servicecomb/spring/cloud/zuul/tracing/SpringCloudZuulTracingTest.java b/integration-tests/spring-zuul-tracing-tests/src/test/java/io/servicecomb/spring/cloud/zuul/tracing/SpringCloudZuulTracingTest.java deleted file mode 100644 index 2c45d6e79ec..00000000000 --- a/integration-tests/spring-zuul-tracing-tests/src/test/java/io/servicecomb/spring/cloud/zuul/tracing/SpringCloudZuulTracingTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.spring.cloud.zuul.tracing; - -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; -import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; -import static org.springframework.http.HttpStatus.OK; - -import java.util.Collection; -import java.util.concurrent.TimeUnit; - -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.ResponseEntity; -import org.springframework.test.context.junit4.SpringRunner; - -import io.servicecomb.tests.tracing.TracingTestBase; - -@RunWith(SpringRunner.class) -@SpringBootTest(classes = TracedZuulMain.class, webEnvironment = RANDOM_PORT) -public class SpringCloudZuulTracingTest extends TracingTestBase { - - @Autowired - private TestRestTemplate testRestTemplate; - - @After - public void tearDown() throws Exception { - appender.clear(); - } - - @Test - public void tracesCallsReceivedFromZuulToCalledService() throws InterruptedException { - ResponseEntity responseEntity = testRestTemplate.getForEntity("/dummy/rest/blah", String.class); - - assertThat(responseEntity.getStatusCode(), is(OK)); - assertThat(responseEntity.getBody(), is("blah")); - - TimeUnit.MILLISECONDS.sleep(1000); - - Collection tracingMessages = appender.pollLogs(".*\\[\\w+/\\w+/\\w*\\]\\s+INFO.*(logged tracing|/blah).*"); - assertThat(tracingMessages.size(), greaterThanOrEqualTo(2)); - - assertThatSpansReceivedByZipkin(tracingMessages, "/dummy/rest/blah", "/blah"); - } - - @Test - public void tracesFailedCallsReceivedByZuul() throws InterruptedException { - ResponseEntity responseEntity = testRestTemplate.getForEntity("/dummy/rest/oops", String.class); - - assertThat(responseEntity.getStatusCode(), is(INTERNAL_SERVER_ERROR)); - - TimeUnit.MILLISECONDS.sleep(1000); - - Collection tracingMessages = appender.pollLogs(".*\\[\\w+/\\w+/\\w*\\]\\s+INFO.*(logged tracing|/oops).*"); - assertThat(tracingMessages.size(), greaterThanOrEqualTo(2)); - - assertThatSpansReceivedByZipkin(tracingMessages, "/dummy/rest/oops", "500", "/oops", "590"); - } -} diff --git a/integration-tests/spring-zuul-tracing-tests/src/test/java/io/servicecomb/spring/cloud/zuul/tracing/TracedController.java b/integration-tests/spring-zuul-tracing-tests/src/test/java/io/servicecomb/spring/cloud/zuul/tracing/TracedController.java deleted file mode 100644 index 5dd8cd5efc1..00000000000 --- a/integration-tests/spring-zuul-tracing-tests/src/test/java/io/servicecomb/spring/cloud/zuul/tracing/TracedController.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.spring.cloud.zuul.tracing; - -import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE; -import static org.springframework.web.bind.annotation.RequestMethod.GET; - -import java.lang.invoke.MethodHandles; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.bind.annotation.RequestMapping; - -import io.servicecomb.provider.rest.common.RestSchema; - -@RestSchema(schemaId = "tracedController") -@RequestMapping("/rest") -public class TracedController { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - @RequestMapping(value = "/blah", method = GET, produces = TEXT_PLAIN_VALUE) - public String blah() { - logger.info("in /blah"); - - return "blah"; - } - - @RequestMapping(value = "/oops", method = GET, produces = TEXT_PLAIN_VALUE) - public String oops() { - logger.info("in /oops"); - - throw new IllegalStateException("oops"); - } -} diff --git a/integration-tests/spring-zuul-tracing-tests/src/test/java/io/servicecomb/spring/cloud/zuul/tracing/TracedZuulMain.java b/integration-tests/spring-zuul-tracing-tests/src/test/java/io/servicecomb/spring/cloud/zuul/tracing/TracedZuulMain.java deleted file mode 100644 index 8e1bde2bc1f..00000000000 --- a/integration-tests/spring-zuul-tracing-tests/src/test/java/io/servicecomb/spring/cloud/zuul/tracing/TracedZuulMain.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.spring.cloud.zuul.tracing; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.netflix.zuul.EnableZuulProxy; - -import io.servicecomb.springboot.starter.provider.EnableServiceComb; - -@SpringBootApplication -@EnableZuulProxy -@EnableServiceComb -public class TracedZuulMain { - - public static void main(String[] args) { - SpringApplication.run(TracedZuulMain.class, args); - } -} diff --git a/integration-tests/spring-zuul-tracing-tests/src/test/resources/application.properties b/integration-tests/spring-zuul-tracing-tests/src/test/resources/application.properties deleted file mode 100644 index 3bfc5385f88..00000000000 --- a/integration-tests/spring-zuul-tracing-tests/src/test/resources/application.properties +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -spring.main.web-environment=true \ No newline at end of file diff --git a/integration-tests/spring-zuul-tracing-tests/src/test/resources/application.yaml b/integration-tests/spring-zuul-tracing-tests/src/test/resources/application.yaml deleted file mode 100644 index 973eaeb3fe7..00000000000 --- a/integration-tests/spring-zuul-tracing-tests/src/test/resources/application.yaml +++ /dev/null @@ -1,12 +0,0 @@ -zuul: - routes: - dummy: - serviceId: dummy - -# disable netflix eurkea since it's not used for service discovery -ribbon: - eureka: - enabled: false - -server: - port: 0 diff --git a/integration-tests/spring-zuul-tracing-tests/src/test/resources/microservice.yaml b/integration-tests/spring-zuul-tracing-tests/src/test/resources/microservice.yaml deleted file mode 100644 index 72f8e7bd9bb..00000000000 --- a/integration-tests/spring-zuul-tracing-tests/src/test/resources/microservice.yaml +++ /dev/null @@ -1,21 +0,0 @@ -APPLICATION_ID: tracing-app -service_description: - name: gateway-service - version: 0.0.2 -cse: - service: - registry: - address: http://127.0.0.1:9980 - rest: - address: 0.0.0.0:8080 - handler: - chain: - Provider: - default: tracing-provider,bizkeeper-provider - Consumer: - default: tracing-consumer,loadbalance,bizkeeper-consumer - -servicecomb: - tracing: - collector: - address: http://localhost:9411/ diff --git a/integration-tests/spring-zuul-tracing-tests/src/test/resources/registry.yaml b/integration-tests/spring-zuul-tracing-tests/src/test/resources/registry.yaml deleted file mode 100644 index c7db79fdf25..00000000000 --- a/integration-tests/spring-zuul-tracing-tests/src/test/resources/registry.yaml +++ /dev/null @@ -1,7 +0,0 @@ -dummy: - - id: "001" - version: "0.0.2" - appid: tracing-app - instances: - - endpoints: - - rest://127.0.0.1:8080?sslEnabled=false diff --git a/integration-tests/springmvc-tests/pom.xml b/integration-tests/springmvc-tests/pom.xml deleted file mode 100644 index 8bec5126171..00000000000 --- a/integration-tests/springmvc-tests/pom.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - integration-tests - io.servicecomb.tests - 0.3.0-SNAPSHOT - - 4.0.0 - - springmvc-tests - - - io.servicecomb.demo - demo-schema - - - io.servicecomb - provider-rest-common - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - test-jar - - - - - - - \ No newline at end of file diff --git a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/RawSpringMvcIntegrationTest.java b/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/RawSpringMvcIntegrationTest.java deleted file mode 100644 index bbe953a2b81..00000000000 --- a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/RawSpringMvcIntegrationTest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package io.servicecomb.demo.springmvc.tests; - -import org.junit.BeforeClass; - -public class RawSpringMvcIntegrationTest extends SpringMvcIntegrationTestBase { - - @BeforeClass - public static void setUp() throws Exception { - SpringMvcTestMain.main(new String[0]); - } -} \ No newline at end of file diff --git a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/SpringMvcIntegrationTestBase.java b/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/SpringMvcIntegrationTestBase.java deleted file mode 100644 index e56bc6e7c98..00000000000 --- a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/SpringMvcIntegrationTestBase.java +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.springmvc.tests; - -import static java.time.temporal.ChronoUnit.SECONDS; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; -import static org.springframework.http.HttpHeaders.CONTENT_TYPE; -import static org.springframework.http.HttpMethod.GET; -import static org.springframework.http.HttpMethod.POST; -import static org.springframework.http.HttpMethod.PUT; -import static org.springframework.http.HttpStatus.ACCEPTED; -import static org.springframework.http.HttpStatus.OK; -import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE; -import static org.springframework.http.MediaType.APPLICATION_JSON; - -import java.io.IOException; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Ignore; -import org.junit.Test; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.client.UnknownHttpStatusCodeException; - -import io.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.demo.compute.Person; -import io.servicecomb.demo.server.User; - -@Ignore -public class SpringMvcIntegrationTestBase { - - private final String baseUrl = "http://127.0.0.1:8080/"; - - private final RestTemplate restTemplate = new RestTemplate(); - - private final String codeFirstUrl = baseUrl + "codeFirstSpringmvc/"; - - private final String controllerUrl = baseUrl + "controller/"; - - @Test - public void ableToQueryAtRootBasePath() { - ResponseEntity responseEntity = restTemplate - .getForEntity(baseUrl + "sayHi?name=Mike", String.class); - - assertThat(responseEntity.getStatusCode(), is(OK)); - assertThat(responseEntity.getBody(), is("Hi Mike")); - - List> convertersOld = restTemplate.getMessageConverters(); - List> converters = new ArrayList<>(); - converters.add(new MappingJackson2HttpMessageConverter()); - restTemplate.setMessageConverters(converters); - responseEntity = restTemplate - .getForEntity(baseUrl + "sayHi?name={name}", String.class, "小 强"); - - assertThat(responseEntity.getStatusCode(), is(OK)); - assertThat(responseEntity.getBody(), is("Hi 小 强")); - - restTemplate.setMessageConverters(convertersOld); - } - - @Test - public void ableToQueryAtRootPath() { - ResponseEntity responseEntity = restTemplate - .getForEntity(baseUrl, String.class); - - assertThat(responseEntity.getStatusCode(), is(OK)); - assertThat(responseEntity.getBody(), is("Welcome home")); - } - - @Test - public void ableToQueryAtNonRootPath() { - ResponseEntity responseEntity = restTemplate - .getForEntity(baseUrl + "french/bonjour?name=Mike", String.class); - - assertThat(responseEntity.getStatusCode(), is(OK)); - assertThat(responseEntity.getBody(), is("Bonjour Mike")); - } - - @Test - public void ableToPostMap() { - Map users = new HashMap<>(); - users.put("user1", userOfNames("name11", "name12")); - users.put("user2", userOfNames("name21", "name22")); - - ParameterizedTypeReference> reference = new ParameterizedTypeReference>() { - }; - ResponseEntity> responseEntity = restTemplate.exchange(codeFirstUrl + "testUserMap", - POST, - jsonRequest(users), - reference); - - assertThat(responseEntity.getStatusCode(), is(OK)); - - Map body = responseEntity.getBody(); - assertArrayEquals(body.get("user1").getNames(), new String[] {"name11", "name12"}); - assertArrayEquals(body.get("user2").getNames(), new String[] {"name21", "name22"}); - } - - private User userOfNames(String... names) { - User user1 = new User(); - user1.setNames(names); - return user1; - } - - @Test - public void ableToConsumeTextPlain() { - String body = "a=1"; - - String result = restTemplate.postForObject( - codeFirstUrl + "textPlain", - body, - String.class); - - assertThat(jsonOf(result, String.class), is(body)); - } - - @Test - public void ableToPostBytes() throws IOException { - byte[] body = new byte[] {0, 1, 2}; - - byte[] result = restTemplate.postForObject( - codeFirstUrl + "bytes", - jsonRequest(RestObjectMapper.INSTANCE.writeValueAsBytes(body)), - byte[].class); - - result = RestObjectMapper.INSTANCE.readValue(result, byte[].class); - - assertEquals(1, result[0]); - assertEquals(1, result[1]); - assertEquals(2, result[2]); - assertEquals(3, result.length); - } - - @Test - public void ableToPostDate() throws Exception { - ZonedDateTime date = ZonedDateTime.now().truncatedTo(SECONDS); - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("date", RestObjectMapper.INSTANCE.convertToString(Date.from(date.toInstant()))); - - HttpHeaders headers = new HttpHeaders(); - headers.add(CONTENT_TYPE, APPLICATION_FORM_URLENCODED_VALUE); - - int seconds = 1; - Date result = restTemplate.postForObject(codeFirstUrl + "addDate?seconds={seconds}", - new HttpEntity<>(body, headers), - Date.class, - seconds); - - assertThat(result, is(Date.from(date.plusSeconds(seconds).toInstant()))); - } - - @Test - public void ableToDeleteWithQueryString() { - ResponseEntity responseEntity = restTemplate.exchange(codeFirstUrl + "addstring?s=a&s=b", - HttpMethod.DELETE, - null, - String.class); - - assertThat(responseEntity.getBody(), is("ab")); - } - - @Test - public void ableToGetBoolean() { - boolean result = restTemplate.getForObject(codeFirstUrl + "istrue", boolean.class); - - assertThat(result, is(true)); - } - - @Test - public void putsEndWithPathParam() { - ResponseEntity responseEntity = restTemplate - .exchange(codeFirstUrl + "sayhi/{name}", PUT, null, String.class, "world"); - - assertThat(responseEntity.getStatusCode(), is(ACCEPTED)); - assertThat(jsonOf(responseEntity.getBody(), String.class), is("world sayhi")); - } - - @Test - public void putsContainingPathParam() { - ResponseEntity responseEntity = restTemplate - .exchange(codeFirstUrl + "sayhi/{name}/v2", PUT, null, String.class, "world"); - - assertThat(jsonOf(responseEntity.getBody(), String.class), is("world sayhi 2")); - } - - @Test - public void ableToPostWithHeader() { - Person person = new Person(); - person.setName("person name"); - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(APPLICATION_JSON); - headers.add("prefix", "prefix prefix"); - - HttpEntity requestEntity = new HttpEntity<>(person, headers); - ResponseEntity responseEntity = restTemplate - .postForEntity(codeFirstUrl + "saysomething", requestEntity, String.class); - - assertThat(jsonOf(responseEntity.getBody(), String.class), is("prefix prefix person name")); - } - - @Test - public void ableToPostObjectAsJson() { - Map personFieldMap = new HashMap<>(); - personFieldMap.put("name", "person name from map"); - - Person person = restTemplate - .postForObject(codeFirstUrl + "sayhello", jsonRequest(personFieldMap), Person.class); - assertThat(person.toString(), is("hello person name from map")); - - Person input = new Person(); - input.setName("person name from Object"); - person = restTemplate.postForObject(codeFirstUrl + "sayhello", jsonRequest(input), Person.class); - - assertThat(person.toString(), is("hello person name from Object")); - } - - @Test - public void ableToPostForm() { - MultiValueMap params = new LinkedMultiValueMap<>(); - params.add("a", "5"); - params.add("b", "3"); - - HttpHeaders headers = new HttpHeaders(); - headers.add(CONTENT_TYPE, APPLICATION_FORM_URLENCODED_VALUE); - int result = restTemplate - .postForObject(codeFirstUrl + "add", new HttpEntity<>(params, headers), Integer.class); - - assertThat(result, is(8)); - } - - @Test - public void ableToExchangeCookie() { - Map params = new HashMap<>(); - params.put("a", "5"); - - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.COOKIE, "b=3"); - - HttpEntity requestEntity = new HttpEntity<>(headers); - ResponseEntity result = restTemplate.exchange( - codeFirstUrl + "reduce?a={a}", - GET, - requestEntity, - Integer.class, - params); - - assertThat(result.getBody(), is(2)); - } - - @Test - public void getsEndWithRequestVariables() { - int result = restTemplate.getForObject( - controllerUrl + "add?a={a}&b={b}", - Integer.class, - 3, - 4); - - assertThat(result, is(7)); - } - - @Test - public void postsEndWithPathParam() { - String result = restTemplate.postForObject( - controllerUrl + "sayhello/{name}", - null, - String.class, - "world"); - - assertThat(jsonOf(result, String.class), is("hello world")); - - List> convertersOld = restTemplate.getMessageConverters(); - List> converters = new ArrayList<>(); - converters.add(new MappingJackson2HttpMessageConverter()); - restTemplate.setMessageConverters(converters); - result = restTemplate.postForObject( - controllerUrl + "sayhello/{name}", - null, - String.class, - "中 国"); - - assertThat(result, is("hello 中 国")); - restTemplate.setMessageConverters(convertersOld); - } - - @Test - public void ableToPostObjectAsJsonWithRequestVariable() { - Person input = new Person(); - input.setName("world"); - - String result = restTemplate.postForObject( - controllerUrl + "saysomething?prefix={prefix}", - jsonRequest(input), - String.class, - "hello"); - - assertThat(jsonOf(result, String.class), is("hello world")); - - List> convertersOld = restTemplate.getMessageConverters(); - List> converters = new ArrayList<>(); - converters.add(new MappingJackson2HttpMessageConverter()); - restTemplate.setMessageConverters(converters); - input = new Person(); - input.setName("中国"); - - result = restTemplate.postForObject( - controllerUrl + "saysomething?prefix={prefix}", - jsonRequest(input), - String.class, - "hello"); - - assertThat(result, is("hello 中国")); - restTemplate.setMessageConverters(convertersOld); - } - - @Test - public void ensureServerWorksFine() { - String result = restTemplate.getForObject( - controllerUrl + "sayhi?name=world", - String.class); - - assertThat(jsonOf(result, String.class), is("hi world [world]")); - } - - @Test - public void ensureServerBlowsUp() { - try { - restTemplate.getForObject( - controllerUrl + "sayhi?name=throwexception", - String.class); - fail("Exception expected, but threw nothing"); - } catch (UnknownHttpStatusCodeException e) { - assertThat(e.getRawStatusCode(), is(590)); - } - } - - @Test - public void ableToSetCustomHeader() { - HttpHeaders headers = new HttpHeaders(); - headers.set("name", "world"); - - HttpEntity requestEntity = new HttpEntity<>(headers); - ResponseEntity result = restTemplate.exchange( - controllerUrl + "sayhei", - GET, - requestEntity, - String.class); - - assertThat(jsonOf(result.getBody(), String.class), is("hei world")); - } - - private HttpEntity jsonRequest(T body) { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(APPLICATION_JSON); - return new HttpEntity<>(body, headers); - } - - private T jsonOf(String json, Class aClass) { - try { - return RestObjectMapper.INSTANCE.readValue(json, aClass); - } catch (IOException e) { - throw new IllegalStateException( - "Failed to read JSON from " + json + ", Exception is: " + e); - } - } -} diff --git a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/SpringMvcTestMain.java b/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/SpringMvcTestMain.java deleted file mode 100644 index dade42e8886..00000000000 --- a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/SpringMvcTestMain.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.springmvc.tests; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class SpringMvcTestMain { - - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/endpoints/CodeFirstSpringmvc.java b/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/endpoints/CodeFirstSpringmvc.java deleted file mode 100644 index 8e304caed8a..00000000000 --- a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/endpoints/CodeFirstSpringmvc.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.springmvc.tests.endpoints; - -import java.util.Date; -import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Response.Status; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.CookieValue; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestAttribute; -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.servicecomb.common.rest.codec.RestObjectMapper; -import io.servicecomb.demo.compute.Person; -import io.servicecomb.demo.server.User; -import io.servicecomb.provider.rest.common.RestSchema; -import io.servicecomb.swagger.extend.annotations.ResponseHeaders; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.context.ContextUtils; -import io.servicecomb.swagger.invocation.context.InvocationContext; -import io.servicecomb.swagger.invocation.response.Headers; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ResponseHeader; - -@RestSchema(schemaId = "codeFirst") -@RequestMapping(path = "/codeFirstSpringmvc", produces = MediaType.APPLICATION_JSON_VALUE) -public class CodeFirstSpringmvc { - - @ResponseHeaders({@ResponseHeader(name = "h1", response = String.class), - @ResponseHeader(name = "h2", response = 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().toString()); - - InvocationContext c2 = ContextUtils.getInvocationContext(); - headers.add("h2", "h2v " + c2.getContext().toString()); - - return new ResponseEntity(date, headers, HttpStatus.ACCEPTED); - } - - @ApiResponse(code = 200, response = User.class, message = "") - @ResponseHeaders({@ResponseHeader(name = "h1", response = String.class), - @ResponseHeader(name = "h2", response = String.class)}) - @RequestMapping(path = "/cseResponse", method = RequestMethod.GET) - public Response cseResponse(InvocationContext c1) { - Response response = Response.createSuccess(Status.ACCEPTED, new User()); - Headers headers = response.getHeaders(); - headers.addHeader("h1", "h1v " + c1.getContext().toString()); - - InvocationContext c2 = ContextUtils.getInvocationContext(); - headers.addHeader("h2", "h2v " + c2.getContext().toString()); - - return response; - } - - @RequestMapping(path = "/testUserMap", method = RequestMethod.POST) - public Map testUserMap(@RequestBody Map userMap) { - return userMap; - } - - @RequestMapping(path = "/textPlain", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE) - public String textPlain(@RequestBody String body) { - return body; - } - - @RequestMapping(path = "/bytes", method = RequestMethod.POST) - public byte[] bytes(@RequestBody byte[] input) { - input[0] = (byte) (input[0] + 1); - return input; - } - - @RequestMapping(path = "/addDate", method = RequestMethod.POST) - public Date addDate(@RequestAttribute("date") Date date, @QueryParam("seconds") long seconds) { - return new Date(date.getTime() + seconds * 1000); - } - - @RequestMapping(path = "/add", method = RequestMethod.POST) - public int add(@RequestAttribute("a") int a, @RequestAttribute("b") int b) { - return a + b; - } - - @RequestMapping(path = "/reduce", method = RequestMethod.GET) - @ApiImplicitParams({@ApiImplicitParam(name = "a", dataType = "integer", format = "int32", paramType = "query")}) - public int reduce(HttpServletRequest request, @CookieValue(name = "b") int b) { - int a = Integer.parseInt(request.getParameter("a")); - return a - b; - } - - @RequestMapping(path = "/sayhello", method = RequestMethod.POST) - public Person sayHello(@RequestBody Person user) { - user.setName("hello " + user.getName()); - return user; - } - - @SuppressWarnings("unchecked") - @RequestMapping(path = "/testrawjson", method = RequestMethod.POST) - public String testRawJsonString(String jsonInput) { - Map person; - try { - person = RestObjectMapper.INSTANCE.readValue(jsonInput.getBytes(), Map.class); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - return "hello " + person.get("name"); - } - - @RequestMapping(path = "/saysomething", method = RequestMethod.POST) - public String saySomething(@RequestHeader(name = "prefix") String prefix, @RequestBody Person user) { - return prefix + " " + user.getName(); - } - - @RequestMapping(path = "/sayhi/{name}", method = RequestMethod.PUT) - public String sayHi(@PathVariable(name = "name") String name) { - ContextUtils.getInvocationContext().setStatus(202); - return name + " sayhi"; - } - - @RequestMapping(path = "/sayhi/{name}/v2", method = RequestMethod.PUT) - public String sayHi2(@PathVariable(name = "name") String name) { - return name + " sayhi 2"; - } - - @RequestMapping(path = "/istrue", method = RequestMethod.GET) - public boolean isTrue() { - return true; - } - - @RequestMapping(path = "/addstring", method = RequestMethod.DELETE, produces = MediaType.TEXT_PLAIN_VALUE) - public String addString(@RequestParam(name = "s") List s) { - String result = ""; - for (String x : s) { - result += x; - } - return result; - } -} diff --git a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/endpoints/ControllerImpl.java b/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/endpoints/ControllerImpl.java deleted file mode 100644 index 007fb455c55..00000000000 --- a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/endpoints/ControllerImpl.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.springmvc.tests.endpoints; - -import java.util.Arrays; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.MediaType; - -import org.springframework.web.bind.annotation.PathVariable; -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.servicecomb.demo.controller.Person; -import io.servicecomb.provider.rest.common.RestSchema; - -@RestSchema(schemaId = "controller") -@RequestMapping(path = "/controller", produces = MediaType.APPLICATION_JSON) -public class ControllerImpl { - - @RequestMapping(path = "/add", method = RequestMethod.GET) - public int add(@RequestParam("a") int a, @RequestParam("b") int b) { - return a + b; - } - - @RequestMapping(path = "/sayhello/{name}", method = RequestMethod.POST) - public String sayHello(@PathVariable("name") String name) { - return "hello " + name; - } - - @RequestMapping(path = "/saysomething", method = RequestMethod.POST) - public String saySomething(String prefix, @RequestBody Person user) { - return prefix + " " + user.getName(); - } - - @RequestMapping(path = "/sayhi", method = RequestMethod.GET) - public String sayHi(HttpServletRequest request) { - String[] values = request.getParameterValues("name"); - if (values != null && values.length > 0 && values[0].equals("throwexception")) { - throw new RuntimeException(); - } - return "hi " + request.getParameter("name") + " " + Arrays.toString(values); - } - - @RequestMapping(path = "/sayhei", method = RequestMethod.GET) - public String sayHei(@RequestHeader("name") String name) { - return "hei " + name; - } -} diff --git a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/endpoints/EnglishGreetingRestEndpoint.java b/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/endpoints/EnglishGreetingRestEndpoint.java deleted file mode 100644 index b4bb5da8351..00000000000 --- a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/endpoints/EnglishGreetingRestEndpoint.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.springmvc.tests.endpoints; - -import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; - -import io.servicecomb.provider.rest.common.RestSchema; - -@RestSchema(schemaId = "englishGreetingRestEndpoint") -@RequestMapping(path = "/") -public class EnglishGreetingRestEndpoint { - - @RequestMapping(path = "/sayHi", method = RequestMethod.GET, produces = TEXT_PLAIN_VALUE) - @ResponseBody - public String sayHi(@RequestParam("name") String name) { - return "Hi " + name; - } - - @RequestMapping(path = "", method = RequestMethod.GET, produces = TEXT_PLAIN_VALUE) - @ResponseBody - public String home() { - return "Welcome home"; - } -} diff --git a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/endpoints/FrenchGreetingRestEndpoint.java b/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/endpoints/FrenchGreetingRestEndpoint.java deleted file mode 100644 index bd84ee5de13..00000000000 --- a/integration-tests/springmvc-tests/src/test/java/io/servicecomb/demo/springmvc/tests/endpoints/FrenchGreetingRestEndpoint.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.demo.springmvc.tests.endpoints; - -import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; - -import io.servicecomb.provider.rest.common.RestSchema; - -@RestSchema(schemaId = "frenchGreetingRestEndpoint") -@RequestMapping(path = "/french") -public class FrenchGreetingRestEndpoint { - - @RequestMapping(path = "/bonjour", method = RequestMethod.GET, produces = TEXT_PLAIN_VALUE) - @ResponseBody - public String bonjour(@RequestParam("name") String name) { - return "Bonjour " + name; - } -} diff --git a/integration-tests/springmvc-tests/src/test/resources/microservice.yaml b/integration-tests/springmvc-tests/src/test/resources/microservice.yaml deleted file mode 100644 index a9e6ed96779..00000000000 --- a/integration-tests/springmvc-tests/src/test/resources/microservice.yaml +++ /dev/null @@ -1,14 +0,0 @@ -APPLICATION_ID: springmvctest-it -service_description: - name: springmvc - version: 0.0.2 -cse: - service: - registry: - address: http://127.0.0.1:9980 - rest: - address: 0.0.0.0:8080 - handler: - chain: - Provider: - default: bizkeeper-provider diff --git a/integration-tests/test-common/pom.xml b/integration-tests/test-common/pom.xml deleted file mode 100644 index f7778672fdd..00000000000 --- a/integration-tests/test-common/pom.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - integration-tests - io.servicecomb.tests - 0.3.0-SNAPSHOT - - 4.0.0 - - test-common - - - - io.servicecomb - provider-rest-common - - - io.servicecomb - provider-springmvc - - - io.servicecomb - provider-jaxrs - - - com.github.tomakehurst - wiremock-standalone - test - - - io.zipkin.java - zipkin-junit - - - com.squareup.okhttp3 - okhttp - test - - - io.zipkin.brave - brave-context-log4j12 - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - test-jar - - - - - - - \ No newline at end of file diff --git a/integration-tests/test-common/src/test/java/io/servicecomb/tests/EmbeddedAppender.java b/integration-tests/test-common/src/test/java/io/servicecomb/tests/EmbeddedAppender.java deleted file mode 100644 index 3e03d328457..00000000000 --- a/integration-tests/test-common/src/test/java/io/servicecomb/tests/EmbeddedAppender.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.tests; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentLinkedQueue; - -import javax.annotation.Nonnull; - -import org.apache.log4j.ConsoleAppender; -import org.apache.log4j.PatternLayout; - -import com.seanyinx.github.unit.scaffolding.Poller; - -public class EmbeddedAppender extends ConsoleAppender { - - private Queue events; - - private final Poller poller = new Poller(5000, 200); - - public EmbeddedAppender() { - super(new PatternLayout("%d{ABSOLUTE} [%X{traceId}/%X{spanId}/%X{parentId}] %-5p [%t] %C{2} (%F:%L) - %m%n")); - } - - @Override - protected OutputStreamWriter createWriter(OutputStream os) { - events = new ConcurrentLinkedQueue<>(); - return super.createWriter(new InMemoryOutputStream(events, os)); - } - - public Collection pollLogs(String regex) { - final Set messages = new LinkedHashSet<>(); - - poller.assertEventually(() -> { - for (String event : events) { - if (event.trim().matches(regex)) { - messages.add(event); - } - } - return !messages.isEmpty(); - }); - - return messages; - } - - public void clear() { - events.clear(); - } - - private static class InMemoryOutputStream extends OutputStream { - - private final Queue events; - - private final OutputStream outputStream; - - InMemoryOutputStream(Queue events, OutputStream outputStream) { - this.events = events; - this.outputStream = outputStream; - } - - @Override - public void flush() throws IOException { - outputStream.flush(); - } - - @Override - public void write(int b) throws IOException { - outputStream.write(b); - events.add(String.valueOf(b)); - } - - @Override - public void write(@Nonnull byte[] b) throws IOException { - outputStream.write(b); - events.add(new String(b)); - } - - @Override - public void write(@Nonnull byte[] b, int off, int len) throws IOException { - outputStream.write(b, off, len); - events.add(new String(b, off, len)); - } - } -} diff --git a/integration-tests/test-common/src/test/java/io/servicecomb/tests/Log4jConfig.java b/integration-tests/test-common/src/test/java/io/servicecomb/tests/Log4jConfig.java deleted file mode 100644 index 6c9d4a1aebc..00000000000 --- a/integration-tests/test-common/src/test/java/io/servicecomb/tests/Log4jConfig.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.tests; - -import org.apache.log4j.Appender; -import org.apache.log4j.Level; -import org.apache.log4j.Logger; - -public class Log4jConfig { - - private static final Logger rootLogger = Logger.getRootLogger(); - - public static Logger addAppender(Appender appender) { - rootLogger.removeAllAppenders(); - rootLogger.setLevel(Level.INFO); - rootLogger.addAppender(appender); - return rootLogger; - } - - public static Logger setLevel(Level level) { - rootLogger.setLevel(level); - return rootLogger; - } -} diff --git a/integration-tests/test-common/src/test/java/io/servicecomb/tests/tracing/TraceContextConfig.java b/integration-tests/test-common/src/test/java/io/servicecomb/tests/tracing/TraceContextConfig.java deleted file mode 100644 index 8caf94716e8..00000000000 --- a/integration-tests/test-common/src/test/java/io/servicecomb/tests/tracing/TraceContextConfig.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.tests.tracing; - -import org.apache.log4j.MDC; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; - -import brave.internal.HexCodec; -import brave.propagation.CurrentTraceContext; -import brave.propagation.TraceContext; - -@Configuration -class TraceContextConfig { - - @Bean - @Primary - CurrentTraceContext traceContext() { - return ParentAwareMDCCurrentTraceContext.create(); - } - - private static class ParentAwareMDCCurrentTraceContext extends CurrentTraceContext { - static ParentAwareMDCCurrentTraceContext create() { - return new ParentAwareMDCCurrentTraceContext(new Default()); - } - - private final CurrentTraceContext delegate; - - ParentAwareMDCCurrentTraceContext(CurrentTraceContext delegate) { - if (delegate == null) { - throw new NullPointerException("delegate == null"); - } - this.delegate = delegate; - } - - @Override - public TraceContext get() { - return delegate.get(); - } - - @Override - public Scope newScope(TraceContext currentSpan) { - final Object previousTraceId = MDC.get("traceId"); - final Object previousSpanId = MDC.get("spanId"); - final Object previousParentId = MDC.get("parentId"); - - if (currentSpan != null) { - MDC.put("traceId", currentSpan.traceIdString()); - MDC.put("spanId", HexCodec.toLowerHex(currentSpan.spanId())); - if (currentSpan.parentId() != null) { - MDC.put("parentId", HexCodec.toLowerHex(currentSpan.parentId())); - } - } else { - MDC.remove("traceId"); - MDC.remove("spanId"); - MDC.remove("parentId"); - } - - Scope scope = delegate.newScope(currentSpan); - class MDCCurrentTraceContextScope implements Scope { - @Override - public void close() { - scope.close(); - if (previousTraceId != null) { - MDC.put("traceId", previousTraceId); - } else { - MDC.remove("traceId"); - } - - if (previousSpanId != null) { - MDC.put("spanId", previousSpanId); - } else { - MDC.remove("spanId"); - } - - if (previousParentId != null) { - MDC.put("parentId", previousParentId); - } else { - MDC.remove("parentId"); - } - } - } - return new MDCCurrentTraceContextScope(); - } - } -} diff --git a/integration-tests/test-common/src/test/java/io/servicecomb/tests/tracing/TracingTestBase.java b/integration-tests/test-common/src/test/java/io/servicecomb/tests/tracing/TracingTestBase.java deleted file mode 100644 index d60d5778c53..00000000000 --- a/integration-tests/test-common/src/test/java/io/servicecomb/tests/tracing/TracingTestBase.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.tests.tracing; - -import static io.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_TRACING_COLLECTOR_ADDRESS; -import static io.servicecomb.serviceregistry.client.LocalServiceRegistryClientImpl.LOCAL_REGISTRY_FILE_KEY; -import static org.hamcrest.collection.IsIterableContainingInOrder.contains; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; -import static org.springframework.http.HttpStatus.OK; - -import java.lang.invoke.MethodHandles; -import java.net.URL; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; - -import io.servicecomb.tests.EmbeddedAppender; -import io.servicecomb.tests.Log4jConfig; -import zipkin.Codec; -import zipkin.Span; -import zipkin.junit.ZipkinRule; - -public class TracingTestBase { - - @ClassRule - public static final ZipkinRule zipkin = new ZipkinRule(); - - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - protected static final EmbeddedAppender appender = new EmbeddedAppender(); - - final RestTemplate restTemplate = new RestTemplate(); - - @BeforeClass - public static void setUpClass() throws Exception { - System.setProperty(CONFIG_TRACING_COLLECTOR_ADDRESS, zipkin.httpUrl()); - setUpLocalRegistry(); - - Log4jConfig.addAppender(appender); - } - - private static void setUpLocalRegistry() { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - URL resource = loader.getResource("registry.yaml"); - System.setProperty(LOCAL_REGISTRY_FILE_KEY, resource.getPath()); - } - - protected void assertThatSpansReceivedByZipkin(Collection logs, String... values) { - logs.forEach(message -> log.info("Received log: " + message)); - List loggedIds = logs.stream() - .map(this::extractIds) - .collect(Collectors.toList()); - - String url = zipkin.httpUrl() + "/api/v1/trace/{traceId}"; - ResponseEntity responseEntity = restTemplate.getForEntity(url, String.class, traceId(loggedIds)); - - assertThat(responseEntity.getStatusCode(), is(OK)); - String body = responseEntity.getBody(); - log.info("Received trace json: {}", body); - List spans = Codec.JSON.readSpans(body.getBytes()); - - List tracedValues = tracedValues(spans); - tracedValues.forEach(value -> log.info("Received value {}", value)); - assertThat(tracedValues, contains(values)); - } - - private List tracedValues(List spans) { - return spans.stream() - .filter(span -> span.binaryAnnotations != null) - .map(span -> span.binaryAnnotations) - .flatMap(Collection::stream) - .filter(span -> "http.path".equals(span.key) || "http.status_code".equals(span.key)) - .filter(span -> span.value != null) - .map(annotation -> new String(annotation.value)) - .distinct() - .collect(Collectors.toList()); - } - - private String extractIds(String message) { - return message.replaceFirst(".*\\[(\\w+/\\w+/\\w*)\\].*", "$1").trim(); - } - - private String traceId(List ids) { - return ids.get(0).split("/")[0]; - } -} diff --git a/integration-tests/tracing-tests/pom.xml b/integration-tests/tracing-tests/pom.xml deleted file mode 100644 index 3432584123b..00000000000 --- a/integration-tests/tracing-tests/pom.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - integration-tests - io.servicecomb.tests - 0.3.0-SNAPSHOT - - 4.0.0 - - tracing-tests - - - - io.servicecomb.tests - test-common - test-jar - - - org.slf4j - slf4j-log4j12 - - - log4j - log4j - - - io.servicecomb - provider-springmvc - - - io.servicecomb - provider-jaxrs - - - io.servicecomb - provider-pojo - - - io.servicecomb - handler-tracing-zipkin - - - io.servicecomb - transport-rest-vertx - - - io.zipkin.java - zipkin-junit - - - com.squareup.okhttp3 - okhttp - - - - \ No newline at end of file diff --git a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/SomeTracedController.java b/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/SomeTracedController.java deleted file mode 100644 index 672877eaa36..00000000000 --- a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/SomeTracedController.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.tests.tracing; - -import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE; -import static org.springframework.web.bind.annotation.RequestMethod.GET; - -import java.util.Random; - -import javax.servlet.http.HttpServletRequest; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.RestTemplate; - -import io.servicecomb.provider.rest.common.RestSchema; - -@RestSchema(schemaId = "someTracedRestEndpoint") -@RestController -@RequestMapping("/") -public class SomeTracedController { - private static final Logger logger = LoggerFactory.getLogger(SomeTracedController.class); - - private final Random random = new Random(); - - @Autowired - private RestTemplate template; - - @RequestMapping(value = "/hello", method = GET, produces = TEXT_PLAIN_VALUE) - public String hello(HttpServletRequest request) throws InterruptedException { - logger.info("in /hello"); - Thread.sleep(random.nextInt(1000)); - - return "hello world, " + template.getForObject("cse://tracing-service/jaxrs/bonjour", String.class); - } -} diff --git a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracedJaxrsController.java b/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracedJaxrsController.java deleted file mode 100644 index afda2684dfe..00000000000 --- a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracedJaxrsController.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.tests.tracing; - -import static javax.ws.rs.core.MediaType.TEXT_PLAIN; - -import java.util.Random; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; - -import io.servicecomb.provider.rest.common.RestSchema; - -@RestSchema(schemaId = "someTracedJaxrsRestEndpoint") -@Path("/jaxrs") -public class TracedJaxrsController { - private static final Logger logger = LoggerFactory.getLogger(TracedJaxrsController.class); - - private final Random random = new Random(); - - //@Autowired - //private RestTemplate template; - - @Autowired - private TracedPojo tracedPojo; - - @GET - @Path("/bonjour") - @Produces(TEXT_PLAIN) - public String bonjour(HttpServletRequest request) throws InterruptedException { - logger.info("in /bonjour"); - Thread.sleep(random.nextInt(1000)); - - return "bonjour le monde, " + tracedPojo.pojo(); - } -} diff --git a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracedPojo.java b/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracedPojo.java deleted file mode 100644 index d5636c8fa21..00000000000 --- a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracedPojo.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.tests.tracing; - -interface TracedPojo { - - String pojo(); -} diff --git a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracedPojoConfig.java b/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracedPojoConfig.java deleted file mode 100644 index 7a7adb5f791..00000000000 --- a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracedPojoConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.tests.tracing; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import io.servicecomb.provider.pojo.RpcReference; - -@Configuration -class TracedPojoConfig { - - @RpcReference(microserviceName = "tracing-service", schemaId = "tracedPojo") - private TracedPojo tracedPojo; - - @Bean - TracedPojo tracedPojo() { - return tracedPojo; - } -} diff --git a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracedPojoImpl.java b/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracedPojoImpl.java deleted file mode 100644 index 1bb40abbcd9..00000000000 --- a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracedPojoImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.tests.tracing; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.provider.pojo.RpcSchema; - -@RpcSchema(schemaId = "tracedPojo") -public class TracedPojoImpl implements TracedPojo { - - private static final Logger logger = LoggerFactory.getLogger(TracedPojoImpl.class); - - @Override - public String pojo() { - logger.info("in /pojo"); - return "hi pojo"; - } -} diff --git a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracingTestMain.java b/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracingTestMain.java deleted file mode 100644 index a33427da5a0..00000000000 --- a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/TracingTestMain.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.tests.tracing; - -import io.servicecomb.foundation.common.utils.BeanUtils; - -public class TracingTestMain { - - public static void main(String[] args) { - BeanUtils.init(); - } -} diff --git a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/ZipkinTracingIntegrationTest.java b/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/ZipkinTracingIntegrationTest.java deleted file mode 100644 index 149b35a8614..00000000000 --- a/integration-tests/tracing-tests/src/test/java/io/servicecomb/tests/tracing/ZipkinTracingIntegrationTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.tests.tracing; - -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; -import static org.springframework.http.HttpStatus.OK; - -import java.util.Collection; -import java.util.concurrent.TimeUnit; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.http.ResponseEntity; - -public class ZipkinTracingIntegrationTest extends TracingTestBase { - - @Before - public void setUp() throws Exception { - TracingTestMain.main(new String[0]); - } - - @Test - public void sendsTracingToConfiguredAddress() throws InterruptedException { - ResponseEntity entity = restTemplate.getForEntity("http://localhost:8080/hello", String.class); - - assertThat(entity.getStatusCode(), is(OK)); - assertThat(entity.getBody(), is("hello world, bonjour le monde, hi pojo")); - - TimeUnit.MILLISECONDS.sleep(1000); - - Collection tracingMessages = appender.pollLogs(".*\\[\\w+/\\w+/\\w*\\]\\s+INFO.*in /.*"); - assertThat(tracingMessages.size(), greaterThanOrEqualTo(2)); - - assertThatSpansReceivedByZipkin(tracingMessages, "/hello", "/bonjour", "/pojo"); - } -} diff --git a/integration-tests/tracing-tests/src/test/resources/microservice.yaml b/integration-tests/tracing-tests/src/test/resources/microservice.yaml deleted file mode 100644 index 53728235ca5..00000000000 --- a/integration-tests/tracing-tests/src/test/resources/microservice.yaml +++ /dev/null @@ -1,23 +0,0 @@ -APPLICATION_ID: tracing-app -service_description: - name: tracing-service - version: 0.0.2 -cse: - service: - registry: - address: http://127.0.0.1:9980 - rest: - address: 0.0.0.0:8080?sslEnabled=false - highway: - address: 0.0.0.0:7070 - handler: - chain: - Provider: - default: tracing-provider,bizkeeper-provider - Consumer: - default: tracing-consumer,loadbalance,bizkeeper-consumer - -servicecomb: - tracing: - collector: - address: http://localhost:9411 diff --git a/integration-tests/tracing-tests/src/test/resources/registry.yaml b/integration-tests/tracing-tests/src/test/resources/registry.yaml deleted file mode 100644 index da80b64d2d5..00000000000 --- a/integration-tests/tracing-tests/src/test/resources/registry.yaml +++ /dev/null @@ -1,8 +0,0 @@ -unknown-service: - - id: "001" - version: "0.0.2" - appid: tracing-app - instances: - - endpoints: - - rest://127.0.0.1:8080?sslEnabled=false - - highway://127.0.0.1:7070?login=true diff --git a/java-chassis-dependencies/pom.xml b/java-chassis-dependencies/pom.xml deleted file mode 100644 index b6bae381ad2..00000000000 --- a/java-chassis-dependencies/pom.xml +++ /dev/null @@ -1,1089 +0,0 @@ - - - - - 4.0.0 - - - io.servicecomb - java-chassis - 0.3.0-SNAPSHOT - - - java-chassis-dependencies - pom - - - 2.8.1 - 3.3.3 - 0.8 - 4.3.4.RELEASE - 1.7.7 - 1.2.17 - 2.4 - 3.18.1-GA - 2.0.1 - 3.1.0 - 4.5.2 - 1.5.2 - 1.5.12 - 4.1.5.Final - ${basedir}/../.. - 5.3.2.Final - 3.1.6 - 1.1.7 - 4.4.0 - 1.24.0 - 0.10.0 - - - - - - javax.inject - javax.inject - 1 - - - com.netflix.ribbon - ribbon - 2.2.2 - - - com.netflix.ribbon - ribbon-core - 2.2.2 - - - com.google.code.findbugs - annotations - - - - - com.netflix.ribbon - ribbon-loadbalancer - 2.2.2 - - - com.google.code.findbugs - annotations - - - - - com.netflix.hystrix - hystrix-core - 1.5.10 - - - ch.qos.logback - logback-classic - ${logback.version} - - - ch.qos.logback - logback-core - ${logback.version} - - - junit - junit - 4.12 - test - - - io.reactivex - rxjava - 1.1.6 - - - org.mockito - mockito-core - 1.10.19 - test - - - org.mockito - mockito-all - 1.10.19 - test - - - org.jmockit - jmockit - 1.30 - test - - - org.hamcrest - hamcrest-core - 1.3 - test - - - com.dyuproject.protostuff - protostuff-api - 1.0.7.fixed.3500 - - - com.dyuproject.protostuff - protostuff-collectionschema - 1.0.7.fixed.3500 - - - com.dyuproject.protostuff - protostuff-core - 1.0.7.fixed.3500 - - - com.dyuproject.protostuff - protostuff-runtime - 1.0.7.fixed.3500 - - - javax.validation - validation-api - 1.1.0.Final - - - org.hibernate - hibernate-validator - 5.2.4.Final - - - - org.aspectj - aspectjweaver - 1.8.8 - - - com.google.guava - guava - 16.0.1 - - - - io.netty - netty-codec - ${netty.version} - - - io.netty - netty-codec-http - ${netty.version} - - - io.netty - netty-codec-http2 - ${netty.version} - - - io.netty - netty-common - ${netty.version} - - - io.netty - netty-handler - ${netty.version} - - - io.netty - netty-buffer - ${netty.version} - - - io.netty - netty-codec-dns - ${netty.version} - - - io.netty - netty-codec-socks - ${netty.version} - - - io.netty - netty-handler-proxy - ${netty.version} - - - io.netty - netty-resolver - ${netty.version} - - - io.netty - netty-resolver-dns - ${netty.version} - - - io.netty - netty-transport - ${netty.version} - - - org.powermock - powermock-api-mockito - 1.6.2 - test - - - org.powermock - powermock-module-junit4 - 1.6.2 - test - - - - org.apache.tomcat.embed - tomcat-embed-core - 8.0.33 - - - org.apache.tomcat.embed - tomcat-embed-el - 8.0.33 - - - org.apache.tomcat.embed - tomcat-embed-logging-juli - 8.0.33 - - - org.apache.tomcat.embed - tomcat-embed-websocket - 8.0.33 - - - - com.squareup.okhttp3 - okhttp - 3.4.1 - - - - org.slf4j - log4j-over-slf4j - ${slf4j.version} - - - - commons-lang - commons-lang - 2.6 - - - - commons-logging - commons-logging - 1.2 - - - - - org.jboss.narayana.rts - restat-util - 5.2.8.Final - - - - org.jboss.logging - jboss-logging - 3.2.1.Final - - - org.jboss.narayana.rts - restat-api - 5.2.8.Final - - - - org.jboss.narayana.rts - restat-integration - 5.2.8.Final - - - - org.jboss.narayana.rts - restat-bridge - 5.2.8.Final - - - org.jboss.narayana.arjunacore - arjunacore - 5.2.8.Final - - - org.jboss.resteasy - resteasy-undertow - 3.0.11.Final - - - - org.jboss.narayana.xts - jbossxts - ${narayana.version} - - - org.jboss.narayana - common - ${narayana.version} - - - org.jboss.narayana - jbosstxbridge - ${narayana.version} - - - org.jboss.narayana.jta - jta - ${narayana.version} - - - - javax.resource - connector-api - 1.5 - - - org.yaml - snakeyaml - 1.16 - - - org.hibernate - hibernate-validator-annotation-processor - 5.2.2.Final - - - javax.transaction - javax.transaction-api - 1.2 - - - - org.jboss.ws.cxf - jbossws-cxf-client - 5.1.3.Final - - - org.jboss.ws.cxf - jbossws-cxf-transports-undertow - - - org.jboss.ws.cxf - jbossws-cxf-transports-udp - - - - - - - org.jboss - jboss-transaction-spi - 7.3.2.Final - - - org.apache.commons - commons-lang3 - 3.3.2 - - - commons-beanutils - commons-beanutils - 1.9.2 - - - org.apache.cxf - cxf-rt-frontend-jaxws - ${cxf.version} - - - org.apache.cxf - cxf-rt-transports-http - ${cxf.version} - - - com.netflix.servo - servo-core - 0.10.1 - - - com.google.inject - guice - 4.0 - - - org.glassfish.jersey.core - jersey-common - 2.22.2 - - - io.reactivex - rxnetty - 0.4.9 - - - - io.netty - netty-codec-http - - - io.netty - netty-handler - - - io.netty - netty-transport-native-epoll - - - - - - com.esotericsoftware - kryo-shaded - 3.0.3 - - - de.javakaffee - kryo-serializers - 0.26 - - - de.ruedigermoeller - fst - 1.55 - - - org.glassfish.web - el-impl - 2.2 - - - com.sun.jersey - jersey-json - 1.19.1 - - - - net.sf.json-lib - json-lib - 2.4 - jdk15 - - - com.netflix.governator - governator-api - 1.12.10 - - - org.codehaus.woodstox - woodstox-core-asl - 4.4.1 - provided - - - - org.codehaus.jettison - jettison - 1.3.8 - runtime - - - com.netflix.archaius - archaius-core - 0.7.3 - - - com.google.code.findbugs - jsr305 - 3.0.1 - - - commons-configuration - commons-configuration - 1.10 - - - - com.netflix.netflix-commons - netflix-eventbus - 0.3.0 - - - javax.servlet - servlet-api - - - - - com.thoughtworks.xstream - xstream - 1.4.9 - - - org.glassfish.jersey.media - jersey-media-json-jackson - 2.23.2 - - - com.sun.jersey - jersey-client - 1.19.1 - - - com.sun.jersey.contribs - jersey-apache-client4 - 1.19.1 - - - com.netflix.governator - governator-annotations - 1.14.2 - - - - com.netflix.netflix-commons - netflix-eventbus-rx - 0.3.0 - - - javax.servlet - servlet-api - - - com.netflix.rxjava - rxjava-core - - - - - - org.apache.zookeeper - zookeeper - 3.4.6 - provided - true - - - redis.clients - jedis - 2.8.0 - provided - true - - - org.mock-server - mockserver-netty - 3.10.4 - test - - - javax.annotation - javax.annotation-api - 1.2 - - - org.powermock - powermock-module-test-mockito-junit4-delegate - 1.6.2 - test - - - - org.apache.httpcomponents - httpclient - ${apache.httpclient} - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.slf4j - slf4j-ext - ${slf4j.version} - - - org.slf4j - slf4j-log4j12 - ${slf4j.version} - - - log4j - log4j - ${log4j.version} - - - commons-io - commons-io - ${commons.io.version} - - - javax.ws.rs - javax.ws.rs-api - ${javax.ws.rs.version} - - - javax.servlet - javax.servlet-api - ${javax.servlet.version} - - - org.javassist - javassist - ${javassist.version} - - - org.springframework - spring-context - ${spring.version} - - - org.springframework - spring-aop - ${spring.version} - - - org.springframework - spring-aspects - ${spring.version} - - - org.springframework - spring-beans - ${spring.version} - - - org.springframework - spring-core - ${spring.version} - - - org.springframework - spring-expression - ${spring.version} - - - org.springframework - spring-web - ${spring.version} - - - org.springframework - spring-webmvc - ${spring.version} - - - org.springframework - spring-jdbc - provided - true - ${spring.version} - - - com.fasterxml.jackson.core - jackson-core - ${jackson.version} - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - - - com.fasterxml.jackson.core - jackson-annotations - ${jackson.version} - - - io.vertx - vertx-core - ${vertx.version} - - - io.vertx - vertx-web - ${vertx.version} - - - com.101tec - zkclient - ${tec.zkclient.version} - - - - org.springframework.boot - spring-boot-dependencies - 1.4.5.RELEASE - pom - import - - - - org.springframework.cloud - spring-cloud-dependencies - Camden.SR6 - pom - import - - - - - io.zipkin.java - zipkin - ${zipkin.version} - - - io.zipkin.brave - brave - ${brave.version} - - - io.zipkin.brave - brave-spring-beans - ${brave.version} - - - io.zipkin.reporter - zipkin-sender-okhttp3 - ${zipkin-reporter.version} - - - io.zipkin.brave - brave-context-log4j12 - ${brave.version} - - - io.zipkin.brave - brave-instrumentation-servlet - ${brave.version} - - - io.zipkin.brave - brave-instrumentation-jaxrs2 - ${brave.version} - - - io.zipkin.brave - brave-instrumentation-spring-webmvc - ${brave.version} - - - io.zipkin.brave - brave-instrumentation-spring-web - ${brave.version} - - - - io.servicecomb - foundation-common - 0.3.0-SNAPSHOT - - - io.servicecomb - foundation-ssl - 0.3.0-SNAPSHOT - - - io.servicecomb - foundation-config - 0.3.0-SNAPSHOT - - - io.servicecomb - foundation-vertx - 0.3.0-SNAPSHOT - - - io.servicecomb - foundation-metrics - 0.3.0-SNAPSHOT - - - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml - ${jackson.version} - - - com.fasterxml.jackson.dataformat - jackson-dataformat-protobuf - ${jackson.version} - - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - ${jackson.version} - - - com.fasterxml.jackson.datatype - jackson-datatype-joda - ${jackson.version} - - - io.protostuff - protostuff-core - ${protostuff.version} - - - io.protostuff - protostuff-runtime - ${protostuff.version} - - - io.swagger - swagger-core - ${swagger.version} - - - io.servicecomb - java-chassis-core - 0.3.0-SNAPSHOT - - - io.servicecomb - handler-bizkeeper - 0.3.0-SNAPSHOT - - - io.servicecomb - handler-loadbalance - 0.3.0-SNAPSHOT - - - io.servicecomb - handler-flowcontrol-qps - 0.3.0-SNAPSHOT - - - io.servicecomb - common-rest - 0.3.0-SNAPSHOT - - - io.servicecomb - transport-rest-client - 0.3.0-SNAPSHOT - - - io.servicecomb - transport-rest-vertx - 0.3.0-SNAPSHOT - - - io.servicecomb - transport-rest-servlet - 0.3.0-SNAPSHOT - - - io.servicecomb - common-protobuf - 0.3.0-SNAPSHOT - - - io.servicecomb - common-javassist - 0.3.0-SNAPSHOT - - - io.servicecomb - swagger-generator-core - 0.3.0-SNAPSHOT - - - io.servicecomb - swagger-generator-jaxrs - 0.3.0-SNAPSHOT - - - io.servicecomb - swagger-generator-springmvc - 0.3.0-SNAPSHOT - - - io.servicecomb - swagger-invocation-core - 0.3.0-SNAPSHOT - - - io.servicecomb - swagger-invocation-jaxrs - 0.3.0-SNAPSHOT - - - io.servicecomb - swagger-invocation-springmvc - 0.3.0-SNAPSHOT - - - io.servicecomb - service-registry - 0.3.0-SNAPSHOT - - - io.servicecomb - provider-pojo - 0.3.0-SNAPSHOT - - - io.servicecomb - provider-rest-common - 0.3.0-SNAPSHOT - - - io.servicecomb - provider-jaxrs - 0.3.0-SNAPSHOT - - - io.servicecomb - provider-springmvc - 0.3.0-SNAPSHOT - - - io.servicecomb - transport-highway - 0.3.0-SNAPSHOT - - - io.servicecomb - spring-boot-starter-configuration - 0.3.0-SNAPSHOT - - - io.servicecomb - spring-boot-starter-servicecomb - 0.3.0-SNAPSHOT - - - io.servicecomb - spring-boot-starter-provider - 0.3.0-SNAPSHOT - - - io.servicecomb - spring-boot-starter-discovery - 0.3.0-SNAPSHOT - - - io.servicecomb - spring-boot-starter-transport - 0.3.0-SNAPSHOT - - - io.servicecomb - spring-cloud-zuul-zipkin - 0.3.0-SNAPSHOT - - - io.servicecomb - handler-tracing-zipkin - 0.3.0-SNAPSHOT - - - - - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.1 - - 1.8 - 1.8 - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.jacoco - jacoco-maven-plugin - [0.0.1,) - - prepare-agent - - - - - - - - - - - - org.eluder.coveralls - coveralls-maven-plugin - 4.3.0 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 2.4 - - - org.apache.maven.plugins - maven-site-plugin - 3.4 - - - org.apache.maven.plugins - maven-checkstyle-plugin - 2.17 - true - - - com.puppycrawl.tools - checkstyle - 7.6 - - - - - org.jacoco - jacoco-maven-plugin - 0.7.8 - - - - - - - diff --git a/java-chassis-distribution/pom.xml b/java-chassis-distribution/pom.xml deleted file mode 100644 index b0059e071df..00000000000 --- a/java-chassis-distribution/pom.xml +++ /dev/null @@ -1,164 +0,0 @@ - - 4.0.0 - - io.servicecomb - java-chassis-parent - 0.3.0-SNAPSHOT - ../parent - - java-chassis-distribution - pom - - - io.servicecomb - common-javassist - - - io.servicecomb - common-protobuf - - - io.servicecomb - common-rest - - - io.servicecomb - java-chassis-core - - - io.servicecomb - spring-boot-starter-configuration - - - io.servicecomb - spring-boot-starter-provider - - - io.servicecomb - spring-boot-starter-discovery - - - io.servicecomb - spring-boot-starter-transport - - - io.servicecomb - swagger-generator-core - - - io.servicecomb - swagger-generator-jaxrs - - - io.servicecomb - swagger-generator-springmvc - - - io.servicecomb - swagger-invocation-core - - - io.servicecomb - swagger-invocation-jaxrs - - - io.servicecomb - swagger-invocation-springmvc - - - io.servicecomb - foundation-common - - - io.servicecomb - foundation-config - - - io.servicecomb - foundation-metrics - - - io.servicecomb - foundation-ssl - - - io.servicecomb - foundation-vertx - - - io.servicecomb - handler-bizkeeper - - - io.servicecomb - handler-flowcontrol-qps - - - io.servicecomb - handler-loadbalance - - - io.servicecomb - provider-jaxrs - - - io.servicecomb - provider-pojo - - - io.servicecomb - provider-rest-common - - - io.servicecomb - provider-springmvc - - - io.servicecomb - service-registry - - - io.servicecomb - transport-highway - - - io.servicecomb - transport-rest-client - - - io.servicecomb - transport-rest-servlet - - - io.servicecomb - transport-rest-vertx - - - - - - release - - - - maven-assembly-plugin - - - package - - single - - - - src/assembly/assembly.xml - - - - - - - - - - diff --git a/java-chassis-distribution/src/assembly/assembly.xml b/java-chassis-distribution/src/assembly/assembly.xml deleted file mode 100644 index 2fc2e835c71..00000000000 --- a/java-chassis-distribution/src/assembly/assembly.xml +++ /dev/null @@ -1,25 +0,0 @@ - - false - - zip - - - - ../samples - java-chassis-distribution-${project.version}/samples - - **/target - **/target/**/* - - - - - - true - false - java-chassis-distribution-${project.version}/libs - runtime - - - diff --git a/metrics/metrics-core/pom.xml b/metrics/metrics-core/pom.xml new file mode 100644 index 00000000000..c68b483fde9 --- /dev/null +++ b/metrics/metrics-core/pom.xml @@ -0,0 +1,73 @@ + + + + + + metrics + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + metrics-core + Java Chassis::Metrics::Core + + + + org.apache.servicecomb + foundation-metrics + + + org.apache.servicecomb + java-chassis-core + + + org.apache.servicecomb + swagger-generator-jaxrs + + + org.apache.servicecomb + common-rest + + + org.apache.servicecomb + foundation-test-scaffolding + + + org.apache.servicecomb + registry-service-center + test + + + io.vertx + vertx-codegen + provided + + + org.mockito + mockito-inline + test + + + org.jmockit + jmockit + test + + + diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/InvocationMetersInitializer.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/InvocationMetersInitializer.java new file mode 100644 index 00000000000..25bc9b62e71 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/InvocationMetersInitializer.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.metrics.core; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.InvocationStartEvent; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.foundation.metrics.MetricsInitializer; +import org.apache.servicecomb.metrics.core.meter.ConsumerMeters; +import org.apache.servicecomb.metrics.core.meter.EdgeMeters; +import org.apache.servicecomb.metrics.core.meter.ProducerMeters; +import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + +import io.micrometer.core.instrument.MeterRegistry; + +public class InvocationMetersInitializer implements MetricsInitializer { + private ConsumerMeters consumerMeters; + + private ProducerMeters producerMeters; + + private EdgeMeters edgeMeters; + + @Override + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + consumerMeters = new ConsumerMeters(meterRegistry, config); + producerMeters = new ProducerMeters(meterRegistry, config); + edgeMeters = new EdgeMeters(meterRegistry, config); + + eventBus.register(this); + } + + protected AbstractInvocationMeters findInvocationMeters(Invocation invocation) { + if (invocation.isConsumer()) { + return consumerMeters.getInvocationMeters(); + } + if (invocation.isEdge()) { + return edgeMeters.getInvocationMeters(); + } + return producerMeters.getInvocationMeters(); + } + + @Subscribe + @AllowConcurrentEvents + public void onInvocationStart(InvocationStartEvent event) { + AbstractInvocationMeters invocationMeters = findInvocationMeters(event.getInvocation()); + invocationMeters.onInvocationStart(event); + } + + @Subscribe + @AllowConcurrentEvents + public void onInvocationFinish(InvocationFinishEvent event) { + AbstractInvocationMeters invocationMeters = findInvocationMeters(event.getInvocation()); + invocationMeters.onInvocationFinish(event); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java new file mode 100644 index 00000000000..eb9e4347544 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.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.metrics.core; + +import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrap; + +public class MetricsBootListener implements BootListener { + private final MetricsBootstrap metricsBootstrap; + + public MetricsBootListener(MetricsBootstrap metricsBootstrap) { + this.metricsBootstrap = metricsBootstrap; + } + + @Override + public void onBeforeProducerProvider(BootEvent event) { + + } + + public MetricsBootstrap getMetricsBootstrap() { + return metricsBootstrap; + } + + @Override + public void onAfterRegistry(BootEvent event) { + metricsBootstrap.start(EventManager.getEventBus()); + } + + @Override + public void onBeforeClose(BootEvent event) { + metricsBootstrap.shutdown(); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsCoreConfiguration.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsCoreConfiguration.java new file mode 100644 index 00000000000..338b2183d02 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsCoreConfiguration.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.metrics.core; + +import java.time.Duration; + +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrap; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.metrics.core.publish.DefaultLogPublisher; +import org.apache.servicecomb.metrics.core.publish.SlowInvocationLogger; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.CountingMode; + +@Configuration +public class MetricsCoreConfiguration { + @Bean + public MetricsBootstrapConfig metricsBootstrapConfig(Environment environment) { + return new MetricsBootstrapConfig(environment); + } + + @Bean + @ConditionalOnMissingBean + public MeterRegistry meterRegistry(MetricsBootstrapConfig config) { + return new SimpleMeterRegistryExt(s -> { + if ("simple.step".equals(s)) { + return Duration.ofMillis(config.getMsPollInterval()).toString(); + } + if ("simple.mode".equals(s)) { + return CountingMode.STEP.name(); + } + return null; + }, Clock.SYSTEM); + } + + @Bean + public MetricsBootListener metricsBootListener(MetricsBootstrap metricsBootstrap) { + return new MetricsBootListener(metricsBootstrap); + } + + @Bean + public MetricsBootstrap metricsBootstrap(MetricsBootstrapConfig config) { + return new MetricsBootstrap(config); + } + + // Begin MetricsInitializers + + @Bean + public DefaultLogPublisher defaultLogPublisher() { + return new DefaultLogPublisher(); + } + + @Bean + public InvocationMetersInitializer invocationMetersInitializer() { + return new InvocationMetersInitializer(); + } + + @Bean + public ThreadPoolMetersInitializer threadPoolMetersInitializer() { + return new ThreadPoolMetersInitializer(); + } + + @Bean + public VertxMetersInitializer vertxMetersInitializer() { + return new VertxMetersInitializer(); + } + + @Bean + public OsMetersInitializer osMetersInitializer() { + return new OsMetersInitializer(); + } + + @Bean + public SlowInvocationLogger slowInvocationLogger(SCBEngine scbEngine) { + return new SlowInvocationLogger(scbEngine); + } + + // End MetricsInitializers +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/OsMetersInitializer.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/OsMetersInitializer.java new file mode 100644 index 00000000000..f5486f78089 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/OsMetersInitializer.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.metrics.core; + +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.foundation.metrics.MetricsInitializer; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; +import org.apache.servicecomb.metrics.core.meter.os.OsMeter; + +import com.google.common.eventbus.EventBus; + +import io.micrometer.core.instrument.MeterRegistry; + +public class OsMetersInitializer implements MetricsInitializer, PeriodMeter { + private OsMeter osMeter; + + @Override + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + osMeter = new OsMeter(meterRegistry); + } + + public OsMeter getOsMeter() { + return osMeter; + } + + @Override + public void poll(long msNow, long secondInterval) { + if (osMeter != null) { + osMeter.poll(msNow, secondInterval); + } + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/SimpleMeterRegistryExt.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/SimpleMeterRegistryExt.java new file mode 100644 index 00000000000..592ec2af113 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/SimpleMeterRegistryExt.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.metrics.core; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.cumulative.CumulativeDistributionSummary; +import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; +import io.micrometer.core.instrument.distribution.HistogramGauges; +import io.micrometer.core.instrument.distribution.StepBucketHistogram; +import io.micrometer.core.instrument.simple.SimpleConfig; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; + +/** + * Custom SimpleMeterRegistry to support StepDistributionSummary + */ +public class SimpleMeterRegistryExt extends SimpleMeterRegistry { + private final SimpleConfig config; + + public SimpleMeterRegistryExt(SimpleConfig config, Clock clock) { + super(config, clock); + this.config = config; + } + + @Override + protected DistributionSummary newDistributionSummary(Meter.Id id, + DistributionStatisticConfig distributionStatisticConfig, double scale) { + DistributionStatisticConfig merged = distributionStatisticConfig + .merge(DistributionStatisticConfig.builder().expiry(config.step()).build()); + + DistributionSummary summary; + switch (config.mode()) { + case CUMULATIVE: + summary = new CumulativeDistributionSummary(id, clock, merged, scale, false); + break; + case STEP: + default: + summary = new StepDistributionSummaryExt(id, clock, merged, scale, config.step().toMillis(), + new StepBucketHistogram(clock, config.step().toMillis(), distributionStatisticConfig, + false, false)); + break; + } + + HistogramGauges.registerWithCommonFormat(summary, this); + + return summary; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/StepDistributionSummaryExt.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/StepDistributionSummaryExt.java new file mode 100644 index 00000000000..519f903a76e --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/StepDistributionSummaryExt.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.metrics.core; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; +import io.micrometer.core.instrument.distribution.Histogram; +import io.micrometer.core.instrument.step.StepDistributionSummary; + +public class StepDistributionSummaryExt extends StepDistributionSummary { + public StepDistributionSummaryExt(Id id, Clock clock, + DistributionStatisticConfig distributionStatisticConfig, double scale, long stepMillis, + Histogram histogram) { + super(id, clock, distributionStatisticConfig, scale, stepMillis, histogram); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/SystemMetrics.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/SystemMetrics.java new file mode 100644 index 00000000000..132412d78b7 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/SystemMetrics.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.metrics.core; + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.OperatingSystemMXBean; +import java.lang.management.ThreadMXBean; + +public class SystemMetrics { + private final OperatingSystemMXBean systemMXBean; + + private final ThreadMXBean threadMXBean; + + private final MemoryMXBean memoryMXBean; + + public SystemMetrics() { + this(ManagementFactory.getOperatingSystemMXBean(), ManagementFactory.getThreadMXBean(), + ManagementFactory.getMemoryMXBean()); + } + + public SystemMetrics(OperatingSystemMXBean systemMXBean, ThreadMXBean threadMXBean, + MemoryMXBean memoryMXBean) { + this.systemMXBean = systemMXBean; + this.threadMXBean = threadMXBean; + this.memoryMXBean = memoryMXBean; + } + + public double getCpuLoad() { + return systemMXBean.getSystemLoadAverage(); + } + + public int getCpuRunningThreads() { + return threadMXBean.getThreadCount(); + } + + public long getHeapInit() { + return memoryMXBean.getHeapMemoryUsage().getInit(); + } + + public long getHeapMax() { + return memoryMXBean.getHeapMemoryUsage().getMax(); + } + + public long getHeapCommit() { + return memoryMXBean.getHeapMemoryUsage().getCommitted(); + } + + public long getHeapUsed() { + return memoryMXBean.getHeapMemoryUsage().getUsed(); + } + + public long getNonHeapInit() { + return memoryMXBean.getNonHeapMemoryUsage().getInit(); + } + + public long getNonHeapMax() { + return memoryMXBean.getNonHeapMemoryUsage().getMax(); + } + + public long getNonHeapCommit() { + return memoryMXBean.getNonHeapMemoryUsage().getCommitted(); + } + + public long getNonHeapUsed() { + return memoryMXBean.getNonHeapMemoryUsage().getUsed(); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/ThreadPoolMetersInitializer.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/ThreadPoolMetersInitializer.java new file mode 100644 index 00000000000..9ada122585b --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/ThreadPoolMetersInitializer.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.metrics.core; + +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +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.common.utils.BeanUtils; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.foundation.metrics.MetricsInitializer; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; +import org.apache.servicecomb.metrics.core.meter.pool.ThreadPoolMeter; + +import com.google.common.eventbus.EventBus; + +import io.micrometer.core.instrument.MeterRegistry; + +public class ThreadPoolMetersInitializer implements MetricsInitializer, PeriodMeter { + private MeterRegistry meterRegistry; + + private Map threadPoolMeters = new HashMap<>(); + + @Override + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + this.meterRegistry = meterRegistry; + + createThreadPoolMeters(); + } + + public void createThreadPoolMeters() { + Map operationExecutors = collectionOperationExecutors(); + // currently, all operation executors come from bean + Map beanExecutors = BeanUtils.getContext().getBeansOfType(Executor.class); + + for (Entry entry : beanExecutors.entrySet()) { + Executor executor = entry.getValue(); + if (!operationExecutors.containsKey(executor)) { + continue; + } + + if (executor instanceof GroupExecutor) { + createThreadPoolMeters(entry.getKey(), (GroupExecutor) executor); + continue; + } + + createThreadPoolMeters(entry.getKey(), executor); + } + } + + protected Map collectionOperationExecutors() { + Map operationExecutors = new IdentityHashMap<>(); + //only one instance in the values + MicroserviceMeta microserviceMeta = SCBEngine.getInstance().getProducerMicroserviceMeta(); + for (OperationMeta operationMeta : microserviceMeta.getOperations()) { + operationExecutors.put(operationMeta.getExecutor(), operationMeta.getExecutor()); + } + return operationExecutors; + } + + protected void createThreadPoolMeters(String threadPoolName, GroupExecutor groupExecutor) { + for (int idx = 0; idx < groupExecutor.getExecutorList().size(); idx++) { + Executor executor = groupExecutor.getExecutorList().get(idx); + createThreadPoolMeters(threadPoolName + "-group" + idx, executor); + } + } + + public void createThreadPoolMeters(String threadPoolName, Executor executor) { + if (!(executor instanceof ThreadPoolExecutor threadPoolExecutor)) { + return; + } + + threadPoolMeters.computeIfAbsent(threadPoolName, + (key) -> new ThreadPoolMeter(meterRegistry, threadPoolName, threadPoolExecutor)); + } + + @Override + public void poll(long msNow, long secondInterval) { + threadPoolMeters.forEach((key, value) -> value.poll(msNow, secondInterval)); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/VertxMetersInitializer.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/VertxMetersInitializer.java new file mode 100644 index 00000000000..8240fabd4f2 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/VertxMetersInitializer.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core; + +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.foundation.metrics.MetricsInitializer; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; +import org.apache.servicecomb.foundation.vertx.SharedVertxFactory; +import org.apache.servicecomb.metrics.core.meter.vertx.HttpClientEndpointsMeter; +import org.apache.servicecomb.metrics.core.meter.vertx.ServerEndpointsMeter; + +import com.google.common.eventbus.EventBus; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; + +public class VertxMetersInitializer implements MetricsInitializer, PeriodMeter { + public static final String VERTX_ENDPOINTS = "servicecomb.vertx.endpoints"; + + public static final String ENDPOINTS_TYPE = "type"; + + public static final String ENDPOINTS_CLIENT = "client"; + + public static final String ENDPOINTS_SERVER = "server"; + + private HttpClientEndpointsMeter httpClientEndpointsMeter; + + private ServerEndpointsMeter serverEndpointsMeter; + + @Override + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + httpClientEndpointsMeter = new HttpClientEndpointsMeter(meterRegistry, VERTX_ENDPOINTS, + Tags.of(ENDPOINTS_TYPE, ENDPOINTS_CLIENT), + SharedVertxFactory.getMetricsFactory(config.getEnvironment()) + .getVertxMetrics() + .getClientEndpointMetricManager()); + + serverEndpointsMeter = new ServerEndpointsMeter(meterRegistry, VERTX_ENDPOINTS, + Tags.of(ENDPOINTS_TYPE, ENDPOINTS_SERVER), + SharedVertxFactory.getMetricsFactory(config.getEnvironment()) + .getVertxMetrics() + .getServerEndpointMetricMap()); + } + + @Override + public void poll(long msNow, long secondInterval) { + httpClientEndpointsMeter.poll(msNow, secondInterval); + serverEndpointsMeter.poll(msNow, secondInterval); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ConsumerMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ConsumerMeters.java new file mode 100644 index 00000000000..ccd7f49f6a3 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ConsumerMeters.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.metrics.core.meter; + +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters; +import org.apache.servicecomb.metrics.core.meter.invocation.ConsumerInvocationMeters; + +import io.micrometer.core.instrument.MeterRegistry; + +public class ConsumerMeters { + private final AbstractInvocationMeters invocationMeters; + + public ConsumerMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { + invocationMeters = new ConsumerInvocationMeters(meterRegistry, metricsBootstrapConfig); + } + + public AbstractInvocationMeters getInvocationMeters() { + return invocationMeters; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/EdgeMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/EdgeMeters.java new file mode 100644 index 00000000000..b81403e89a3 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/EdgeMeters.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.metrics.core.meter; + +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters; +import org.apache.servicecomb.metrics.core.meter.invocation.EdgeInvocationMeters; + +import io.micrometer.core.instrument.MeterRegistry; + +public class EdgeMeters { + private final AbstractInvocationMeters invocationMeters; + + + public EdgeMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { + this.invocationMeters = new EdgeInvocationMeters(meterRegistry, metricsBootstrapConfig); + } + + public AbstractInvocationMeters getInvocationMeters() { + return invocationMeters; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ProducerMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ProducerMeters.java new file mode 100644 index 00000000000..1822b940f06 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ProducerMeters.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.metrics.core.meter; + +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters; +import org.apache.servicecomb.metrics.core.meter.invocation.ProducerInvocationMeters; + +import io.micrometer.core.instrument.MeterRegistry; + +public class ProducerMeters { + private final AbstractInvocationMeters invocationMeters; + + public ProducerMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { + invocationMeters = new ProducerInvocationMeters(meterRegistry, metricsBootstrapConfig); + } + + public AbstractInvocationMeters getInvocationMeters() { + return invocationMeters; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ThreadPoolMonitorPublishModelFactory.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ThreadPoolMonitorPublishModelFactory.java new file mode 100644 index 00000000000..d0474142cca --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ThreadPoolMonitorPublishModelFactory.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.metrics.core.meter; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; +import org.apache.servicecomb.metrics.core.meter.pool.ThreadPoolMeter; +import org.apache.servicecomb.metrics.core.publish.model.ThreadPoolPublishModel; + +import io.micrometer.core.instrument.Measurement; + +public class ThreadPoolMonitorPublishModelFactory { + interface Setter { + void set(ThreadPoolPublishModel model, Measurement measurement); + } + + public static Map create(MeasurementTree tree) { + Map result = new HashMap<>(8); + readMeasurement(result, tree, ThreadPoolMeter.TASK_COUNT, + (model, measurement) -> model.setAvgTaskCount(measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.COMPLETED_TASK_COUNT, + (model, measurement) -> model.setAvgCompletedTaskCount(measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.CURRENT_THREADS_BUSY, + (model, measurement) -> model.setCurrentThreadsBusy((int) measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.MAX_THREADS, + (model, measurement) -> model.setMaxThreads((int) measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.POOL_SIZE, + (model, measurement) -> model.setPoolSize((int) measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.CORE_POOL_SIZE, + (model, measurement) -> model.setCorePoolSize((int) measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.QUEUE_SIZE, + (model, measurement) -> model.setQueueSize((int) measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.REJECTED_COUNT, + (model, measurement) -> model.setRejected(measurement.getValue())); + return result; + } + + protected static void readMeasurement(Map threadPools, MeasurementTree tree, + String name, Setter setter) { + MeasurementNode node = tree.findChild(ThreadPoolMeter.THREAD_POOL_METER); + if (node == null) { + return; + } + + for (String threadPoolName : node.getChildren().keySet()) { + MeasurementNode measure = node.findChild(threadPoolName, name); + if (measure == null) { + continue; + } + ThreadPoolPublishModel model = threadPools.computeIfAbsent(threadPoolName, tpn -> new ThreadPoolPublishModel()); + setter.set(model, measure.getMeasurements().get(0)); + } + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java new file mode 100644 index 00000000000..dbaf875ba81 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.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.metrics.core.meter.invocation; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.foundation.metrics.meter.LatencyDistributionConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; + +public abstract class AbstractInvocationMeter { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractInvocationMeter.class); + + // total time distribution + private final DistributionSummary totalSummary; + + //total time + private final Timer totalTimer; + + // prepare time + private final Timer prepareTimer; + + protected final MetricsBootstrapConfig metricsBootstrapConfig; + + public AbstractInvocationMeter(MeterRegistry meterRegistry, String name, Tags tags, + MetricsBootstrapConfig metricsBootstrapConfig) { + this.metricsBootstrapConfig = metricsBootstrapConfig; + + double[] sla = toSla(metricsBootstrapConfig.getLatencyDistribution()); + if (sla != null) { + totalSummary = DistributionSummary.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_DISTRIBUTION)) + .distributionStatisticExpiry(Duration.ofMillis(metricsBootstrapConfig.getMsPollInterval())) + .serviceLevelObjectives(sla).register(meterRegistry); + } else { + totalSummary = null; + } + this.totalTimer = Timer.builder(name).tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE + , MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_TOTAL)).register(meterRegistry); + this.prepareTimer = Timer.builder(name).tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE + , MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PREPARE)).register(meterRegistry); + } + + private static double[] toSla(String config) { + if (StringUtils.isEmpty(config)) { + return null; + } + config = config.trim() + "," + LatencyDistributionConfig.MAX_LATENCY; + String[] array = config.split("\\s*,+\\s*"); + double[] result = new double[array.length]; + + for (int idx = 0; idx < array.length - 1; idx++) { + long msMin = Long.parseLong(array[idx]); + long msMax = Long.parseLong(array[idx + 1]); + if (msMin >= msMax) { + LOGGER.error("invalid latency scope, min={}, max={}.", array[idx], array[idx + 1]); + return null; + } + + result[idx] = msMin; + } + result[array.length - 1] = LatencyDistributionConfig.MAX_LATENCY; + + if (Double.compare(0, result[0]) == 0) { + double[] target = new double[result.length - 1]; + System.arraycopy(result, 1, target, 0, target.length); + return target; + } + + return result; + } + + public void onInvocationFinish(InvocationFinishEvent event) { + InvocationStageTrace stageTrace = event.getInvocation().getInvocationStageTrace(); + totalTimer.record(stageTrace.calcTotal(), TimeUnit.NANOSECONDS); + prepareTimer.record(stageTrace.calcPrepare(), TimeUnit.NANOSECONDS); + if (totalSummary != null) { + totalSummary.record(TimeUnit.NANOSECONDS.toMillis(stageTrace.calcTotal())); + } + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeters.java new file mode 100644 index 00000000000..30e8e2987ca --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeters.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.metrics.core.meter.invocation; + +import java.util.Map; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.InvocationStartEvent; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.swagger.invocation.Response; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; + +public abstract class AbstractInvocationMeters { + protected MeterRegistry registry; + + private final Map metersMap = new ConcurrentHashMapEx<>(); + + // not care for concurrency, just for make build key faster + private int maxKeyLen = 64; + + protected MetricsBootstrapConfig metricsBootstrapConfig; + + public AbstractInvocationMeters(MeterRegistry registry, MetricsBootstrapConfig metricsBootstrapConfig) { + this.registry = registry; + this.metricsBootstrapConfig = metricsBootstrapConfig; + } + + protected AbstractInvocationMeter getOrCreateMeters(Invocation invocation, Response response) { + // build string key is faster than use Id to locate timer directly + StringBuilder keyBuilder = new StringBuilder(maxKeyLen); + String invocationName = invocation.getInvocationType().name(); + + keyBuilder + .append(invocationName) + .append(invocation.getRealTransportName()) + .append(invocation.getMicroserviceQualifiedName()) + .append(response.getStatusCode()); + if (keyBuilder.length() > maxKeyLen) { + maxKeyLen = keyBuilder.length(); + } + + return metersMap.computeIfAbsent(keyBuilder.toString(), k -> { + AbstractInvocationMeter meter = createMeter(MeterInvocationConst.INVOCATION_NAME, Tags.empty() + .and(MeterInvocationConst.TAG_ROLE, invocationName) + .and(MeterInvocationConst.TAG_TRANSPORT, invocation.getRealTransportName()) + .and(MeterInvocationConst.TAG_OPERATION, invocation.getMicroserviceQualifiedName()) + .and(MeterInvocationConst.TAG_STATUS, String.valueOf(response.getStatusCode()))); + return meter; + }); + } + + protected abstract AbstractInvocationMeter createMeter(String name, Tags tags); + + public void onInvocationStart(InvocationStartEvent event) { + } + + public void onInvocationFinish(InvocationFinishEvent event) { + AbstractInvocationMeter meters = getOrCreateMeters(event.getInvocation(), event.getResponse()); + meters.onInvocationFinish(event); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeter.java new file mode 100644 index 00000000000..d0b4c08f75d --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeter.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.metrics.core.meter.invocation; + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; + +public class ConsumerInvocationMeter extends AbstractInvocationMeter { + private final Timer consumerEncodeRequestTimer; + + private final Timer consumerDecodeResponseTimer; + + private final Timer consumerGetConnectionTimer; + + private final Timer consumerSendRequestTimer; + + private final Timer consumerWaitResponseTimer; + + public ConsumerInvocationMeter(MeterRegistry meterRegistry, String name, Tags tags, + MetricsBootstrapConfig metricsBootstrapConfig) { + super(meterRegistry, name, tags, metricsBootstrapConfig); + consumerSendRequestTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_SEND)).register(meterRegistry); + consumerGetConnectionTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_CONNECTION)).register(meterRegistry); + consumerEncodeRequestTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST)) + .register(meterRegistry); + consumerDecodeResponseTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE)) + .register(meterRegistry); + consumerWaitResponseTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_WAIT)).register(meterRegistry); + } + + @Override + public void onInvocationFinish(InvocationFinishEvent event) { + super.onInvocationFinish(event); + + InvocationStageTrace invocationStageTrace = event.getInvocation().getInvocationStageTrace(); + consumerEncodeRequestTimer.record(invocationStageTrace.calcConsumerEncodeRequest(), TimeUnit.NANOSECONDS); + consumerSendRequestTimer.record(invocationStageTrace.calcConsumerSendRequest(), TimeUnit.NANOSECONDS); + consumerGetConnectionTimer.record(invocationStageTrace.calcConnection(), TimeUnit.NANOSECONDS); + consumerWaitResponseTimer.record(invocationStageTrace.calcWait(), TimeUnit.NANOSECONDS); + consumerDecodeResponseTimer.record(invocationStageTrace.calcConsumerDecodeResponse(), TimeUnit.NANOSECONDS); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeters.java new file mode 100644 index 00000000000..842122e08db --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeters.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.metrics.core.meter.invocation; + +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; + +public class ConsumerInvocationMeters extends AbstractInvocationMeters { + public ConsumerInvocationMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { + super(meterRegistry, metricsBootstrapConfig); + } + + @Override + protected AbstractInvocationMeter createMeter(String name, Tags tags) { + return new ConsumerInvocationMeter(registry, name, tags, this.metricsBootstrapConfig); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeter.java new file mode 100644 index 00000000000..32fe7aa926a --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeter.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.metrics.core.meter.invocation; + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; + +public class EdgeInvocationMeter extends ConsumerInvocationMeter { + private final Timer providerDecodeRequestTimer; + + private final Timer providerEncodeResponseTimer; + + private final Timer sendResponseTimer; + + public EdgeInvocationMeter(MeterRegistry meterRegistry, String name, Tags tags, + MetricsBootstrapConfig metricsBootstrapConfig) { + super(meterRegistry, name, tags, metricsBootstrapConfig); + providerDecodeRequestTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST)) + .register(meterRegistry); + providerEncodeResponseTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE)) + .register(meterRegistry); + sendResponseTimer = Timer.builder(name).tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_SEND)) + .register(meterRegistry); + } + + @Override + public void onInvocationFinish(InvocationFinishEvent event) { + super.onInvocationFinish(event); + InvocationStageTrace invocationStageTrace = event.getInvocation().getInvocationStageTrace(); + + providerDecodeRequestTimer.record(invocationStageTrace.calcProviderDecodeRequest(), TimeUnit.NANOSECONDS); + providerEncodeResponseTimer.record(invocationStageTrace.calcProviderEncodeResponse(), TimeUnit.NANOSECONDS); + sendResponseTimer.record(invocationStageTrace.calcProviderSendResponse(), TimeUnit.NANOSECONDS); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeters.java new file mode 100644 index 00000000000..ecdbd3670b0 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeters.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.metrics.core.meter.invocation; + +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; + +public class EdgeInvocationMeters extends ConsumerInvocationMeters { + public EdgeInvocationMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { + super(meterRegistry, metricsBootstrapConfig); + } + + @Override + protected AbstractInvocationMeter createMeter(String name, Tags tags) { + return new EdgeInvocationMeter(registry, name, tags, metricsBootstrapConfig); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java new file mode 100644 index 00000000000..2a377e43260 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.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.metrics.core.meter.invocation; + +public interface MeterInvocationConst { + String INVOCATION_NAME = "servicecomb.invocation"; + + // consumer or producer + String TAG_ROLE = "role"; + + String TAG_OPERATION = "operation"; + + String TAG_TRANSPORT = "transport"; + + String TAG_TYPE = "type"; + + String TAG_STAGE = "stage"; + + String TAG_DISTRIBUTION = "distribution"; + + String TAG_STATUS = "status"; + + String EDGE_INVOCATION_NAME = "EDGE"; +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeter.java new file mode 100644 index 00000000000..2ed55e5e0ef --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeter.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.metrics.core.meter.invocation; + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; + +public class ProducerInvocationMeter extends AbstractInvocationMeter { + private final Timer executorQueueTimer; + + private final Timer executionTimer; + + private final Timer providerDecodeRequestTimer; + + private final Timer providerEncodeResponseTimer; + + private final Timer sendResponseTimer; + + public ProducerInvocationMeter(MeterRegistry meterRegistry, String name, Tags tags, + MetricsBootstrapConfig metricsBootstrapConfig) { + super(meterRegistry, name, tags, metricsBootstrapConfig); + executorQueueTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_QUEUE)) + .register(meterRegistry); + executionTimer = Timer.builder(name).tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_BUSINESS)) + .register(meterRegistry); + providerDecodeRequestTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST)) + .register(meterRegistry); + providerEncodeResponseTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE)) + .register(meterRegistry); + sendResponseTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_SEND)) + .register(meterRegistry); + } + + @Override + public void onInvocationFinish(InvocationFinishEvent event) { + super.onInvocationFinish(event); + + InvocationStageTrace invocationStageTrace = event.getInvocation().getInvocationStageTrace(); + executorQueueTimer.record(invocationStageTrace.calcQueue(), TimeUnit.NANOSECONDS); + executionTimer.record(invocationStageTrace.calcBusinessExecute(), TimeUnit.NANOSECONDS); + providerDecodeRequestTimer.record(invocationStageTrace.calcProviderDecodeRequest(), TimeUnit.NANOSECONDS); + providerEncodeResponseTimer.record(invocationStageTrace.calcProviderEncodeResponse(), TimeUnit.NANOSECONDS); + sendResponseTimer.record(invocationStageTrace.calcProviderSendResponse(), TimeUnit.NANOSECONDS); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeters.java new file mode 100644 index 00000000000..bf4f84104f9 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeters.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.metrics.core.meter.invocation; + +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; + +public class ProducerInvocationMeters extends AbstractInvocationMeters { + public ProducerInvocationMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { + super(meterRegistry, metricsBootstrapConfig); + } + + @Override + protected AbstractInvocationMeter createMeter(String name, Tags tags) { + return new ProducerInvocationMeter(registry, name, tags, metricsBootstrapConfig); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/NetMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/NetMeter.java new file mode 100644 index 00000000000..882fb6bf5de --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/NetMeter.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.meter.os; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; +import org.apache.servicecomb.metrics.core.meter.os.net.InterfaceUsage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; + +public class NetMeter implements PeriodMeter { + private static final Logger LOGGER = LoggerFactory.getLogger(NetMeter.class); + + public static final String STATISTIC = "statistic"; + + public static final String INTERFACE = "interface"; + + public static final Tag TAG_RECEIVE = Tag.of(STATISTIC, "receive"); + + public static final Tag TAG_PACKETS_RECEIVE = Tag.of(STATISTIC, "receivePackets"); + + public static final Tag TAG_SEND = Tag.of(STATISTIC, "send"); + + public static final Tag TAG_PACKETS_SEND = Tag.of(STATISTIC, "sendPackets"); + + private final Map interfaceUsageMap = new ConcurrentHashMap<>(); + + private boolean isOsLinux = SystemUtils.IS_OS_LINUX; + + protected final MeterRegistry meterRegistry; + + protected final String name; + + protected final Tags tags; + + public NetMeter(MeterRegistry meterRegistry, String name, Tags tags) { + this.meterRegistry = meterRegistry; + this.name = name; + this.tags = tags; + poll(0, 0); + } + + @VisibleForTesting + public void setOsLinux(boolean osLinux) { + isOsLinux = osLinux; + } + + @Override + public void poll(long msNow, long secondInterval) { + if (isOsLinux) { + refreshNet(secondInterval); + } + } + + /* + * Inter-| Receive | Transmit + * face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed + * eth0: 2615248100 32148518 0 0 0 0 0 0 87333034794 21420267 0 0 0 0 0 0 + * 0 1 2 3 4 5 6 7 8 9 + */ + protected void refreshNet(long secondInterval) { + try { + File file = new File("/proc/net/dev"); + List netInfo = FileUtils.readLines(file, StandardCharsets.UTF_8); + + //the first two lines is useless + for (int i = 2; i < netInfo.size(); i++) { + String interfaceData = netInfo.get(i); + String[] strings = interfaceData.split(":"); + if (strings.length != 2) { + LOGGER.warn(" there is something wrong with {} ", interfaceData); + continue; + } + + String interfaceName = strings[0].trim(); + + InterfaceUsage interfaceUsage = interfaceUsageMap.computeIfAbsent(interfaceName, + key -> new InterfaceUsage(meterRegistry, name, tags, key)); + interfaceUsage.update(strings[1], secondInterval); + } + } catch (IOException e) { + LOGGER.error("Failed to read net info/", e); + } + } + + public Map getInterfaceUsageMap() { + return interfaceUsageMap; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/OsMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/OsMeter.java new file mode 100644 index 00000000000..9b74fd199de --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/OsMeter.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.metrics.core.meter.os; + +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; + +import com.google.common.annotations.VisibleForTesting; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; + +/** + * name=os type=cpu value = 0 + * name=os type=processCpu value = 0 + * name=os type=net interface=eth0 statistic=send value=100 + * name=os type=net interface=eth0 statistic=receive value=100 + * name=os type=net interface=eth0 statistic=sendPackets value=100 + * name=os type=net interface=eth0 statistic=receivePackets value=100 + */ +public class OsMeter implements PeriodMeter { + public static final String OS_NAME = "os"; + + public static final String OS_TYPE = "type"; + + public static final String OS_TYPE_NET = "net"; + + private final SystemMeter systemMeter; + + private final NetMeter netMeter; + + public OsMeter(MeterRegistry meterRegistry) { + systemMeter = new SystemMeter(meterRegistry, OS_NAME); + netMeter = new NetMeter(meterRegistry, OS_NAME, Tags.of(OS_TYPE, OS_TYPE_NET)); + } + + @Override + public void poll(long msNow, long secondInterval) { + systemMeter.poll(msNow, secondInterval); + netMeter.poll(msNow, secondInterval); + } + + @VisibleForTesting + public SystemMeter getCpuMeter() { + return systemMeter; + } + + @VisibleForTesting + public NetMeter getNetMeter() { + return netMeter; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/SystemMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/SystemMeter.java new file mode 100644 index 00000000000..2b9ce7d471e --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/SystemMeter.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.metrics.core.meter.os; + +import java.lang.management.ManagementFactory; + +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; + +import com.google.common.annotations.VisibleForTesting; +import com.sun.management.OperatingSystemMXBean; + +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; + +public class SystemMeter implements PeriodMeter { + public static final String SYSTEM_LOAD_AVERAGE = "sla"; + + public static final String CPU_USAGE = "cpu"; + + public static final String PROCESS_CPU_USAGE = "processCpu"; + + public static final String MEMORY_USAGE = "memory"; + + private OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class); + + public SystemMeter(MeterRegistry meterRegistry, String name) { + Gauge.builder(name, () -> getOsBean().getSystemLoadAverage()) + .tags(Tags.of(OsMeter.OS_TYPE, SYSTEM_LOAD_AVERAGE)) + .register(meterRegistry); + + Gauge.builder(name, () -> getOsBean().getCpuLoad()) + .tags(Tags.of(OsMeter.OS_TYPE, CPU_USAGE)) + .register(meterRegistry); + + Gauge.builder(name, () -> getOsBean().getProcessCpuLoad()) + .tags(Tags.of(OsMeter.OS_TYPE, PROCESS_CPU_USAGE)) + .register(meterRegistry); + + Gauge.builder(name, () -> (getOsBean().getTotalMemorySize() - getOsBean().getFreeMemorySize()) + / (double) getOsBean().getTotalMemorySize()) + .tags(Tags.of(OsMeter.OS_TYPE, MEMORY_USAGE)) + .register(meterRegistry); + } + + @VisibleForTesting + public void setOsBean(OperatingSystemMXBean mock) { + this.osBean = mock; + } + + private OperatingSystemMXBean getOsBean() { + return this.osBean; + } + + @Override + public void poll(long msNow, long secondInterval) { + + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/InterfaceUsage.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/InterfaceUsage.java new file mode 100644 index 00000000000..b3d40a1bbf5 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/InterfaceUsage.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.metrics.core.meter.os.net; + +import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.INTERFACE; +import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.TAG_PACKETS_RECEIVE; +import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.TAG_PACKETS_SEND; +import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.TAG_RECEIVE; +import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.TAG_SEND; + +import com.google.common.annotations.VisibleForTesting; + +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; + +public class InterfaceUsage { + private final String interfaceName; + + private final NetStat receive; + + private final NetStat send; + + private final NetStat packetsReceive; + + private final NetStat packetsSend; + + public InterfaceUsage(MeterRegistry meterRegistry, String name, Tags tags, String interfaceName) { + this.interfaceName = interfaceName; + tags = tags.and(Tag.of(INTERFACE, interfaceName)); + + // recv/Bps + receive = new NetStat(0); + Gauge.builder(name, receive::getRate).tags(tags.and(TAG_RECEIVE)).register(meterRegistry); + // send/Bps + send = new NetStat(8); + Gauge.builder(name, send::getRate).tags(tags.and(TAG_SEND)).register(meterRegistry); + // recv/pps + packetsReceive = new NetStat(1); + Gauge.builder(name, packetsReceive::getRate).tags(tags.and(TAG_PACKETS_RECEIVE)).register(meterRegistry); + // send/pps + packetsSend = new NetStat(9); + Gauge.builder(name, packetsSend::getRate).tags(tags.and(TAG_PACKETS_SEND)).register(meterRegistry); + } + + public void update(String interfaceData, long secondInterval) { + String[] netInfo = interfaceData.trim().split("\\s+"); + receive.update(netInfo, secondInterval); + send.update(netInfo, secondInterval); + packetsReceive.update(netInfo, secondInterval); + packetsSend.update(netInfo, secondInterval); + } + + @VisibleForTesting + public NetStat getReceive() { + return receive; + } + + @VisibleForTesting + public NetStat getSend() { + return send; + } + + @VisibleForTesting + public NetStat getPacketsReceive() { + return packetsReceive; + } + + @VisibleForTesting + public NetStat getPacketsSend() { + return packetsSend; + } + + public String getName() { + return interfaceName; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/NetStat.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/NetStat.java new file mode 100644 index 00000000000..fac707dce6d --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/NetStat.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.metrics.core.meter.os.net; + + +public class NetStat { + private final int index; + + // send/recv bytes/packets + private long lastValue; + + // Bps/pps + private double rate; + + public NetStat(int index) { + this.index = index; + } + + public void clearRate() { + rate = 0; + } + + public void update(String[] netInfo, long secondInterval) { + long currentValue = Long.parseLong(netInfo[index]); + rate = (double) (currentValue - lastValue) / secondInterval; + lastValue = currentValue; + } + + public long getLastValue() { + return lastValue; + } + + public double getRate() { + return rate; + } + + public int getIndex() { + return index; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/pool/ThreadPoolMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/pool/ThreadPoolMeter.java new file mode 100644 index 00000000000..1267bc53e6f --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/pool/ThreadPoolMeter.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.metrics.core.meter.pool; + +import java.util.concurrent.ThreadPoolExecutor; + +import org.apache.servicecomb.core.executor.ThreadPoolExecutorEx; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; + +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; + +public class ThreadPoolMeter implements PeriodMeter { + public static final String THREAD_POOL_METER = "servicecomb.threadpool"; + + public static final String ID = "id"; + + public static final String STAGE = "stage"; + + public static final String REJECTED_COUNT = "rejectedCount"; + + public static final String TASK_COUNT = "taskCount"; + + public static final String COMPLETED_TASK_COUNT = "completedTaskCount"; + + public static final String CURRENT_THREADS_BUSY = "currentThreadsBusy"; + + public static final String MAX_THREADS = "maxThreads"; + + public static final String POOL_SIZE = "poolSize"; + + public static final String CORE_POOL_SIZE = "corePoolSize"; + + public static final String QUEUE_SIZE = "queueSize"; + + private final ThreadPoolExecutor threadPoolExecutor; + + private long currentTask; + + private long lastTask; + + private long currentCompletedTask; + + private long lastCompletedTask; + + public ThreadPoolMeter(MeterRegistry meterRegistry, String threadPoolName, ThreadPoolExecutor threadPoolExecutor) { + this.threadPoolExecutor = threadPoolExecutor; + + Gauge.builder(THREAD_POOL_METER, () -> currentTask) + .tags(ID, threadPoolName, STAGE, TASK_COUNT) + .register(meterRegistry); + Gauge.builder(THREAD_POOL_METER, () -> currentCompletedTask) + .tags(ID, threadPoolName, STAGE, COMPLETED_TASK_COUNT) + .register(meterRegistry); + Gauge.builder(THREAD_POOL_METER, threadPoolExecutor::getActiveCount) + .tags(ID, threadPoolName, STAGE, CURRENT_THREADS_BUSY) + .register(meterRegistry); + Gauge.builder(THREAD_POOL_METER, threadPoolExecutor::getMaximumPoolSize) + .tags(ID, threadPoolName, STAGE, MAX_THREADS) + .register(meterRegistry); + Gauge.builder(THREAD_POOL_METER, threadPoolExecutor::getPoolSize) + .tags(ID, threadPoolName, STAGE, POOL_SIZE) + .register(meterRegistry); + Gauge.builder(THREAD_POOL_METER, threadPoolExecutor::getCorePoolSize) + .tags(ID, threadPoolName, STAGE, CORE_POOL_SIZE) + .register(meterRegistry); + Gauge.builder(THREAD_POOL_METER, () -> threadPoolExecutor.getQueue().size()) + .tags(ID, threadPoolName, STAGE, QUEUE_SIZE) + .register(meterRegistry); + + if (threadPoolExecutor instanceof ThreadPoolExecutorEx) { + Gauge.builder(THREAD_POOL_METER, () -> ((ThreadPoolExecutorEx) (threadPoolExecutor)).getRejectedCount()) + .tags(ID, threadPoolName, STAGE, REJECTED_COUNT) + .register(meterRegistry); + } + } + + @Override + public void poll(long msNow, long secondInterval) { + long temp = threadPoolExecutor.getTaskCount(); + currentTask = temp - lastTask; + lastTask = temp; + + temp = threadPoolExecutor.getCompletedTaskCount(); + currentCompletedTask = temp - lastCompletedTask; + lastCompletedTask = temp; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/EndpointMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/EndpointMeter.java new file mode 100644 index 00000000000..e7f960b6d6d --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/EndpointMeter.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.meter.vertx; + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; + +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; + +public class EndpointMeter implements PeriodMeter { + private static final double SNV_MILLI_SECONDS = 1.0 / TimeUnit.MILLISECONDS.toNanos(1L); + + public static final String ADDRESS = "address"; + + public static final String STATISTIC = "statistic"; + + public static final String CONNECT_COUNT = "connectCount"; + + public static final String DISCONNECT_COUNT = "disconnectCount"; + + public static final String CONNECTIONS = "connections"; + + public static final String BYTES_READ = "bytesRead"; + + public static final String BYTES_WRITTEN = "bytesWritten"; + + public static final String REQUESTS = "requests"; + + public static final String LATENCY = "latency"; + + protected DefaultEndpointMetric metric; + + protected final MeterRegistry meterRegistry; + + private final Gauge connectCount; + + private final Gauge disconnectCount; + + private final Gauge connections; + + private final Gauge bytesRead; + + private final Gauge bytesWritten; + + private final Gauge requests; + + private final Gauge latency; + + private long currentConnectCount; + + private long lastConnectCount; + + private long currentDisconnectCount; + + private long lastDisconnectCount; + + private long currentBytesRead; + + private long lastBytesRead; + + private long currentBytesWritten; + + private long lastBytesWritten; + + private long currentRequests; + + private long lastRequests; + + private long currentRequestsForLatency; + + private long lastRequestsForLatency; + + private long currentLatency; + + private long lastLatency; + + public EndpointMeter(MeterRegistry meterRegistry, String name, Tags tags, DefaultEndpointMetric metric) { + this.meterRegistry = meterRegistry; + this.metric = metric; + + tags = tags.and(Tag.of(ADDRESS, metric.getAddress())); + connectCount = Gauge.builder(name, () -> currentConnectCount) + .tags(tags.and(Tag.of(STATISTIC, CONNECT_COUNT))) + .register(meterRegistry); + disconnectCount = Gauge.builder(name, () -> currentDisconnectCount) + .tags(tags.and(Tag.of(STATISTIC, DISCONNECT_COUNT))) + .register(meterRegistry); + connections = Gauge.builder(name, () -> this.metric.getConnectCount() - this.metric.getDisconnectCount()) + .tags(tags.and(Tag.of(STATISTIC, CONNECTIONS))) + .register(meterRegistry); + bytesRead = Gauge.builder(name, () -> currentBytesRead) + .tags(tags.and(Tag.of(STATISTIC, BYTES_READ))) + .register(meterRegistry); + bytesWritten = Gauge.builder(name, () -> currentBytesWritten) + .tags(tags.and(Tag.of(STATISTIC, BYTES_WRITTEN))) + .register(meterRegistry); + requests = Gauge.builder(name, () -> currentRequests) + .tags(tags.and(Tag.of(STATISTIC, REQUESTS))) + .register(meterRegistry); + latency = Gauge.builder(name, () -> currentRequestsForLatency == 0 ? 0 : + (currentLatency) / ((double) (currentRequestsForLatency)) * SNV_MILLI_SECONDS) + .tags(tags.and(Tag.of(STATISTIC, LATENCY))) + .register(meterRegistry); + } + + public DefaultEndpointMetric getMetric() { + return metric; + } + + public void destroy() { + this.meterRegistry.remove(connectCount); + this.meterRegistry.remove(disconnectCount); + this.meterRegistry.remove(connections); + this.meterRegistry.remove(bytesRead); + this.meterRegistry.remove(bytesWritten); + this.meterRegistry.remove(requests); + this.meterRegistry.remove(latency); + } + + @Override + public void poll(long msNow, long secondInterval) { + long temp = metric.getConnectCount(); + currentConnectCount = temp - lastConnectCount; + lastConnectCount = temp; + + temp = metric.getDisconnectCount(); + currentDisconnectCount = temp - lastDisconnectCount; + lastDisconnectCount = temp; + + temp = metric.getBytesRead(); + currentBytesRead = temp - lastBytesRead; + lastBytesRead = temp; + + temp = metric.getBytesWritten(); + currentBytesWritten = temp - lastBytesWritten; + lastBytesWritten = temp; + + temp = metric.getRequests(); + currentRequests = temp - lastRequests; + lastRequests = temp; + + temp = metric.getRequests(); + currentRequestsForLatency = temp - lastRequestsForLatency; + lastRequestsForLatency = temp; + temp = metric.getLatency(); + currentLatency = temp - lastLatency; + lastLatency = temp; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointMeter.java new file mode 100644 index 00000000000..a8e75dc013f --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointMeter.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.metrics.core.meter.vertx; + +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; + +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; + +public class HttpClientEndpointMeter extends EndpointMeter { + public static final String QUEUE_COUNT = "queueCount"; + + private final Gauge queueCount; + + private long currentQueueCount; + + public HttpClientEndpointMeter(MeterRegistry meterRegistry, String name, Tags tags, DefaultEndpointMetric metric) { + super(meterRegistry, name, tags, metric); + queueCount = Gauge.builder(name, () -> currentQueueCount) + .tags(tags.and(Tag.of(STATISTIC, QUEUE_COUNT), Tag.of(ADDRESS, metric.getAddress()))) + .register(meterRegistry); + } + + @Override + public void destroy() { + super.destroy(); + this.meterRegistry.remove(queueCount); + } + + @Override + public void poll(long msNow, long secondInterval) { + super.poll(msNow, secondInterval); + + currentQueueCount = ((DefaultClientEndpointMetric) metric).getQueueCount(); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointsMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointsMeter.java new file mode 100644 index 00000000000..f68f8413cc5 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointsMeter.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.metrics.core.meter.vertx; + +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetricManager; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; + +public class HttpClientEndpointsMeter extends VertxEndpointsMeter { + public HttpClientEndpointsMeter(MeterRegistry meterRegistry, String name, Tags tags, + DefaultClientEndpointMetricManager clientEndpointMetricManager) { + super(meterRegistry, name, tags, clientEndpointMetricManager.getClientEndpointMetricMap()); + } + + @Override + protected EndpointMeter createEndpointMeter(DefaultEndpointMetric metric) { + return new HttpClientEndpointMeter(meterRegistry, name, tags, metric); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointMeter.java new file mode 100644 index 00000000000..bee385ef9b5 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointMeter.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.metrics.core.meter.vertx; + +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; + +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; + +public class ServerEndpointMeter extends EndpointMeter { + public static final String REJECT_BY_CONNECTION_LIMIT = "rejectByConnectionLimit"; + + private final Gauge rejectByConnectionLimit; + + private long lastRejectByConnectionLimit; + + private long currentRejectByConnectionLimit; + + public ServerEndpointMeter(MeterRegistry meterRegistry, String name, Tags tags, DefaultEndpointMetric metric) { + super(meterRegistry, name, tags, metric); + rejectByConnectionLimit = Gauge.builder(name, () -> currentRejectByConnectionLimit) + .tags(tags.and(Tag.of(STATISTIC, REJECT_BY_CONNECTION_LIMIT), Tag.of(ADDRESS, metric.getAddress()))) + .register(meterRegistry); + } + + @Override + public void destroy() { + super.destroy(); + this.meterRegistry.remove(rejectByConnectionLimit); + } + + @Override + public void poll(long msNow, long secondInterval) { + super.poll(msNow, secondInterval); + + long current = ((DefaultServerEndpointMetric) metric).getRejectByConnectionLimitCount(); + currentRejectByConnectionLimit = current - lastRejectByConnectionLimit; + lastRejectByConnectionLimit = current; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointsMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointsMeter.java new file mode 100644 index 00000000000..11f5a706523 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointsMeter.java @@ -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. + */ +package org.apache.servicecomb.metrics.core.meter.vertx; + +import java.util.Map; + +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; + +public class ServerEndpointsMeter extends VertxEndpointsMeter { + public ServerEndpointsMeter(MeterRegistry meterRegistry, String name, Tags tags, + Map endpointMetricMap) { + super(meterRegistry, name, tags, endpointMetricMap); + } + + @Override + protected EndpointMeter createEndpointMeter(DefaultEndpointMetric metric) { + return new ServerEndpointMeter(meterRegistry, name, tags, metric); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/VertxEndpointsMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/VertxEndpointsMeter.java new file mode 100644 index 00000000000..fd4d10f0d41 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/VertxEndpointsMeter.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.meter.vertx; + +import java.util.Map; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; + +public class VertxEndpointsMeter implements PeriodMeter { + private final Map endpointMetricMap; + + protected final MeterRegistry meterRegistry; + + protected final String name; + + protected final Tags tags; + + private final Map endpointMeterMap = new ConcurrentHashMapEx<>(); + + @SuppressWarnings("unchecked") + public VertxEndpointsMeter(MeterRegistry meterRegistry, String name, Tags tags, + Map endpointMetricMap) { + this.meterRegistry = meterRegistry; + this.name = name; + this.tags = tags; + this.endpointMetricMap = (Map) endpointMetricMap; + } + + private void syncMeters(long msNow, long secondInterval) { + for (EndpointMeter meter : endpointMeterMap.values()) { + if (!endpointMetricMap.containsKey(meter.getMetric().getAddress())) { + EndpointMeter removed = endpointMeterMap.remove(meter.getMetric().getAddress()); + removed.destroy(); + } + } + for (DefaultEndpointMetric metric : endpointMetricMap.values()) { + EndpointMeter updated = endpointMeterMap.computeIfAbsent(metric.getAddress(), + address -> createEndpointMeter(metric)); + updated.poll(msNow, secondInterval); + } + } + + protected EndpointMeter createEndpointMeter(DefaultEndpointMetric metric) { + return new EndpointMeter(meterRegistry, name, tags, metric); + } + + @Override + public void poll(long msNow, long secondInterval) { + syncMeters(msNow, secondInterval); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/AbstractMeasurementNodeLogPublisher.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/AbstractMeasurementNodeLogPublisher.java new file mode 100644 index 00000000000..cba8e334ce9 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/AbstractMeasurementNodeLogPublisher.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.metrics.core.publish; + +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; + +public abstract class AbstractMeasurementNodeLogPublisher { + protected StringBuilder sb; + + protected MeasurementNode measurementNode; + + private final boolean exists; + + public AbstractMeasurementNodeLogPublisher(MeasurementTree tree, StringBuilder sb, String... childNames) { + this.sb = sb; + measurementNode = tree.findChild(childNames); + exists = measurementNode != null && !measurementNode.getMeasurements().isEmpty(); + } + + public boolean isExists() { + return exists; + } + + public abstract void print(boolean printDetail); +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ClientEndpointsLogPublisher.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ClientEndpointsLogPublisher.java new file mode 100644 index 00000000000..0669283fc73 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ClientEndpointsLogPublisher.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.metrics.core.publish; + +import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.foundation.common.net.NetUtils; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; +import org.apache.servicecomb.metrics.core.VertxMetersInitializer; +import org.apache.servicecomb.metrics.core.meter.vertx.EndpointMeter; +import org.apache.servicecomb.metrics.core.meter.vertx.HttpClientEndpointMeter; + +public class ClientEndpointsLogPublisher extends AbstractMeasurementNodeLogPublisher { + public ClientEndpointsLogPublisher(MeasurementTree tree, StringBuilder sb, String meterName) { + super(tree, sb, VertxMetersInitializer.VERTX_ENDPOINTS, meterName); + } + + @Override + public void print(boolean printDetail) { + if (!printDetail) { + return; + } + appendLine(sb, " client.endpoints:"); + appendLine(sb, + " connectCount disconnectCount queue connections requests latency send(Bps) receive(Bps) remote"); + + List measurements = new ArrayList<>(measurementNode.getChildren().size()); + measurements.addAll(measurementNode.getChildren().values()); + measurements.sort(MeasurementNode::compareTo); + for (MeasurementNode address : measurements) { + appendLine(sb, " %-12.0f %-15.0f %-13.0f %-11.0f %-8.0f %-7.0f %-9s %-12s %s", + address.findChild(EndpointMeter.CONNECT_COUNT).summary(), + address.findChild(EndpointMeter.DISCONNECT_COUNT).summary(), + address.findChild(HttpClientEndpointMeter.QUEUE_COUNT).summary(), + address.findChild(EndpointMeter.CONNECTIONS).summary(), + address.findChild(EndpointMeter.REQUESTS).summary(), + address.findChild(EndpointMeter.LATENCY).summary(), + NetUtils.humanReadableBytes((long) address.findChild(EndpointMeter.BYTES_WRITTEN).summary()), + NetUtils.humanReadableBytes((long) address.findChild(EndpointMeter.BYTES_READ).summary()), + address.getName() + ); + } + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java new file mode 100644 index 00000000000..73327df1385 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java @@ -0,0 +1,504 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.publish; + +import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.common.net.NetUtils; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.foundation.metrics.MetricsInitializer; +import org.apache.servicecomb.foundation.metrics.PolledEvent; +import org.apache.servicecomb.foundation.metrics.meter.LatencyDistributionConfig; +import org.apache.servicecomb.foundation.metrics.meter.LatencyScopeConfig; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.apache.servicecomb.metrics.core.VertxMetersInitializer; +import org.apache.servicecomb.metrics.core.meter.os.NetMeter; +import org.apache.servicecomb.metrics.core.meter.os.OsMeter; +import org.apache.servicecomb.metrics.core.meter.os.SystemMeter; +import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; +import org.apache.servicecomb.metrics.core.publish.model.ThreadPoolPublishModel; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +import com.google.common.base.Strings; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; +import io.vertx.core.Vertx; + +public class DefaultLogPublisher implements MetricsInitializer { + private static final Logger LOGGER = LoggerFactory.getLogger("scb-metrics"); + + public static final String ENABLED = "servicecomb.metrics.publisher.defaultLog.enabled"; + + // for a client, maybe will connect to too many endpoints, so default not print detail, just print summary + public static final String ENDPOINTS_CLIENT_DETAIL_ENABLED = "servicecomb.metrics.publisher.defaultLog.endpoints.client.detail.enabled"; + + private static final String FIRST_LINE_SIMPLE_FORMAT = " %-11s\n"; + + private static final String SIMPLE_FORMAT = " %-8.1f %-18s %s%s\n"; + + //details + private static final String PRODUCER_DETAILS_FORMAT = "" + + " prepare: %-18s decode-request : %-18s queue : %-18s business-execute: %s\n" + + " encode-response: %-18s send: %-18s\n"; + + private static final String CONSUMER_DETAILS_FORMAT = "" + + " prepare : %-18s connection : %-18s encode-request: %-18s send : %s\n" + + " wait : %-18s decode-response : %-18s\n"; + + private static final String EDGE_DETAILS_FORMAT = "" + + " prepare : %-18s provider-decode : %-18s connection : %-18s consumer-encode : %s\n" + + " consumer-send : %-18s wait : %-18s consumer-decode : %-18s provider-encode : %s\n" + + " provider-send : %-18s\n"; + + private LatencyDistributionConfig latencyDistributionConfig; + + /** + * if config is 0,1,10,100 then header will be:
+ * [0,1) [1,10) [10,100) [100,) + */ + private String latencyDistributionHeader = ""; + + /** + * if config is 0,1,10,100 then format will be:
+ * %-7d %-7d %-9d %-7d + */ + private String latencyDistributionFormat = ""; + + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + if (!environment.getProperty(ENABLED, boolean.class, false)) { + return; + } + + initLatencyDistribution(config); + + eventBus.register(this); + } + + private void initLatencyDistribution(MetricsBootstrapConfig config) { + // default length is 7 which include a space, one minute 999999 requests, TPS is 16666, mostly it's enough + int leastLatencyScopeStrLength = config.getMinScopeLength(); + + latencyDistributionConfig = new LatencyDistributionConfig(config.getLatencyDistribution()); + String header; + for (LatencyScopeConfig scopeConfig : latencyDistributionConfig.getScopeConfigs()) { + if (scopeConfig.getMsMax() == LatencyDistributionConfig.MAX_LATENCY) { + header = String.format("[%d,) ", scopeConfig.getMsMin()); + } else { + header = String.format("[%d,%d) ", scopeConfig.getMsMin(), scopeConfig.getMsMax()); + } + header = Strings.padEnd(header, leastLatencyScopeStrLength, ' '); + latencyDistributionHeader += header; + + String format = "%-" + (header.length() - 1) + "d "; + latencyDistributionFormat += format; + } + } + + @Subscribe + public void onPolledEvent(PolledEvent event) { + try { + printLog(event.getMeters()); + } catch (Throwable e) { + // make development easier + LOGGER.error("Failed to print perf log.", e); + } + } + + protected void printLog(List meters) { + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + PublishModelFactory factory = new PublishModelFactory(meters); + DefaultPublishModel model = factory.createDefaultPublishModel(); + + printOsLog(factory.getTree(), sb); + printVertxMetrics(factory.getTree(), sb); + printThreadPoolMetrics(model, sb); + + printConsumerLog(model, sb); + printProducerLog(model, sb); + printEdgeLog(model, sb); + + LOGGER.info(sb.toString()); + } + + protected void printOsLog(MeasurementTree tree, StringBuilder sb) { + MeasurementNode osNode = tree.findChild(OsMeter.OS_NAME); + if (osNode == null || osNode.getMeasurements().isEmpty()) { + return; + } + + appendLine(sb, "os:"); + printCpuLog(sb, osNode); + printNetLog(sb, osNode); + } + + private void printNetLog(StringBuilder sb, MeasurementNode osNode) { + MeasurementNode netNode = osNode.findChild(OsMeter.OS_TYPE_NET); + if (netNode == null || netNode.getMeasurements().isEmpty()) { + return; + } + + appendLine(sb, " net:"); + appendLine(sb, " send(Bps) recv(Bps) send(pps) recv(pps) interface"); + + StringBuilder tmpSb = new StringBuilder(); + for (MeasurementNode interfaceNode : netNode.getChildren().values()) { + double sendRate = interfaceNode.findChild(NetMeter.TAG_SEND.getValue()).summary(); + double sendPacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_SEND.getValue()).summary(); + double receiveRate = interfaceNode.findChild(NetMeter.TAG_RECEIVE.getValue()).summary(); + double receivePacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_RECEIVE.getValue()).summary(); + if (sendRate == 0 && receiveRate == 0 && receivePacketsRate == 0 && sendPacketsRate == 0) { + continue; + } + appendLine(tmpSb, " %-12s %-12s %-12s %-12s %s", + NetUtils.humanReadableBytes((long) sendRate), + NetUtils.humanReadableBytes((long) receiveRate), + NetUtils.humanReadableBytes((long) sendPacketsRate), + NetUtils.humanReadableBytes((long) receivePacketsRate), + interfaceNode.getName()); + } + if (tmpSb.length() != 0) { + sb.append(tmpSb); + } + } + + private void printCpuLog(StringBuilder sb, MeasurementNode osNode) { + MeasurementNode cpuNode = osNode.findChild(SystemMeter.CPU_USAGE); + MeasurementNode processNode = osNode.findChild(SystemMeter.PROCESS_CPU_USAGE); + MeasurementNode memoryNode = osNode.findChild(SystemMeter.MEMORY_USAGE); + MeasurementNode slaNode = osNode.findChild(SystemMeter.SYSTEM_LOAD_AVERAGE); + appendLine(sb, " cpu:"); + appendLine(sb, " all usage: %.2f%% process usage: %.2f%% sla: %.2f memory usage: %.2f%%", + cpuNode.summary() * 100, processNode.summary() * 100, + slaNode.summary(), memoryNode.summary() * 100); + } + + protected void printThreadPoolMetrics(DefaultPublishModel model, StringBuilder sb) { + if (model.getThreadPools().isEmpty()) { + return; + } + sb.append("threadPool:\n"); + sb.append(" coreSize maxThreads poolSize currentBusy rejected queueSize taskCount taskFinished name\n"); + for (Entry entry : model.getThreadPools().entrySet()) { + ThreadPoolPublishModel threadPoolPublishModel = entry.getValue(); + sb.append(String.format(" %-8d %-10d %-8d %-11d %-8.0f %-9d %-9.1f %-12.1f %s\n", + threadPoolPublishModel.getCorePoolSize(), + threadPoolPublishModel.getMaxThreads(), + threadPoolPublishModel.getPoolSize(), + threadPoolPublishModel.getCurrentThreadsBusy(), + threadPoolPublishModel.getRejected(), + threadPoolPublishModel.getQueueSize(), + threadPoolPublishModel.getAvgTaskCount(), + threadPoolPublishModel.getAvgCompletedTaskCount(), + entry.getKey())); + } + } + + protected void printEdgeLog(DefaultPublishModel model, StringBuilder sb) { + OperationPerfGroups edgePerf = model.getEdge().getOperationPerfGroups(); + if (edgePerf == null) { + return; + } + sb.append("" + + "edge:\n" + + " simple:\n" + + " status requests latency ") + .append(latencyDistributionHeader) + .append("operation\n"); + StringBuilder detailsBuilder = new StringBuilder(); + //print sample + for (Map statusMap : edgePerf.getGroups().values()) { + for (OperationPerfGroup perfGroup : statusMap.values()) { + //append sample + sb.append(printSamplePerf(perfGroup)); + //append details + detailsBuilder.append(printEdgeDetailsPerf(perfGroup)); + } + } + sb.append(" details:\n").append(detailsBuilder); + } + + protected void printConsumerLog(DefaultPublishModel model, StringBuilder sb) { + OperationPerfGroups consumerPerf = model.getConsumer().getOperationPerfGroups(); + if (consumerPerf == null) { + return; + } + sb.append("" + + "consumer:\n" + + " simple:\n" + + " status requests latency ") + .append(latencyDistributionHeader) + .append("operation\n"); + StringBuilder detailsBuilder = new StringBuilder(); + //print sample + for (Map statusMap : consumerPerf.getGroups().values()) { + for (OperationPerfGroup perfGroup : statusMap.values()) { + //append sample + sb.append(printSamplePerf(perfGroup)); + //append details + detailsBuilder.append(printConsumerDetailsPerf(perfGroup)); + } + } + sb.append(" details:\n").append(detailsBuilder); + } + + protected void printProducerLog(DefaultPublishModel model, StringBuilder sb) { + OperationPerfGroups producerPerf = model.getProducer().getOperationPerfGroups(); + if (producerPerf == null) { + return; + } + sb.append("" + + "producer:\n" + + " simple:\n" + + " status requests latency ") + .append(latencyDistributionHeader) + .append("operation\n"); + // use detailsBuilder, we can traverse the map only once + StringBuilder detailsBuilder = new StringBuilder(); + //print sample + for (Map statusMap : producerPerf.getGroups().values()) { + for (OperationPerfGroup perfGroup : statusMap.values()) { + //append sample + sb.append(printSamplePerf(perfGroup)); + //append details + detailsBuilder.append(printProducerDetailsPerf(perfGroup)); + } + } + //print details + sb.append(" details:\n").append(detailsBuilder); + } + + private StringBuilder printSamplePerf(OperationPerfGroup perfGroup) { + StringBuilder sb = new StringBuilder(); + String status = perfGroup.getTransport() + "." + perfGroup.getStatus() + ":"; + sb.append(String.format(FIRST_LINE_SIMPLE_FORMAT, status)); + + for (int i = 0; i < perfGroup.getOperationPerfs().size(); i++) { + OperationPerf operationPerf = perfGroup.getOperationPerfs().get(i); + if (isIgnoreEmptyPerf(operationPerf)) { + continue; + } + PerfInfo stageTotal = operationPerf.findStage(InvocationStageTrace.STAGE_TOTAL); + sb.append(String.format(SIMPLE_FORMAT, stageTotal.getTotalRequests(), + getDetailsFromPerf(stageTotal), + formatLatencyDistribution(operationPerf), + operationPerf.getOperation())); + } + OperationPerf summaryOperation = perfGroup.getSummary(); + PerfInfo stageSummaryTotal = summaryOperation.findStage(InvocationStageTrace.STAGE_TOTAL); + //print summary + sb.append(String.format(SIMPLE_FORMAT, stageSummaryTotal.getTotalRequests(), + getDetailsFromPerf(stageSummaryTotal), + formatLatencyDistribution(summaryOperation), + "(summary)")); + return sb; + } + + private String formatLatencyDistribution(OperationPerf operationPerf) { + return String.format(latencyDistributionFormat, (Object[]) operationPerf.getLatencyDistribution()); + } + + private StringBuilder printProducerDetailsPerf(OperationPerfGroup perfGroup) { + StringBuilder sb = new StringBuilder(); + //append rest."200": + sb.append(" ") + .append(perfGroup.getTransport()) + .append(".") + .append(perfGroup.getStatus()) + .append(":\n"); + PerfInfo prepare, queue, providerDecode, providerEncode, execute, sendResp; + for (OperationPerf operationPerf : perfGroup.getOperationPerfs()) { + if (isIgnoreEmptyPerf(operationPerf)) { + continue; + } + prepare = operationPerf.findStage(InvocationStageTrace.STAGE_PREPARE); + queue = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_QUEUE); + providerDecode = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST); + providerEncode = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE); + execute = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_BUSINESS); + sendResp = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_SEND); + + sb.append(" ") + .append(operationPerf.getOperation()) + .append(":\n") + .append(String.format(PRODUCER_DETAILS_FORMAT, + getDetailsFromPerf(prepare), + getDetailsFromPerf(providerDecode), + getDetailsFromPerf(queue), + getDetailsFromPerf(execute), + getDetailsFromPerf(providerEncode), + getDetailsFromPerf(sendResp) + )); + } + + return sb; + } + + private StringBuilder printConsumerDetailsPerf(OperationPerfGroup perfGroup) { + StringBuilder sb = new StringBuilder(); + //append rest."200": + sb.append(" ") + .append(perfGroup.getTransport()) + .append(".") + .append(perfGroup.getStatus()) + .append(":\n"); + + PerfInfo prepare, encodeRequest, decodeResponse, sendReq, getConnect, + waitResp; + for (OperationPerf operationPerf : perfGroup.getOperationPerfs()) { + if (isIgnoreEmptyPerf(operationPerf)) { + continue; + } + prepare = operationPerf.findStage(InvocationStageTrace.STAGE_PREPARE); + encodeRequest = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST); + decodeResponse = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE); + sendReq = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_SEND); + getConnect = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_CONNECTION); + waitResp = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_WAIT); + + sb.append(" ") + .append(operationPerf.getOperation()) + .append(":\n") + .append(String.format(CONSUMER_DETAILS_FORMAT, + getDetailsFromPerf(prepare), + getDetailsFromPerf(getConnect), + getDetailsFromPerf(encodeRequest), + getDetailsFromPerf(sendReq), + getDetailsFromPerf(waitResp), + getDetailsFromPerf(decodeResponse) + )); + } + + return sb; + } + + private StringBuilder printEdgeDetailsPerf(OperationPerfGroup perfGroup) { + StringBuilder sb = new StringBuilder(); + //append rest."200": + sb.append(" ") + .append(perfGroup.getTransport()) + .append(".") + .append(perfGroup.getStatus()) + .append(":\n"); + + PerfInfo prepare, connection, decodeProviderRequest, encodeProviderResponse, + encodeConsumerRequest, decodeConsumerResponse, sendReq, getConnect, + waitResp, sendResp; + for (OperationPerf operationPerf : perfGroup.getOperationPerfs()) { + if (isIgnoreEmptyPerf(operationPerf)) { + continue; + } + prepare = operationPerf.findStage(InvocationStageTrace.STAGE_PREPARE); + connection = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_CONNECTION); + decodeProviderRequest = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST); + encodeProviderResponse = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE); + encodeConsumerRequest = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST); + sendReq = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_SEND); + decodeConsumerResponse = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE); + waitResp = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_WAIT); + sendResp = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_SEND); + + sb.append(" ") + .append(operationPerf.getOperation()) + .append(":\n") + .append(String.format(EDGE_DETAILS_FORMAT, + getDetailsFromPerf(prepare), + getDetailsFromPerf(decodeProviderRequest), + getDetailsFromPerf(connection), + getDetailsFromPerf(encodeConsumerRequest), + getDetailsFromPerf(sendReq), + getDetailsFromPerf(waitResp), + getDetailsFromPerf(decodeConsumerResponse), + getDetailsFromPerf(encodeProviderResponse), + getDetailsFromPerf(sendResp) + )); + } + + return sb; + } + + private boolean isIgnoreEmptyPerf(OperationPerf operationPerf) { + PerfInfo stageTotal = operationPerf.findStage(InvocationStageTrace.STAGE_TOTAL); + // max latency is calculated in ring algorithm, maybe not 0 + if (Double.compare(0D, stageTotal.getTotalRequests()) == 0 + && Double.compare(0D, stageTotal.getMsMaxLatency()) == 0) { + return true; + } + return false; + } + + protected void printVertxMetrics(MeasurementTree tree, StringBuilder sb) { + appendLine(sb, "vertx:"); + + appendLine(sb, " instances:"); + appendLine(sb, " name eventLoopContext-created"); + for (Entry entry : VertxUtils.getVertxMap().entrySet()) { + appendLine(sb, " %-10s %d", + entry.getKey(), + // TODO will be fixed by next vertx update.entry.getValue().getEventLoopContextCreatedCount() + 0); + } + + ClientEndpointsLogPublisher client = new ClientEndpointsLogPublisher(tree, sb, + VertxMetersInitializer.ENDPOINTS_CLIENT); + ServerEndpointsLogPublisher server = new ServerEndpointsLogPublisher(tree, sb, + VertxMetersInitializer.ENDPOINTS_SERVER); + if (client.isExists() || server.isExists()) { + appendLine(sb, " transport:"); + if (client.isExists()) { + client.print(environment.getProperty(ENDPOINTS_CLIENT_DETAIL_ENABLED, boolean.class, true)); + } + + if (server.isExists()) { + server.print(true); + } + } + } + + private static String getDetailsFromPerf(PerfInfo perfInfo) { + String result = ""; + if (perfInfo != null) { + result = String.format("%.3f/%.3f", perfInfo.calcMsLatency(), perfInfo.getMsMaxLatency()); + } + return result; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java new file mode 100644 index 00000000000..7fb9ad43cda --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.publish; + +import java.util.List; + +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.apache.servicecomb.metrics.core.VertxMetersInitializer; +import org.apache.servicecomb.metrics.core.meter.ThreadPoolMonitorPublishModelFactory; +import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; +import org.apache.servicecomb.metrics.core.meter.os.NetMeter; +import org.apache.servicecomb.metrics.core.meter.os.OsMeter; +import org.apache.servicecomb.metrics.core.meter.pool.ThreadPoolMeter; +import org.apache.servicecomb.metrics.core.meter.vertx.EndpointMeter; +import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; +import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; +import org.apache.servicecomb.swagger.invocation.InvocationType; + +import io.micrometer.core.instrument.Meter; + +public class PublishModelFactory { + private final MeasurementTree tree; + + public PublishModelFactory(List meters) { + tree = createMeasurementTree(meters); + } + + protected MeasurementTree createMeasurementTree(List meters) { + MeasurementGroupConfig groupConfig = createMeasurementGroupConfig(); + + MeasurementTree tree = new MeasurementTree(); + tree.from(meters.iterator(), groupConfig); + return tree; + } + + public MeasurementTree getTree() { + return tree; + } + + protected MeasurementGroupConfig createMeasurementGroupConfig() { + MeasurementGroupConfig groupConfig = new MeasurementGroupConfig(); + groupConfig.addGroup(MeterInvocationConst.INVOCATION_NAME, + MeterInvocationConst.TAG_ROLE, + MeterInvocationConst.TAG_TRANSPORT, + MeterInvocationConst.TAG_OPERATION, + MeterInvocationConst.TAG_STATUS, + MeterInvocationConst.TAG_TYPE, + new DefaultTagFinder(MeterInvocationConst.TAG_STAGE, true)); + + groupConfig.addGroup(OsMeter.OS_NAME, + OsMeter.OS_TYPE, + new DefaultTagFinder(NetMeter.INTERFACE, true), + new DefaultTagFinder(NetMeter.STATISTIC, true)); + + groupConfig.addGroup(VertxMetersInitializer.VERTX_ENDPOINTS, + VertxMetersInitializer.ENDPOINTS_TYPE, + EndpointMeter.ADDRESS, EndpointMeter.STATISTIC); + + groupConfig.addGroup(ThreadPoolMeter.THREAD_POOL_METER, + ThreadPoolMeter.ID, ThreadPoolMeter.STAGE); + + return groupConfig; + } + + protected OperationPerfGroups generateOperationPerfGroups(MeasurementTree tree, String invocationTypeName) { + MeasurementNode node = tree.findChild(MeterInvocationConst.INVOCATION_NAME, invocationTypeName); + if (node == null) { + return null; + } + + OperationPerfGroups groups = new OperationPerfGroups(); + + // group by transport + for (MeasurementNode transportNode : node.getChildren().values()) { + // group by operation + for (MeasurementNode operationNode : transportNode.getChildren().values()) { + // group by status + for (MeasurementNode statusNode : operationNode.getChildren().values()) { + PublishUtils.addOperationPerfGroups(groups, transportNode.getName(), operationNode.getName(), statusNode); + } + } + } + + return groups; + } + + public DefaultPublishModel createDefaultPublishModel() { + DefaultPublishModel model = new DefaultPublishModel(); + + model.getConsumer() + .setOperationPerfGroups(generateOperationPerfGroups(tree, InvocationType.CONSUMER.name())); + + model.getProducer() + .setOperationPerfGroups(generateOperationPerfGroups(tree, InvocationType.PROVIDER.name())); + + model.getEdge() + .setOperationPerfGroups(generateOperationPerfGroups(tree, MeterInvocationConst.EDGE_INVOCATION_NAME)); + + model.setThreadPools(ThreadPoolMonitorPublishModelFactory.create(tree)); + + return model; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java new file mode 100644 index 00000000000..9c761aaa5d4 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.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.metrics.core.publish; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; +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 io.micrometer.core.instrument.Statistic; + +public final class PublishUtils { + private PublishUtils() { + } + + public static PerfInfo createPerfInfo(MeasurementNode stageNode) { + PerfInfo perfInfo = new PerfInfo(); + perfInfo.setTotalRequests(stageNode.findChild(Statistic.COUNT.name()).summary()); + perfInfo.setMsTotalTime(stageNode.findChild(Statistic.TOTAL_TIME.name()).summary() * 1000); + perfInfo.setMsMaxLatency(stageNode.findChild(Statistic.MAX.name()).summary() * 1000); + return perfInfo; + } + + public static OperationPerf createOperationPerf(String operation, MeasurementNode statusNode) { + OperationPerf operationPerf = new OperationPerf(); + + operationPerf.setOperation(operation); + MeasurementNode stageNode = statusNode.findChild(MeterInvocationConst.TAG_STAGE); + stageNode.getChildren().values().forEach(mNode -> { + PerfInfo perfInfo = createPerfInfo(mNode); + operationPerf.getStages().put(mNode.getName(), perfInfo); + }); + + MeasurementNode latencyNode = statusNode.findChild(MeterInvocationConst.TAG_DISTRIBUTION); + if (latencyNode != null && latencyNode.getMeasurements() != null) { + operationPerf.setLatencyDistribution(latencyNode.getMeasurements().stream() + .map(m -> (int) m.getValue()) + .toArray(Integer[]::new)); + } + return operationPerf; + } + + public static void addOperationPerfGroups(OperationPerfGroups operationPerfGroups, String transport, String operation, + MeasurementNode statusNode) { + Map statusMap = operationPerfGroups + .getGroups() + .computeIfAbsent(transport, tn -> new HashMap<>()); + OperationPerfGroup group = statusMap + .computeIfAbsent(statusNode.getName(), status -> new OperationPerfGroup(transport, status)); + OperationPerf operationPerf = createOperationPerf(operation, statusNode); + group.addOperationPerf(operationPerf); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ServerEndpointsLogPublisher.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ServerEndpointsLogPublisher.java new file mode 100644 index 00000000000..115e9660009 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ServerEndpointsLogPublisher.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.metrics.core.publish; + +import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; + +import org.apache.servicecomb.foundation.common.net.NetUtils; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; +import org.apache.servicecomb.metrics.core.VertxMetersInitializer; +import org.apache.servicecomb.metrics.core.meter.vertx.EndpointMeter; +import org.apache.servicecomb.metrics.core.meter.vertx.ServerEndpointMeter; + +public class ServerEndpointsLogPublisher extends AbstractMeasurementNodeLogPublisher { + public ServerEndpointsLogPublisher(MeasurementTree tree, StringBuilder sb, String meterName) { + super(tree, sb, VertxMetersInitializer.VERTX_ENDPOINTS, meterName); + } + + @Override + public void print(boolean printDetail) { + if (!printDetail) { + return; + } + appendLine(sb, " server.endpoints:"); + appendLine(sb, + " connectCount disconnectCount rejectByLimit connections requests latency send(Bps) receive(Bps) listen"); + + for (MeasurementNode address : measurementNode.getChildren().values()) { + if (printDetail) { + appendLine(sb, " %-12.0f %-15.0f %-13.0f %-11.0f %-8.0f %-7.0f %-9s %-12s %s", + address.findChild(EndpointMeter.CONNECT_COUNT).summary(), + address.findChild(EndpointMeter.DISCONNECT_COUNT).summary(), + address.findChild(ServerEndpointMeter.REJECT_BY_CONNECTION_LIMIT).summary(), + address.findChild(EndpointMeter.CONNECTIONS).summary(), + address.findChild(EndpointMeter.REQUESTS).summary(), + address.findChild(EndpointMeter.LATENCY).summary(), + NetUtils.humanReadableBytes((long) address.findChild(EndpointMeter.BYTES_WRITTEN).summary()), + NetUtils.humanReadableBytes((long) address.findChild(EndpointMeter.BYTES_READ).summary()), + address.getName() + ); + } + } + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/SlowInvocationLogger.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/SlowInvocationLogger.java new file mode 100644 index 00000000000..797876aafb6 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/SlowInvocationLogger.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.metrics.core.publish; + +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_CONNECTION; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_SEND; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_WAIT; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PREPARE; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_BUSINESS; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_QUEUE; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_SEND; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_TOTAL; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.definition.OperationConfig; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; +import org.apache.servicecomb.swagger.invocation.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.Subscribe; + +public class SlowInvocationLogger { + private static final Logger LOGGER = LoggerFactory.getLogger("scb-slow"); + + public SlowInvocationLogger(SCBEngine scbEngine) { + scbEngine.getEventBus().register(this); + } + + @Subscribe + @AllowConcurrentEvents + public void onInvocationFinish(InvocationFinishEvent event) { + Invocation invocation = event.getInvocation(); + OperationConfig operationConfig = invocation.getOperationMeta().getConfig(); + if (!operationConfig.isSlowInvocationEnabled() || + invocation.getInvocationStageTrace().calcTotal() < operationConfig.getNanoSlowInvocation()) { + return; + } + + if (invocation.isProducer()) { + logSlowProvider(invocation, event.getResponse(), operationConfig); + return; + } + + if (invocation.isEdge()) { + logSlowEdge(invocation, event.getResponse(), operationConfig); + return; + } + + logSlowConsumer(invocation, event.getResponse(), operationConfig); + } + + private static String collectClientAddress(Invocation invocation) { + HttpServletRequestEx requestEx = invocation.getRequestEx(); + return requestEx == null ? "unknown" : requestEx.getRemoteAddr() + ":" + requestEx.getRemotePort(); + } + + private static String collectTargetAddress(Invocation invocation) { + Endpoint endpoint = invocation.getEndpoint(); + return endpoint == null ? "unknown" : endpoint.getEndpoint(); + } + + private static String formatTime(long doubleNano) { + long micros = TimeUnit.NANOSECONDS.toMicros(doubleNano); + return micros / 1000 + "." + micros % 1000; + } + + private static void logSlowProvider(Invocation invocation, Response response, OperationConfig operationConfig) { + RestOperationMeta restOperationMeta = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); + InvocationStageTrace stageTrace = invocation.getInvocationStageTrace(); + StringBuilder sb = new StringBuilder(); + sb.append("Slow Provider invocation [").append(invocation.getInvocationQualifiedName()) + .append("](").append(operationConfig.getMsSlowInvocation()).append(" ms") + .append(")[").append(invocation.getTraceId()).append("]\n") + .append(formatPair(" ", "http method", restOperationMeta.getHttpMethod())) + .append(formatPair(" ", "url", restOperationMeta.getAbsolutePath())) + .append(formatPair(" ", "endpoint", collectClientAddress(invocation))) + .append(formatPair(" ", "status code", String.valueOf(response.getStatusCode()))) + + .append(formatPair(" ", STAGE_TOTAL, stageTrace.calcTotal())) + .append(formatPair(" ", STAGE_PREPARE, stageTrace.calcPrepare())) + .append(formatPair(" ", STAGE_PROVIDER_DECODE_REQUEST, stageTrace.calcProviderDecodeRequest())) + .append(formatPair(" ", STAGE_PROVIDER_QUEUE, stageTrace.calcQueue())) + .append(formatPair(" ", STAGE_PROVIDER_BUSINESS, stageTrace.calcBusinessExecute())) + .append(formatPair(" ", STAGE_PROVIDER_ENCODE_RESPONSE, stageTrace.calcProviderEncodeResponse())) + .append(formatPair(" ", STAGE_PROVIDER_SEND, stageTrace.calcProviderSendResponse())); + + List sorted = new ArrayList<>(stageTrace.getStages().keySet()); + sorted.stream().sorted().forEach(key -> { + sb.append(formatPair(" ", key, + InvocationStageTrace.calc(stageTrace.getStages().get(key).getEndTime(), + stageTrace.getStages().get(key).getBeginTime()))); + }); + + LOGGER.warn(sb.toString()); + } + + protected static String formatPair(String padding, String name, String value) { + return String.format("%-20s: %20s\n", padding + name, value); + } + + protected static String formatPair(String padding, String name, long time) { + return String.format("%-20s: %8sms\n", padding + name, formatTime(time)); + } + + private static void logSlowConsumer(Invocation invocation, Response response, OperationConfig operationConfig) { + RestOperationMeta restOperationMeta = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); + InvocationStageTrace stageTrace = invocation.getInvocationStageTrace(); + StringBuilder sb = new StringBuilder(); + sb.append("Slow Consumer invocation [").append(invocation.getInvocationQualifiedName()) + .append("](").append(operationConfig.getMsSlowInvocation()).append(" ms") + .append(")[").append(invocation.getTraceId()).append("]\n") + .append(formatPair(" ", "http method", restOperationMeta.getHttpMethod())) + .append(formatPair(" ", "url", restOperationMeta.getAbsolutePath())) + .append(formatPair(" ", "endpoint", collectTargetAddress(invocation))) + .append(formatPair(" ", "status code", String.valueOf(response.getStatusCode()))) + .append(formatPair(" ", STAGE_TOTAL, stageTrace.calcTotal())) + .append(formatPair(" ", STAGE_PREPARE, stageTrace.calcPrepare())) + .append(formatPair(" ", STAGE_CONSUMER_CONNECTION, stageTrace.calcConnection())) + .append(formatPair(" ", STAGE_CONSUMER_ENCODE_REQUEST, stageTrace.calcConsumerEncodeRequest())) + .append(formatPair(" ", STAGE_CONSUMER_SEND, stageTrace.calcConsumerSendRequest())) + .append(formatPair(" ", STAGE_CONSUMER_WAIT, stageTrace.calcWait())) + .append(formatPair(" ", STAGE_CONSUMER_DECODE_RESPONSE, stageTrace.calcConsumerDecodeResponse())); + + List sorted = new ArrayList<>(stageTrace.getStages().keySet()); + sorted.stream().sorted().forEach(key -> { + sb.append(formatPair(" ", key, + InvocationStageTrace.calc(stageTrace.getStages().get(key).getEndTime(), + stageTrace.getStages().get(key).getBeginTime()))); + }); + + LOGGER.warn(sb.toString()); + } + + private static void logSlowEdge(Invocation invocation, Response response, OperationConfig operationConfig) { + RestOperationMeta restOperationMeta = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); + InvocationStageTrace stageTrace = invocation.getInvocationStageTrace(); + StringBuilder sb = new StringBuilder(); + sb.append("Slow Edge invocation [").append(invocation.getInvocationQualifiedName()) + .append("](").append(operationConfig.getMsSlowInvocation()).append(" ms") + .append(")[").append(invocation.getTraceId()).append("]\n") + .append(formatPair(" ", "http method", restOperationMeta.getHttpMethod())) + .append(formatPair(" ", "url", restOperationMeta.getAbsolutePath())) + .append(formatPair(" ", "endpoint", collectTargetAddress(invocation))) + .append(formatPair(" ", "status code", String.valueOf(response.getStatusCode()))) + .append(formatPair(" ", STAGE_TOTAL, stageTrace.calcTotal())) + .append(formatPair(" ", STAGE_PREPARE, stageTrace.calcPrepare())) + .append(formatPair(" ", STAGE_PROVIDER_DECODE_REQUEST, stageTrace.calcProviderDecodeRequest())) + .append(formatPair(" ", STAGE_CONSUMER_CONNECTION, stageTrace.calcConnection())) + .append(formatPair(" ", STAGE_CONSUMER_ENCODE_REQUEST, stageTrace.calcConsumerEncodeRequest())) + .append(formatPair(" ", STAGE_CONSUMER_SEND, stageTrace.calcConsumerSendRequest())) + .append(formatPair(" ", STAGE_CONSUMER_WAIT, stageTrace.calcWait())) + .append(formatPair(" ", STAGE_CONSUMER_DECODE_RESPONSE, stageTrace.calcConsumerDecodeResponse())) + .append(formatPair(" ", STAGE_PROVIDER_ENCODE_RESPONSE, stageTrace.calcProviderEncodeResponse())) + .append(formatPair(" ", STAGE_PROVIDER_SEND, stageTrace.calcProviderSendResponse())) + ; + + List sorted = new ArrayList<>(stageTrace.getStages().keySet()); + sorted.stream().sorted().forEach(key -> { + sb.append(formatPair(" ", key, + InvocationStageTrace.calc(stageTrace.getStages().get(key).getEndTime(), + stageTrace.getStages().get(key).getBeginTime()))); + }); + + LOGGER.warn(sb.toString()); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ConsumerPublishModel.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ConsumerPublishModel.java new file mode 100644 index 00000000000..22e40bdbe1d --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ConsumerPublishModel.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.metrics.core.publish.model; + +import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; + +public class ConsumerPublishModel { + private OperationPerfGroups operationPerfGroups; + + public OperationPerfGroups getOperationPerfGroups() { + return operationPerfGroups; + } + + public void setOperationPerfGroups(OperationPerfGroups operationPerfGroups) { + this.operationPerfGroups = operationPerfGroups; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/DefaultPublishModel.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/DefaultPublishModel.java new file mode 100644 index 00000000000..825158801ff --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/DefaultPublishModel.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.metrics.core.publish.model; + +import java.util.HashMap; +import java.util.Map; + +public class DefaultPublishModel { + private ConsumerPublishModel consumer = new ConsumerPublishModel(); + + private ProducerPublishModel producer = new ProducerPublishModel(); + + private EdgePublishModel edge = new EdgePublishModel(); + + private Map threadPools = new HashMap<>(); + + public ConsumerPublishModel getConsumer() { + return consumer; + } + + public void setConsumer(ConsumerPublishModel consumer) { + this.consumer = consumer; + } + + public ProducerPublishModel getProducer() { + return producer; + } + + public EdgePublishModel getEdge() { + return edge; + } + + public void setEdge(EdgePublishModel edge) { + this.edge = edge; + } + + public void setProducer(ProducerPublishModel producer) { + this.producer = producer; + } + + public Map getThreadPools() { + return threadPools; + } + + public void setThreadPools(Map threadPools) { + this.threadPools = threadPools; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/EdgePublishModel.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/EdgePublishModel.java new file mode 100644 index 00000000000..836223c43fd --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/EdgePublishModel.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.metrics.core.publish.model; + +import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; + +public class EdgePublishModel extends ConsumerPublishModel { + private OperationPerfGroups operationPerfGroups; + + public OperationPerfGroups getOperationPerfGroups() { + return operationPerfGroups; + } + + public void setOperationPerfGroups(OperationPerfGroups operationPerfGroups) { + this.operationPerfGroups = operationPerfGroups; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ProducerPublishModel.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ProducerPublishModel.java new file mode 100644 index 00000000000..d02c98641f5 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ProducerPublishModel.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.metrics.core.publish.model; + +import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; + +public class ProducerPublishModel { + private OperationPerfGroups operationPerfGroups; + + public OperationPerfGroups getOperationPerfGroups() { + return operationPerfGroups; + } + + public void setOperationPerfGroups(OperationPerfGroups operationPerfGroups) { + this.operationPerfGroups = operationPerfGroups; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ThreadPoolPublishModel.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ThreadPoolPublishModel.java new file mode 100644 index 00000000000..eb4aee56dc1 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ThreadPoolPublishModel.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.metrics.core.publish.model; + +public class ThreadPoolPublishModel { + private double avgTaskCount; + + private double avgCompletedTaskCount; + + private int currentThreadsBusy; + + private int maxThreads; + + private int poolSize; + + private int corePoolSize; + + private int queueSize; + + private double rejected = Double.NaN; + + public double getAvgTaskCount() { + return avgTaskCount; + } + + public void setAvgTaskCount(double avgTaskCount) { + this.avgTaskCount = avgTaskCount; + } + + public double getAvgCompletedTaskCount() { + return avgCompletedTaskCount; + } + + public void setAvgCompletedTaskCount(double avgCompletedTaskCount) { + this.avgCompletedTaskCount = avgCompletedTaskCount; + } + + public int getCurrentThreadsBusy() { + return currentThreadsBusy; + } + + public void setCurrentThreadsBusy(int currentThreadsBusy) { + this.currentThreadsBusy = currentThreadsBusy; + } + + public int getMaxThreads() { + return maxThreads; + } + + public void setMaxThreads(int maxThreads) { + this.maxThreads = maxThreads; + } + + public int getPoolSize() { + return poolSize; + } + + public void setPoolSize(int poolSize) { + this.poolSize = poolSize; + } + + public int getCorePoolSize() { + return corePoolSize; + } + + public void setCorePoolSize(int corePoolSize) { + this.corePoolSize = corePoolSize; + } + + public int getQueueSize() { + return queueSize; + } + + public void setQueueSize(int queueSize) { + this.queueSize = queueSize; + } + + public double getRejected() { + return rejected; + } + + public void setRejected(double rejected) { + this.rejected = rejected; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerf.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerf.java new file mode 100644 index 00000000000..28ddd5a5915 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerf.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.publish.model.invocation; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class OperationPerf { + private String operation; + + private Map stages = new HashMap<>(); + + private Integer[] latencyDistribution; + + public String getOperation() { + return operation; + } + + public void setOperation(String operation) { + this.operation = operation; + } + + public Map getStages() { + return stages; + } + + public Integer[] getLatencyDistribution() { + return latencyDistribution; + } + + public void setLatencyDistribution(Integer[] latencyDistribution) { + this.latencyDistribution = latencyDistribution; + } + + public void setStages(Map stages) { + this.stages = stages; + } + + public PerfInfo findStage(String stage) { + return stages.get(stage); + } + + public void add(OperationPerf operationPerf) { + operationPerf.stages.forEach((key, value) -> { + PerfInfo perfInfo = stages.computeIfAbsent(key, n -> new PerfInfo()); + perfInfo.add(value); + }); + + if (operationPerf.getLatencyDistribution() == null) { + return; + } + + if (latencyDistribution == null) { + latencyDistribution = new Integer[operationPerf.getLatencyDistribution().length]; + Arrays.fill(latencyDistribution, 0); + } + for (int idx = 0; idx < operationPerf.getLatencyDistribution().length; idx++) { + latencyDistribution[idx] += operationPerf.getLatencyDistribution()[idx]; + } + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroup.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroup.java new file mode 100644 index 00000000000..eca0baa5a44 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroup.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.publish.model.invocation; + +import java.util.ArrayList; +import java.util.List; + +public class OperationPerfGroup { + private final String transport; + + private final String status; + + private final List operationPerfs = new ArrayList<>(); + + private OperationPerf summary; + + public OperationPerfGroup(String transport, String status) { + this.transport = transport; + this.status = status; + } + + public String getTransport() { + return transport; + } + + public String getStatus() { + return status; + } + + public List getOperationPerfs() { + return operationPerfs; + } + + public OperationPerf getSummary() { + return summary; + } + + public void addOperationPerf(OperationPerf operationPerf) { + operationPerfs.add(operationPerf); + + if (summary == null) { + summary = new OperationPerf(); + summary.setOperation(""); + } + summary.add(operationPerf); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroups.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroups.java new file mode 100644 index 00000000000..addef3fa344 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroups.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.metrics.core.publish.model.invocation; + +import java.util.HashMap; +import java.util.Map; + +public class OperationPerfGroups { + // first key: transport + // second key: statusCode + private Map> groups = new HashMap<>(); + + public Map> getGroups() { + return groups; + } + + public void setGroups(Map> groups) { + this.groups = groups; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/PerfInfo.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/PerfInfo.java new file mode 100644 index 00000000000..e9e3afbee5b --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/PerfInfo.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.metrics.core.publish.model.invocation; + +public class PerfInfo { + private double totalRequests; + + private double msTotalTime; + + private double msMaxLatency; + + public double getTotalRequests() { + return totalRequests; + } + + public void setTotalRequests(double totalRequests) { + this.totalRequests = totalRequests; + } + + public double getMsTotalTime() { + return msTotalTime; + } + + public void setMsTotalTime(double msTotalTime) { + this.msTotalTime = msTotalTime; + } + + public double getMsMaxLatency() { + return msMaxLatency; + } + + public void setMsMaxLatency(double msMaxLatency) { + this.msMaxLatency = msMaxLatency; + } + + public void add(PerfInfo other) { + totalRequests += other.totalRequests; + msTotalTime += other.msTotalTime; + if (msMaxLatency < other.msMaxLatency) { + msMaxLatency = other.msMaxLatency; + } + } + + public double calcMsLatency() { + return (totalRequests != 0) ? msTotalTime / totalRequests : 0; + } + + @Override + public String toString() { + return "PerfInfo [tps=" + totalRequests + ", msTotalTime=" + msTotalTime + ", msLatency=" + calcMsLatency() + + ", msMaxLatency=" + + msMaxLatency + "]"; + } +} diff --git a/metrics/metrics-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/metrics/metrics-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..87677909bd6 --- /dev/null +++ b/metrics/metrics-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.metrics.core.MetricsCoreConfiguration diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/AssertUtil.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/AssertUtil.java new file mode 100644 index 00000000000..f154ba65449 --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/AssertUtil.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.metrics.core; + +import java.util.List; + +import org.junit.jupiter.api.Assertions; + +import io.micrometer.core.instrument.Measurement; + +public class AssertUtil { + + public static void assertMeasure(List measurements, int index, String expected) { + Assertions.assertEquals(String.format("Measurement{%s}", expected), measurements.get(index).toString()); + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestInvocationMetersInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestInvocationMetersInitializer.java new file mode 100644 index 00000000000..316b9bf9b53 --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestInvocationMetersInitializer.java @@ -0,0 +1,412 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core; + +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.List; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; +import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; +import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.apache.servicecomb.swagger.invocation.Response; +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.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.core.env.Environment; + +import com.google.common.eventbus.EventBus; + +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; + +@ExtendWith(MockitoExtension.class) +public class TestInvocationMetersInitializer { + EventBus eventBus = new EventBus(); + + MeterRegistry registry = new SimpleMeterRegistry(); + + InvocationMetersInitializer invocationMetersInitializer = new InvocationMetersInitializer(); + + @Mock + Invocation invocation; + + 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); + invocationMetersInitializer.init(registry, eventBus, new MetricsBootstrapConfig(environment)); + } + + @Test + public void consumerInvocation() { + InvocationFinishEvent event = Mockito.mock(InvocationFinishEvent.class); + Mockito.when(invocation.isConsumer()).thenReturn(true); + Mockito.when(invocation.getInvocationType()).thenReturn(InvocationType.CONSUMER); + Mockito.when(invocation.getRealTransportName()).thenReturn(CoreConst.RESTFUL); + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("m.s.o"); + InvocationStageTrace invocationStageTrace = Mockito.mock(InvocationStageTrace.class); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); + Mockito.when(invocationStageTrace.calcTotal()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcPrepare()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcConnection()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcConsumerEncodeRequest()).thenReturn(4L); + Mockito.when(invocationStageTrace.calcConsumerSendRequest()).thenReturn(5L); + Mockito.when(invocationStageTrace.calcWait()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcConsumerDecodeResponse()).thenReturn(9L); + Mockito.when(event.getInvocation()).thenReturn(invocation); + Response mockResponse = Mockito.spy(Response.class); + Mockito.when(event.getResponse()).thenReturn(mockResponse); + Mockito.doReturn(0).when(mockResponse).getStatusCode(); + + eventBus.post(event); + eventBus.post(event); + + MeasurementTree tree = new MeasurementTree(); + tree.from(registry.getMeters().iterator(), + new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME, "stage")); + List measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "total") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "prepare") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-send") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.0E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=5.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "connection") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-encode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=8.0E-9"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=4.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "connection") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "wait") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-decode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + } + + @Test + public void edgeInvocation() { + InvocationFinishEvent event = Mockito.mock(InvocationFinishEvent.class); + Mockito.when(invocation.getInvocationType()).thenReturn(InvocationType.EDGE); + Mockito.when(invocation.isEdge()).thenReturn(true); + Mockito.when(invocation.getRealTransportName()).thenReturn(CoreConst.RESTFUL); + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("m.s.o"); + InvocationStageTrace invocationStageTrace = Mockito.mock(InvocationStageTrace.class); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); + Mockito.when(invocationStageTrace.calcTotal()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcPrepare()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcProviderDecodeRequest()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcConnection()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcConsumerEncodeRequest()).thenReturn(4L); + Mockito.when(invocationStageTrace.calcConsumerSendRequest()).thenReturn(5L); + Mockito.when(invocationStageTrace.calcConsumerDecodeResponse()).thenReturn(8L); + Mockito.when(invocationStageTrace.calcWait()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcProviderEncodeResponse()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcProviderSendResponse()).thenReturn(9L); + Mockito.when(event.getInvocation()).thenReturn(invocation); + Response mockResponse = Mockito.spy(Response.class); + Mockito.when(event.getResponse()).thenReturn(mockResponse); + Mockito.doReturn(0).when(mockResponse).getStatusCode(); + + eventBus.post(event); + eventBus.post(event); + + MeasurementTree tree = new MeasurementTree(); + tree.from(registry.getMeters().iterator(), + new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME, "stage")); + + List measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "total") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "prepare") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-send") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.0E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=5.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "connection") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-encode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=8.0E-9"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=4.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "wait") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-decode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.6E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=8.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-decode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-encode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-send") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + } + + @Test + public void producerInvocation() { + InvocationFinishEvent event = Mockito.mock(InvocationFinishEvent.class); + Mockito.when(invocation.isConsumer()).thenReturn(false); + Mockito.when(invocation.getInvocationType()).thenReturn(InvocationType.PROVIDER); + Mockito.when(invocation.getRealTransportName()).thenReturn(CoreConst.RESTFUL); + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("m.s.o"); + InvocationStageTrace invocationStageTrace = Mockito.mock(InvocationStageTrace.class); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); + Mockito.when(invocationStageTrace.calcTotal()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcPrepare()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcProviderDecodeRequest()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcQueue()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcBusinessExecute()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcProviderEncodeResponse()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcProviderSendResponse()).thenReturn(9L); + Mockito.when(event.getInvocation()).thenReturn(invocation); + Response mockResponse = Mockito.spy(Response.class); + Mockito.when(event.getResponse()).thenReturn(mockResponse); + Mockito.doReturn(0).when(mockResponse).getStatusCode(); + + eventBus.post(event); + eventBus.post(event); + + MeasurementTree tree = new MeasurementTree(); + tree.from(registry.getMeters().iterator(), + new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME, "stage")); + + List measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "total") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "prepare") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "queue") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "execute") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-decode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-encode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-send") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestOsMeterInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestOsMeterInitializer.java new file mode 100644 index 00000000000..c9461668da1 --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestOsMeterInitializer.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core; + +import java.io.File; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; +import org.apache.servicecomb.metrics.core.meter.os.NetMeter; +import org.apache.servicecomb.metrics.core.meter.os.OsMeter; +import org.apache.servicecomb.metrics.core.meter.os.SystemMeter; +import org.apache.servicecomb.metrics.core.meter.os.net.InterfaceUsage; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import com.google.common.eventbus.EventBus; +import com.sun.management.OperatingSystemMXBean; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class TestOsMeterInitializer { + MeterRegistry registry = new SimpleMeterRegistry(); + + @Mock + EventBus eventBus; + + @Test + public void init() { + List list = new ArrayList<>(); + list.add("13 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1"); + list.add("useless"); + list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); + + try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { + fileUtilsMockedStatic.when(() -> { + FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(list); + OsMetersInitializer osMetersInitializer = new OsMetersInitializer(); + osMetersInitializer.init(registry, eventBus, null); + + OsMeter osMeter = osMetersInitializer.getOsMeter(); + SystemMeter systemMeter = osMeter.getCpuMeter(); + OperatingSystemMXBean osBean = Mockito.mock(OperatingSystemMXBean.class); + Mockito.when(osBean.getCpuLoad()).thenReturn(3.2D); + Mockito.when(osBean.getProcessCpuLoad()).thenReturn(1.2D); + systemMeter.setOsBean(osBean); + NetMeter netMeter = osMeter.getNetMeter(); + netMeter.setOsLinux(true); + + osMetersInitializer.poll(System.currentTimeMillis(), 1000); + + MeasurementTree tree = new MeasurementTree(); + MeasurementGroupConfig group = new MeasurementGroupConfig(); + group.addGroup(OsMeter.OS_NAME, OsMeter.OS_TYPE); + tree.from(registry.getMeters().iterator(), group); + + Assertions.assertEquals(1.2D, + tree.findChild(OsMeter.OS_NAME, SystemMeter.PROCESS_CPU_USAGE).summary(), 0.0); + Assertions.assertEquals(3.2D, + tree.findChild(OsMeter.OS_NAME, SystemMeter.CPU_USAGE).summary(), 0.0); + + Map interfaceInfoMap = netMeter.getInterfaceUsageMap(); + Assertions.assertEquals(1, interfaceInfoMap.size()); + InterfaceUsage eth0 = interfaceInfoMap.get("eth0"); + // recv Bps + Assertions.assertEquals(0L, eth0.getReceive().getLastValue()); + Assertions.assertEquals(0, eth0.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, eth0.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(0L, eth0.getSend().getLastValue()); + Assertions.assertEquals(0, eth0.getSend().getRate(), 0.0); + Assertions.assertEquals(8, eth0.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(0L, eth0.getPacketsReceive().getLastValue()); + Assertions.assertEquals(0, eth0.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); + + // send pps + Assertions.assertEquals(0L, eth0.getPacketsSend().getLastValue()); + Assertions.assertEquals(0, eth0.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); + } + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestThreadPoolMetersInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestThreadPoolMetersInitializer.java new file mode 100644 index 00000000000..9c9698aed2b --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestThreadPoolMetersInitializer.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.metrics.core; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.RunnableScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; + +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.core.executor.ThreadPoolExecutorEx; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +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.apache.servicecomb.metrics.core.meter.pool.ThreadPoolMeter; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; +import org.springframework.context.ApplicationContext; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import mockit.Expectations; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; + +public class TestThreadPoolMetersInitializer { + MeterRegistry registry = new SimpleMeterRegistry(); + + ThreadPoolMetersInitializer threadPoolMetersInitializer = new ThreadPoolMetersInitializer(); + + ThreadPoolExecutorEx threadPoolExecutor = Mockito.mock(ThreadPoolExecutorEx.class); + + @Mocked + BlockingQueue queue; + + @Mocked + GroupExecutor groupExecutor; + + ExecutorService executor = Mockito.mock(ExecutorService.class); + + @Mocked + ApplicationContext applicationContext; + + @Mocked + MicroserviceMeta microserviceMeta; + + @Mocked + OperationMeta operationMetaExecutor; + + @Mocked + OperationMeta operationMetaSameExecutor; + + @Mocked + OperationMeta operationMetaFixedThreadExecutor; + + @Test + public void init() { + new Expectations(SCBEngine.class) { + { + SCBEngine.getInstance().getProducerMicroserviceMeta(); + result = microserviceMeta; + } + }; + Map beanExecutors = new HashMap<>(); + beanExecutors.put("executor", executor); + beanExecutors.put("groupExecutor", groupExecutor); + beanExecutors.put("threadPoolExecutor", threadPoolExecutor); + new Expectations(BeanUtils.class) { + { + BeanUtils.getContext(); + result = applicationContext; + applicationContext.getBeansOfType(Executor.class); + result = beanExecutors; + } + }; + + Mockito.when(threadPoolExecutor.getQueue()).thenReturn(queue); + new Expectations() { + { + microserviceMeta.getOperations(); + result = Arrays.asList(operationMetaExecutor, operationMetaSameExecutor, operationMetaFixedThreadExecutor); + operationMetaExecutor.getExecutor(); + result = executor; + operationMetaSameExecutor.getExecutor(); + result = executor; + operationMetaFixedThreadExecutor.getExecutor(); + result = groupExecutor; + + groupExecutor.getExecutorList(); + result = Arrays.asList(threadPoolExecutor); + + queue.size(); + result = 10d; + } + }; + + new MockUp() { + @Mock + void delayedExecute(RunnableScheduledFuture task) { + + } + }; + + threadPoolMetersInitializer.init(registry, null, null); + + MeasurementTree tree = new MeasurementTree(); + MeasurementGroupConfig group = new MeasurementGroupConfig(); + group.addGroup(ThreadPoolMeter.THREAD_POOL_METER, ThreadPoolMeter.ID, + ThreadPoolMeter.STAGE); + tree.from(registry.getMeters().iterator(), group); + + MeasurementNode node = tree.findChild(ThreadPoolMeter.THREAD_POOL_METER); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "maxThreads").summary(), 0, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "rejectedCount").summary(), 0, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "completedTaskCount").summary(), 0, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "currentThreadsBusy").summary(), 0, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "corePoolSize").summary(), 0, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "poolSize").summary(), 0, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "queueSize").summary(), 10, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "taskCount").summary(), 0, 0); + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java new file mode 100644 index 00000000000..35ba63b3bdc --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java @@ -0,0 +1,216 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core; + +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 static org.apache.servicecomb.metrics.core.publish.DefaultLogPublisher.ENDPOINTS_CLIENT_DETAIL_ENABLED; + +import java.util.List; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.foundation.metrics.PolledEvent; +import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; +import org.apache.servicecomb.foundation.vertx.SharedVertxFactory; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; +import org.apache.servicecomb.metrics.core.publish.DefaultLogPublisher; +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; + +import com.google.common.eventbus.EventBus; + +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.DeploymentOptions; +import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServer; +import io.vertx.core.impl.SysProps; +import io.vertx.ext.web.Router; + +public class TestVertxMetersInitializer { + MeterRegistry registry = new SimpleMeterRegistry(); + + EventBus eventBus = new EventBus(); + + VertxMetersInitializer vertxMetersInitializer = new VertxMetersInitializer(); + + DefaultLogPublisher logPublisher = new DefaultLogPublisher(); + + Environment environment = Mockito.mock(Environment.class); + + LogCollector logCollector = new LogCollector(); + + static HttpClient client; + + static HttpServer server; + + static int port; + + static String body = "body"; + + public static class TestServerVerticle extends AbstractVerticle { + @Override + public void start(Promise startPromise) { + Router mainRouter = Router.router(vertx); + mainRouter.route("/").handler(context -> context.response().end(body)); + + server = vertx.createHttpServer(); + server.requestHandler(mainRouter); + Future future = server.listen(0, "0.0.0.0"); + future.onComplete((s, f) -> { + if (f == null) { + port = s.actualPort(); + startPromise.complete(); + return; + } + + startPromise.fail(f); + }); + } + } + + public static class TestClientVerticle extends AbstractVerticle { + @Override + public void start(Promise startPromise) { + client = vertx.createHttpClient(); + Future future = client.request(HttpMethod.GET, port, "127.0.0.1", "/"); + future.onComplete((s, f) -> { + if (f == null) { + Future responseFuture = s.send(body); + responseFuture.onComplete((rs, rf) -> { + if (rf == null) { + rs.bodyHandler((buffer) -> startPromise.complete()); + } else { + startPromise.fail(f); + } + }); + } + }); + } + } + + @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); + HttpClients.load(); + } + + @AfterEach + public void tearDown() { + logCollector.clear(); + HttpClients.destroy(); + if (client != null) { + client.shutdown(); + } + if (server != null) { + server.shutdown(); + } + } + + @Test + public void init() throws InterruptedException { + 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); + Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(false); + VertxUtils + .blockDeploy(SharedVertxFactory.getSharedVertx(environment), TestServerVerticle.class, new DeploymentOptions()); + VertxUtils + .blockDeploy(SharedVertxFactory.getSharedVertx(environment), TestClientVerticle.class, new DeploymentOptions()); + + vertxMetersInitializer.init(registry, eventBus, new MetricsBootstrapConfig(environment)); + logPublisher.setEnvironment(environment); + logPublisher.init(registry, eventBus, null); + + vertxMetersInitializer.poll(0, 1); + List meters = registry.getMeters(); + + testLog(logCollector, meters, true); + logCollector.clear(); + + testLog(logCollector, meters, false); + } + + private void testLog(LogCollector logCollector, List meters, boolean printDetail) { + Mockito.when(environment.getProperty(ENDPOINTS_CLIENT_DETAIL_ENABLED, boolean.class, true)).thenReturn(printDetail); + logPublisher.onPolledEvent(new PolledEvent(meters)); + + StringBuilder sb = new StringBuilder(); + logCollector.getEvents().forEach(event -> sb.append(event.getMessage()).append("\n")); + String actual = sb.toString(); + int idx = actual.indexOf("vertx:\n"); + actual = actual.substring(idx); + + String clientLatency; + String serverLatency; + + String expect = "vertx:\n" + + " instances:\n" + + " name eventLoopContext-created\n" + + " transport 0\n" + + " transport:\n"; + + int clientLatencyIndex = actual.indexOf("1 0 0 1 1 ") + + "1 0 0 1 1 ".length(); + clientLatency = actual.substring(clientLatencyIndex, actual.indexOf(" ", clientLatencyIndex)); + int serverLatencyIndex = actual.lastIndexOf("1 0 0 1 1 ") + + "1 0 0 1 1 ".length(); + serverLatency = actual.substring(serverLatencyIndex, actual.indexOf(" ", serverLatencyIndex)); + int portSize = String.valueOf(port).length(); + // in new vert.x version, bytes written must be higher than 4K or will be zero + if (printDetail) { + expect = expect + " client.endpoints:\n" + + " connectCount disconnectCount queue connections requests latency send(Bps) receive(Bps) remote\n"; + expect += + " 1 0 0 1 1 %-7s 4 4 http://127.0.0.1:%-" + + portSize + "s\n"; + } + expect += "" + + " server.endpoints:\n" + + " connectCount disconnectCount rejectByLimit connections requests latency send(Bps) receive(Bps) listen\n" + + " 1 0 0 1 1 %-7s 4 4 0.0.0.0:0\n\n"; + + if (printDetail) { + expect = String + .format(expect, clientLatency, port, serverLatency); + } else { + expect = String.format(expect, serverLatency); + } + + Assertions.assertEquals(expect, actual); + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestNetMeter.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestNetMeter.java new file mode 100644 index 00000000000..8ed2a920de7 --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestNetMeter.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.metrics.core.meter.os; + +import static org.apache.servicecomb.metrics.core.meter.os.OsMeter.OS_TYPE; +import static org.apache.servicecomb.metrics.core.meter.os.OsMeter.OS_TYPE_NET; + +import java.io.File; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +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.apache.servicecomb.metrics.core.meter.os.net.InterfaceUsage; +import org.junit.jupiter.api.Assertions; + +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +public class TestNetMeter { + @Test + public void testNetRefreshUnchanged() { + List list = new ArrayList<>(); + list.add("useless"); + list.add("useless"); + list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); + + try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { + fileUtilsMockedStatic.when(() -> { + FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(list); + + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + NetMeter netMeter = new NetMeter(meterRegistry, "os", Tags.of(OS_TYPE, OS_TYPE_NET)); + list.remove(2); + list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); + netMeter.refreshNet(1); + + Map meterInterfaceInfoMap = netMeter.getInterfaceUsageMap(); + Assertions.assertTrue(meterInterfaceInfoMap.containsKey("eth0")); + + InterfaceUsage eth0 = meterInterfaceInfoMap.get("eth0"); + + Assertions.assertEquals("eth0", eth0.getName()); + + // recv Bps + Assertions.assertEquals(1L, eth0.getReceive().getLastValue()); + Assertions.assertEquals(1, eth0.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, eth0.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(1L, eth0.getSend().getLastValue()); + Assertions.assertEquals(1, eth0.getSend().getRate(), 0.0); + Assertions.assertEquals(8, eth0.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(1L, eth0.getPacketsReceive().getLastValue()); + Assertions.assertEquals(1, eth0.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); + + // send pps + Assertions.assertEquals(1L, eth0.getPacketsSend().getLastValue()); + Assertions.assertEquals(1, eth0.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); + + // measurement tree test + MeasurementGroupConfig group = new MeasurementGroupConfig(); + group.addGroup(OsMeter.OS_NAME, + OS_TYPE, + new DefaultTagFinder(NetMeter.INTERFACE, true), + new DefaultTagFinder(NetMeter.STATISTIC, true)); + + MeasurementTree tree = new MeasurementTree(); + tree.from(meterRegistry.getMeters().iterator(), group); + MeasurementNode osNode = tree.findChild(OsMeter.OS_NAME); + MeasurementNode netNode = osNode.findChild(OS_TYPE_NET); + Assertions.assertEquals(1, netNode.getChildren().size()); + for (MeasurementNode interfaceNode : netNode.getChildren().values()) { + double sendRate = interfaceNode.findChild(NetMeter.TAG_SEND.getValue()).summary(); + double sendPacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_SEND.getValue()).summary(); + double receiveRate = interfaceNode.findChild(NetMeter.TAG_RECEIVE.getValue()).summary(); + double receivePacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_RECEIVE.getValue()).summary(); + Assertions.assertEquals(1, sendRate, 0); + Assertions.assertEquals(1, sendPacketsRate, 0); + Assertions.assertEquals(1, receiveRate, 0); + Assertions.assertEquals(1, receivePacketsRate, 0); + } + } + } + + + @Test + public void testNetRefreshAdd() { + List list = new ArrayList<>(); + list.add("useless"); + list.add("useless"); + list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); + try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { + fileUtilsMockedStatic.when(() -> { + FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(list); + + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + NetMeter netMeter = new NetMeter(meterRegistry, "net", Tags.empty()); + netMeter.setOsLinux(true); + netMeter.poll(0, 0); + Map netMap = netMeter.getInterfaceUsageMap(); + Assertions.assertEquals(1, netMap.size()); + list.remove(2); + list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); + list.add("lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); + netMeter.refreshNet(1); + Assertions.assertEquals(2, netMap.size()); + InterfaceUsage eth0 = netMap.get("eth0"); + Assertions.assertEquals("eth0", eth0.getName()); + + // recv Bps + Assertions.assertEquals(1L, eth0.getReceive().getLastValue()); + Assertions.assertEquals(1, eth0.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, eth0.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(1L, eth0.getSend().getLastValue()); + Assertions.assertEquals(1, eth0.getSend().getRate(), 0.0); + Assertions.assertEquals(8, eth0.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(1L, eth0.getPacketsReceive().getLastValue()); + Assertions.assertEquals(1, eth0.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); + + // send pps + Assertions.assertEquals(1L, eth0.getPacketsSend().getLastValue()); + Assertions.assertEquals(1, eth0.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); + + InterfaceUsage lo = netMap.get("lo"); + Assertions.assertEquals("lo", lo.getName()); + + // recv Bps + Assertions.assertEquals(0L, lo.getReceive().getLastValue()); + Assertions.assertEquals(0, lo.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, lo.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(0L, lo.getSend().getLastValue()); + Assertions.assertEquals(0, lo.getSend().getRate(), 0.0); + Assertions.assertEquals(8, lo.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(0L, lo.getPacketsReceive().getLastValue()); + Assertions.assertEquals(0, lo.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, lo.getPacketsReceive().getIndex()); + + // send pps + Assertions.assertEquals(0L, lo.getPacketsSend().getLastValue()); + Assertions.assertEquals(0, lo.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, lo.getPacketsSend().getIndex()); + } + } + + + @Test + public void testNetRefreshRemove() { + List list = new ArrayList<>(); + list.add("useless"); + list.add("useless"); + list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); + list.add("lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); + try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { + fileUtilsMockedStatic.when(() -> { + FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(list); + + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + NetMeter netMeter = new NetMeter(meterRegistry, "net", Tags.empty()); + netMeter.setOsLinux(true); + netMeter.poll(0, 1); + + Map netMap = netMeter.getInterfaceUsageMap(); + Assertions.assertEquals(2, netMap.size()); + InterfaceUsage lo = netMap.get("lo"); + InterfaceUsage eth0 = netMap.get("eth0"); + Assertions.assertEquals("lo", lo.getName()); + // recv Bps + Assertions.assertEquals(0L, lo.getReceive().getLastValue()); + Assertions.assertEquals(0, lo.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, lo.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(0L, lo.getSend().getLastValue()); + Assertions.assertEquals(0, lo.getSend().getRate(), 0.0); + Assertions.assertEquals(8, lo.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(0L, lo.getPacketsReceive().getLastValue()); + Assertions.assertEquals(0, lo.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, lo.getPacketsReceive().getIndex()); + + // send pps + Assertions.assertEquals(0L, lo.getPacketsSend().getLastValue()); + Assertions.assertEquals(0, lo.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, lo.getPacketsSend().getIndex()); + + Assertions.assertEquals("eth0", eth0.getName()); + // recv Bps + Assertions.assertEquals(0L, eth0.getReceive().getLastValue()); + Assertions.assertEquals(0, eth0.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, eth0.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(0L, eth0.getSend().getLastValue()); + Assertions.assertEquals(0, eth0.getSend().getRate(), 0.0); + Assertions.assertEquals(8, eth0.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(0L, eth0.getPacketsReceive().getLastValue()); + Assertions.assertEquals(0, eth0.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); + + // send pps + Assertions.assertEquals(0L, eth0.getPacketsSend().getLastValue()); + Assertions.assertEquals(0, eth0.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); + list.remove(2); + list.remove(2); + list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); + netMeter.refreshNet(1); + + Assertions.assertEquals("eth0", eth0.getName()); + + // recv Bps + Assertions.assertEquals(1L, eth0.getReceive().getLastValue()); + Assertions.assertEquals(1, eth0.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, eth0.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(1L, eth0.getSend().getLastValue()); + Assertions.assertEquals(1, eth0.getSend().getRate(), 0.0); + Assertions.assertEquals(8, eth0.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(1L, eth0.getPacketsReceive().getLastValue()); + Assertions.assertEquals(1, eth0.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); + + // send pps + Assertions.assertEquals(1L, eth0.getPacketsSend().getLastValue()); + Assertions.assertEquals(1, eth0.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); + } + + } + + @Test + public void testCalcMeasurements() { + List list = new ArrayList<>(); + list.add("useless"); + list.add("useless"); + list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { + fileUtilsMockedStatic.when(() -> { + FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(list); + + NetMeter netMeter = new NetMeter(meterRegistry, "net", Tags.empty()); + netMeter.setOsLinux(true); + list.remove(2); + list.add("eth0: 3 1 0 0 0 0 0 1 3 1 1 0 0 0 0 0"); + netMeter.poll(0, 1); + MeasurementTree tree = new MeasurementTree(); + tree.from(meterRegistry.getMeters().iterator(), + new MeasurementGroupConfig("net", "statistic")); + + Measurement receive = tree.findChild("net", "receive").getMeasurements().get(0); + Measurement send = tree.findChild("net", "send").getMeasurements().get(0); + Measurement receivePackets = tree.findChild("net", "receivePackets").getMeasurements().get(0); + Measurement sendPackets = tree.findChild("net", "sendPackets").getMeasurements().get(0); + Assertions.assertEquals(3.0, send.getValue(), 0.0); + Assertions.assertEquals(1.0, sendPackets.getValue(), 0.0); + Assertions.assertEquals(3.0, receive.getValue(), 0.0); + Assertions.assertEquals(1.0, receivePackets.getValue(), 0.0); + } + + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestSystemMeter.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestSystemMeter.java new file mode 100644 index 00000000000..05d32250dbf --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestSystemMeter.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.metrics.core.meter.os; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import com.google.common.io.CharSource; +import com.google.common.io.Files; +import com.sun.management.OperatingSystemMXBean; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; + +public class TestSystemMeter { + @Test + public void testCalcMeasurement() throws IOException { + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + CharSource charSource = Mockito.mock(CharSource.class); + List list = new ArrayList<>(); + list.add("useless"); + list.add("useless"); + list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); + + try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class); + MockedStatic filesMockedStatic = Mockito.mockStatic(Files.class)) { + fileUtilsMockedStatic.when(() -> { + FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(list); + filesMockedStatic.when(() -> { + Files.asCharSource(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(charSource); + + OsMeter osMeter = new OsMeter(meterRegistry); + list.clear(); + list.add("useless"); + list.add("useless"); + list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); + + SystemMeter systemMeter = osMeter.getCpuMeter(); + OperatingSystemMXBean osBean = Mockito.mock(OperatingSystemMXBean.class); + Mockito.when(osBean.getSystemLoadAverage()).thenReturn(0.775); + Mockito.when(osBean.getCpuLoad()).thenReturn(0.875); + Mockito.when(osBean.getProcessCpuLoad()).thenReturn(0.5); + Mockito.when(osBean.getTotalMemorySize()).thenReturn(1000000000L); + Mockito.when(osBean.getFreeMemorySize()).thenReturn(300000000L); + systemMeter.setOsBean(osBean); + NetMeter netMeter = osMeter.getNetMeter(); + netMeter.setOsLinux(true); + + osMeter.poll(0, 1); + + MeasurementTree tree = new MeasurementTree(); + tree.from(meterRegistry.getMeters().iterator(), + new MeasurementGroupConfig("os", "type")); + + Assertions.assertEquals(0.875, tree.findChild("os", "cpu").getMeasurements().get(0).getValue(), 0.0); + Assertions.assertEquals(0.5, tree.findChild("os", "processCpu").getMeasurements().get(0).getValue(), 0.0); + Assertions.assertEquals(0.7, tree.findChild("os", "memory").getMeasurements().get(0).getValue(), 0.0); + Assertions.assertEquals(0.775, tree.findChild("os", "sla").getMeasurements().get(0).getValue(), 0.0); + Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(0).getValue(), 0.0); + Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(1).getValue(), 0.0); + Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(2).getValue(), 0.0); + Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(3).getValue(), 0.0); + } + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java new file mode 100644 index 00000000000..a1867955a60 --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java @@ -0,0 +1,312 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.publish; + +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.Collections; +import java.util.HashMap; +import java.util.List; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.common.Holder; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.foundation.metrics.PolledEvent; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; +import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.apache.servicecomb.metrics.core.meter.os.OsMeter; +import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; +import org.apache.servicecomb.metrics.core.publish.model.ThreadPoolPublishModel; +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.junit.After; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.runners.MethodSorters; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import com.google.common.eventbus.EventBus; + +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.MeterRegistry; +import io.micrometer.core.instrument.Statistic; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import io.vertx.core.impl.VertxImpl; +import jakarta.ws.rs.core.Response.Status; +import mockit.Expectations; +import mockit.Injectable; +import mockit.Mock; +import mockit.MockUp; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestDefaultLogPublisher { + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + + EventBus eventBus = new EventBus(); + + DefaultLogPublisher publisher = new DefaultLogPublisher(); + + LogCollector collector = new LogCollector(); + + Environment environment = Mockito.mock(Environment.class); + + @Before + 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); + publisher.setEnvironment(environment); + } + + @After + public void tearDown() { + collector.tearDown(); + } + + @Test + public void init_enabled_default() { + Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(false); + Holder registered = new Holder<>(false); + new MockUp(eventBus) { + @Mock + void register(Object object) { + registered.value = true; + } + }; + + publisher.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment)); + Assertions.assertFalse(registered.value); + } + + @Test + public void init_enabled_true() { + Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(true); + Holder registered = new Holder<>(); + new MockUp(eventBus) { + @Mock + void register(Object object) { + registered.value = true; + } + }; + + publisher.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment)); + Assertions.assertTrue(registered.value); + } + + @Test + public void init_enabled_false() { + Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(false); + Holder registered = new Holder<>(); + new MockUp(eventBus) { + @Mock + void register(Object object) { + registered.value = true; + } + }; + + publisher.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment)); + Assertions.assertNull(registered.value); + } + + @Test + public void onPolledEvent_failed() { + publisher.onPolledEvent(null); + + LogEvent event = collector.getEvents().get(0); + Assertions.assertEquals("Failed to print perf log.", event.getMessage().getFormattedMessage()); + Assertions.assertEquals(NullPointerException.class, event.getThrown().getClass()); + } + + @Test + public void onPolledEvent(@Injectable VertxImpl vertxImpl, @Injectable MeasurementTree tree, + @Injectable EventBus eventBus) { + MetricsBootstrapConfig config = Mockito.mock(MetricsBootstrapConfig.class); + try { + Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(true); + Mockito.when(config.getLatencyDistribution()).thenReturn("0,1,100"); + publisher.init(meterRegistry, eventBus, config); + new Expectations(VertxUtils.class) { + { + VertxUtils.getVertxMap(); + result = Collections.singletonMap("v", vertxImpl); + } + }; + DefaultPublishModel model = new DefaultPublishModel(); + PerfInfo perfTotal = new PerfInfo(); + perfTotal.setTotalRequests(10_0000); + perfTotal.setMsTotalTime(30000L * 1_0000); + perfTotal.setMsMaxLatency(30000); + OperationPerf operationPerf = new OperationPerf(); + operationPerf.setOperation("op"); + operationPerf.setLatencyDistribution(new Integer[] {12, 120, 1200}); + operationPerf.getStages().put(InvocationStageTrace.STAGE_TOTAL, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_QUEUE, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_BUSINESS, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_PREPARE, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_WAIT, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_SEND, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_SEND, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_CONNECTION, perfTotal); + + OperationPerfGroup operationPerfGroup = new OperationPerfGroup(CoreConst.RESTFUL, Status.OK.name()); + operationPerfGroup.addOperationPerf(operationPerf); + + OperationPerfGroups operationPerfGroups = new OperationPerfGroups(); + operationPerfGroups.getGroups().put(operationPerfGroup.getTransport(), + Collections.singletonMap(operationPerfGroup.getStatus(), operationPerfGroup)); + model.getConsumer().setOperationPerfGroups(operationPerfGroups); + model.getProducer().setOperationPerfGroups(operationPerfGroups); + model.getEdge().setOperationPerfGroups(operationPerfGroups); + + model.getThreadPools().put("test", new ThreadPoolPublishModel()); + Id id = new Id("test", Tags.empty(), null, null, Type.OTHER); + Measurement measurement = new Measurement(() -> 1.0, Statistic.VALUE); + + MeasurementNode measurementNodeCpuAll = new MeasurementNode("allProcess", id, new HashMap<>()); + MeasurementNode measurementNodeCpuProcess = new MeasurementNode("currentProcess", id, new HashMap<>()); + MeasurementNode measurementNodeSla = new MeasurementNode("sla", id, new HashMap<>()); + MeasurementNode measurementNodeMemory = new MeasurementNode("memory", id, new HashMap<>()); + MeasurementNode measurementNodeSend = new MeasurementNode("send", id, new HashMap<>()); + MeasurementNode measurementNodeSendPacket = new MeasurementNode("sendPackets", id, new HashMap<>()); + MeasurementNode measurementNodeRecv = new MeasurementNode("receive", id, new HashMap<>()); + MeasurementNode measurementNodeRecvPacket = new MeasurementNode("receivePackets", id, new HashMap<>()); + MeasurementNode measurementNodeEth0 = new MeasurementNode("eth0", id, new HashMap<>()); + MeasurementNode measurementNodeNet = new MeasurementNode("net", id, new HashMap<>()); + MeasurementNode measurementNodeOs = new MeasurementNode("os", id, new HashMap<>()); + + measurementNodeSend.getMeasurements().add(measurement); + measurementNodeRecv.getMeasurements().add(measurement); + measurementNodeCpuAll.getMeasurements().add(measurement); + measurementNodeCpuProcess.getMeasurements().add(measurement); + measurementNodeSla.getMeasurements().add(measurement); + measurementNodeMemory.getMeasurements().add(measurement); + measurementNodeRecvPacket.getMeasurements().add(measurement); + measurementNodeSendPacket.getMeasurements().add(measurement); + + measurementNodeEth0.getChildren().put("send", measurementNodeSend); + measurementNodeEth0.getChildren().put("receive", measurementNodeRecv); + measurementNodeEth0.getChildren().put("receivePackets", measurementNodeRecvPacket); + measurementNodeEth0.getChildren().put("sendPackets", measurementNodeSendPacket); + + measurementNodeNet.getChildren().put("eth0", measurementNodeEth0); + measurementNodeOs.getChildren().put("cpu", measurementNodeCpuAll); + measurementNodeOs.getChildren().put("processCpu", measurementNodeCpuProcess); + measurementNodeOs.getChildren().put("sla", measurementNodeSla); + measurementNodeOs.getChildren().put("memory", measurementNodeMemory); + measurementNodeOs.getChildren().put("net", measurementNodeNet); + + measurementNodeOs.getMeasurements().add(measurement); + measurementNodeNet.getMeasurements().add(measurement); + measurementNodeEth0.getMeasurements().add(measurement); + + new MockUp() { + @Mock + DefaultPublishModel createDefaultPublishModel() { + return model; + } + + @Mock + MeasurementTree getTree() { + return tree; + } + }; + new Expectations() { + { + tree.findChild(OsMeter.OS_NAME); + result = measurementNodeOs; + } + }; + publisher.onPolledEvent(new PolledEvent(Collections.emptyList())); + List events = collector.getEvents().stream() + .filter(e -> "scb-metrics".equals(e.getLoggerName())).toList(); + LogEvent event = events.get(0); + Assertions.assertEquals(""" + os: + cpu: + all usage: 100.00% process usage: 100.00% sla: 1.00 memory usage: 100.00% + net: + send(Bps) recv(Bps) send(pps) recv(pps) interface + 1 1 1 1 eth0 + vertx: + instances: + name eventLoopContext-created + v 0 + threadPool: + coreSize maxThreads poolSize currentBusy rejected queueSize taskCount taskFinished name + 0 0 0 0 NaN 0 0.0 0.0 test + consumer: + simple: + status requests latency [0,1) [1,100) [100,) operation + rest.OK: \s + 100000.0 3000.000/30000.000 12 120 1200 op + 100000.0 3000.000/30000.000 12 120 1200 (summary) + details: + rest.OK: + op: + prepare : 3000.000/30000.000 connection : 3000.000/30000.000 encode-request: 3000.000/30000.000 send : 3000.000/30000.000 + wait : 3000.000/30000.000 decode-response : 3000.000/30000.000 + producer: + simple: + status requests latency [0,1) [1,100) [100,) operation + rest.OK: \s + 100000.0 3000.000/30000.000 12 120 1200 op + 100000.0 3000.000/30000.000 12 120 1200 (summary) + details: + rest.OK: + op: + prepare: 3000.000/30000.000 decode-request : 3000.000/30000.000 queue : 3000.000/30000.000 business-execute: 3000.000/30000.000 + encode-response: 3000.000/30000.000 send: 3000.000/30000.000 + edge: + simple: + status requests latency [0,1) [1,100) [100,) operation + rest.OK: \s + 100000.0 3000.000/30000.000 12 120 1200 op + 100000.0 3000.000/30000.000 12 120 1200 (summary) + details: + rest.OK: + op: + prepare : 3000.000/30000.000 provider-decode : 3000.000/30000.000 connection : 3000.000/30000.000 consumer-encode : 3000.000/30000.000 + consumer-send : 3000.000/30000.000 wait : 3000.000/30000.000 consumer-decode : 3000.000/30000.000 provider-encode : 3000.000/30000.000 + provider-send : 3000.000/30000.000 + """.trim(), + event.getMessage().getFormattedMessage().trim()); + } catch (Exception e) { + e.printStackTrace(); + Assertions.fail("unexpected error happen. " + e.getMessage()); + } + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestInvocationPublishModelFactory.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestInvocationPublishModelFactory.java new file mode 100644 index 00000000000..483aa6ab75d --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestInvocationPublishModelFactory.java @@ -0,0 +1,302 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.publish; + +import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.CONFIG_LATENCY_DISTRIBUTION; +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 org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.metrics.core.InvocationMetersInitializer; +import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; +import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.apache.servicecomb.swagger.invocation.Response; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import com.google.common.eventbus.EventBus; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import io.vertx.core.json.Json; + +import org.skyscreamer.jsonassert.JSONAssert; + +public class TestInvocationPublishModelFactory { + EventBus eventBus = new EventBus(); + + // not step mode. + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + + InvocationMetersInitializer invocationMetersInitializer = new InvocationMetersInitializer(); + + Invocation invocation = Mockito.mock(Invocation.class); + + InvocationStageTrace invocationStageTrace = Mockito.mock(InvocationStageTrace.class); + + Response response = Mockito.mock(Response.class); + + InvocationType invocationType; + + Environment environment = Mockito.mock(Environment.class); + + @Test + public void createDefaultPublishModel() throws Exception { + 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); + Mockito.when(environment.getProperty(CONFIG_LATENCY_DISTRIBUTION, String.class)).thenReturn("0,1,100"); + + invocationMetersInitializer.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment)); + prepareInvocation(); + + PublishModelFactory factory = new PublishModelFactory(meterRegistry.getMeters()); + DefaultPublishModel model = factory.createDefaultPublishModel(); + + String expect = """ + { + "operationPerfGroups" : { + "groups" : { + "rest" : { + "200" : { + "transport" : "rest", + "status" : "200", + "operationPerfs" : [ { + "operation" : "m.s.o", + "stages" : { + "consumer-encode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "prepare" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "wait" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "total" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.4E-5, + "msMaxLatency" : 1.4E-5 + }, + "consumer-send" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "connection" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "consumer-decode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + } + }, + "latencyDistribution" : [ 1, 1, 1 ] + } ], + "summary" : { + "operation" : "", + "stages" : { + "consumer-encode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "prepare" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "total" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.4E-5, + "msMaxLatency" : 1.4E-5 + }, + "wait" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "consumer-send" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "connection" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "consumer-decode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + } + }, + "latencyDistribution" : [ 1, 1, 1 ] + } + } + } + } + } + } + """; + JSONAssert.assertEquals(Json.encodePrettily(Json.decodeValue(expect, Object.class)), + Json.encodePrettily(model.getConsumer()), false); + + expect = """ + { + "operationPerfGroups" : { + "groups" : { + "rest" : { + "200" : { + "transport" : "rest", + "status" : "200", + "operationPerfs" : [ { + "operation" : "m.s.o", + "stages" : { + "consumer-encode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "prepare" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "wait" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "total" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.4E-5, + "msMaxLatency" : 1.4E-5 + }, + "consumer-send" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "connection" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "consumer-decode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + } + }, + "latencyDistribution" : [ 1, 1, 1 ] + } ], + "summary" : { + "operation" : "", + "stages" : { + "consumer-encode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "prepare" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "total" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.4E-5, + "msMaxLatency" : 1.4E-5 + }, + "wait" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "consumer-send" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "connection" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "consumer-decode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + } + }, + "latencyDistribution" : [ 1, 1, 1 ] + } + } + } + } + } + } + """; + JSONAssert.assertEquals(Json.encodePrettily(Json.decodeValue(expect, Object.class)), + Json.encodePrettily(model.getProducer()), false); + } + + protected void prepareInvocation() { + Mockito.when(invocationStageTrace.calcTotal()).thenReturn(14L); + Mockito.when(invocationStageTrace.calcPrepare()).thenReturn(1L); + Mockito.when(invocationStageTrace.calcConnection()).thenReturn(1L); + Mockito.when(invocationStageTrace.calcConsumerEncodeRequest()).thenReturn(1L); + Mockito.when(invocationStageTrace.calcConsumerSendRequest()).thenReturn(1L); + Mockito.when(invocationStageTrace.calcWait()).thenReturn(1L); + Mockito.when(invocationStageTrace.calcConsumerDecodeResponse()).thenReturn(1L); + + invocationType = InvocationType.CONSUMER; + + Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); + Mockito.when(invocation.isConsumer()).thenReturn(InvocationType.CONSUMER.equals(invocationType)); + Mockito.when(invocation.getRealTransportName()).thenReturn(CoreConst.RESTFUL); + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("m.s.o"); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); + Mockito.when(response.getStatusCode()).thenReturn(200); + + InvocationFinishEvent finishEvent = new InvocationFinishEvent(invocation, response); + eventBus.post(finishEvent); + + invocationType = InvocationType.PROVIDER; + Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); + eventBus.post(finishEvent); + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java new file mode 100644 index 00000000000..dc8b1ec72d7 --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.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.metrics.core.publish; + +import java.util.Map; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; +import org.apache.servicecomb.metrics.core.meter.ThreadPoolMonitorPublishModelFactory; +import org.apache.servicecomb.metrics.core.publish.model.ThreadPoolPublishModel; +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.metrics.core.publish.model.invocation.Utils; +import org.junit.jupiter.api.Assertions; + +import jakarta.ws.rs.core.Response.Status; +import org.junit.jupiter.api.Test; + +public class TestPublishUtils { + String op = "op"; + + @Test + public void createPerfInfo() { + MeasurementNode stageNode = Utils.createStageNode(InvocationStageTrace.STAGE_TOTAL, 10, 10, 100); + + PerfInfo perf = PublishUtils.createPerfInfo(stageNode); + + Assertions.assertEquals(10, perf.getTotalRequests(), 0); + Assertions.assertEquals(1000, perf.calcMsLatency(), 0); + Assertions.assertEquals(100000, perf.getMsMaxLatency(), 0); + } + + @Test + public void createOperationPerf() { + OperationPerf opPerf = Utils.createOperationPerf(op); + + PerfInfo perfInfo = opPerf.findStage(InvocationStageTrace.STAGE_TOTAL); + Integer[] latencyDistribution = opPerf.getLatencyDistribution(); + Assertions.assertEquals(10, perfInfo.getTotalRequests(), 0); + Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); + Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); + Assertions.assertEquals(2, latencyDistribution.length); + Assertions.assertEquals(1, latencyDistribution[0].intValue()); + Assertions.assertEquals(2, latencyDistribution[1].intValue()); + } + + @Test + public void addOperationPerfGroups() { + OperationPerfGroups groups = new OperationPerfGroups(); + PublishUtils.addOperationPerfGroups(groups, + CoreConst.RESTFUL, + op, + Utils.createStatusNode(Status.OK.name(), Utils.totalStageNode)); + + Map statusMap = groups.getGroups().get(CoreConst.RESTFUL); + OperationPerfGroup group = statusMap.get(Status.OK.name()); + + PerfInfo perfInfo = group.getSummary().findStage(InvocationStageTrace.STAGE_TOTAL); + Integer[] latencyDistribution = group.getSummary().getLatencyDistribution(); + Assertions.assertEquals(10, perfInfo.getTotalRequests(), 0); + Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); + Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); + Assertions.assertEquals(2, latencyDistribution.length); + Assertions.assertEquals(1, latencyDistribution[0].intValue()); + Assertions.assertEquals(2, latencyDistribution[1].intValue()); + } + + @Test + public void createThreadPoolPublishModels_empty() { + Map threadPools = + ThreadPoolMonitorPublishModelFactory.create(new MeasurementTree()); + + Assertions.assertTrue(threadPools.isEmpty()); + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestSlowInvocationLogger.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestSlowInvocationLogger.java new file mode 100644 index 00000000000..e09252c4e61 --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestSlowInvocationLogger.java @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.publish; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.definition.OperationConfig; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; +import org.apache.servicecomb.swagger.invocation.Response; +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.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.google.common.eventbus.EventBus; + +@ExtendWith(MockitoExtension.class) +public class TestSlowInvocationLogger { + @Mock + SCBEngine scbEngine; + + @Mock + Invocation invocation; + + @Mock + OperationMeta operationMeta; + + @Mock + RestOperationMeta restOperationMeta; + + @Mock + OperationConfig operationConfig; + + @Mock + Response response; + + @Mock + InvocationStageTrace stageTrace; + + InvocationFinishEvent event; + + SlowInvocationLogger logger; + + LogCollector logCollector; + + @BeforeEach + public void setup() { + EventBus eventBus = Mockito.spy(EventBus.class); + Mockito.doNothing().when(eventBus).register(Mockito.any()); + Mockito.when(scbEngine.getEventBus()).thenReturn(eventBus); + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + Mockito.when(operationMeta.getConfig()).thenReturn(operationConfig); + + logger = new SlowInvocationLogger(scbEngine); + event = new InvocationFinishEvent(invocation, response); + logCollector = new LogCollector(); + } + + @AfterEach + public void tearDown() { + logCollector.tearDown(); + } + + @Test + public void disable() { + logger.onInvocationFinish(event); + + Assertions.assertTrue(logCollector.getEvents().isEmpty()); + } + + @Test + public void enableButNotSlow() { + + Mockito.when(operationConfig.isSlowInvocationEnabled()).thenReturn(true); + Mockito.when(operationConfig.getNanoSlowInvocation()).thenReturn(2L); + Mockito.when(stageTrace.calcTotal()).thenReturn(1L); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); + logger.onInvocationFinish(event); + + Assertions.assertTrue(logCollector.getEvents().isEmpty()); + } + + @Test + public void consumerSlow() { + Endpoint endpoint = Mockito.mock(Endpoint.class); + Mockito.when(invocation.getEndpoint()).thenReturn(endpoint); + Mockito.when(endpoint.getEndpoint()).thenReturn("rest://1.1.1.1:1234"); + Mockito.when(invocation.isProducer()).thenReturn(false); + Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(restOperationMeta); + Mockito.when(operationConfig.isSlowInvocationEnabled()).thenReturn(true); + Mockito.when(operationConfig.getNanoSlowInvocation()).thenReturn(1L); + Mockito.when(stageTrace.calcTotal()).thenReturn(1L); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); + logger.onInvocationFinish(event); + + Assertions.assertEquals(""" + Slow Consumer invocation [null](0 ms)[null] + http method : null + url : null + endpoint : rest://1.1.1.1:1234 + status code : 0 + total : 0.0ms + prepare : 0.0ms + connection : 0.0ms + consumer-encode : 0.0ms + consumer-send : 0.0ms + wait : 0.0ms + consumer-decode : 0.0ms + """, + logCollector.getEvent(0).getMessage().getFormattedMessage()); + } + + @Test + public void edgeSlow() { + Endpoint endpoint = Mockito.mock(Endpoint.class); + Mockito.when(invocation.getEndpoint()).thenReturn(endpoint); + Mockito.when(endpoint.getEndpoint()).thenReturn("rest://1.1.1.1:1234"); + Mockito.when(invocation.isEdge()).thenReturn(true); + Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(restOperationMeta); + Mockito.when(operationConfig.isSlowInvocationEnabled()).thenReturn(true); + Mockito.when(operationConfig.getNanoSlowInvocation()).thenReturn(1L); + Mockito.when(stageTrace.calcTotal()).thenReturn(1L); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); + logger.onInvocationFinish(event); + + Assertions.assertEquals(""" + Slow Edge invocation [null](0 ms)[null] + http method : null + url : null + endpoint : rest://1.1.1.1:1234 + status code : 0 + total : 0.0ms + prepare : 0.0ms + provider-decode : 0.0ms + connection : 0.0ms + consumer-encode : 0.0ms + consumer-send : 0.0ms + wait : 0.0ms + consumer-decode : 0.0ms + provider-encode : 0.0ms + provider-send : 0.0ms + """, + logCollector.getEvent(0).getMessage().getFormattedMessage()); + } + + @Test + public void producerSlow() { + + HttpServletRequestEx requestEx = Mockito.mock(HttpServletRequestEx.class); + + Mockito.when(invocation.getRequestEx()).thenReturn(requestEx); + Mockito.when(requestEx.getRemoteAddr()).thenReturn("1.1.1.1"); + Mockito.when(requestEx.getRemotePort()).thenReturn(1234); + Mockito.when(invocation.isProducer()).thenReturn(true); + Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(restOperationMeta); + Mockito.when(operationConfig.isSlowInvocationEnabled()).thenReturn(true); + Mockito.when(operationConfig.getNanoSlowInvocation()).thenReturn(1L); + Mockito.when(stageTrace.calcTotal()).thenReturn(1L); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); + + logger.onInvocationFinish(event); + + Assertions.assertEquals(""" + Slow Provider invocation [null](0 ms)[null] + http method : null + url : null + endpoint : 1.1.1.1:1234 + status code : 0 + total : 0.0ms + prepare : 0.0ms + provider-decode : 0.0ms + queue : 0.0ms + execute : 0.0ms + provider-encode : 0.0ms + provider-send : 0.0ms + """, + logCollector.getEvent(0).getMessage().getFormattedMessage()); + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestThreadPoolPublishModelFactory.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestThreadPoolPublishModelFactory.java new file mode 100644 index 00000000000..f00e49180ae --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestThreadPoolPublishModelFactory.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.publish; + +import java.util.concurrent.BlockingQueue; + +import org.apache.servicecomb.core.executor.ThreadPoolExecutorEx; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.metrics.core.ThreadPoolMetersInitializer; +import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.mockito.junit.jupiter.MockitoExtension; + +import org.skyscreamer.jsonassert.JSONAssert; + +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestThreadPoolPublishModelFactory { + MeterRegistry registry = new SimpleMeterRegistry(); + + @Mock + BlockingQueue queue; + + @Test + public void createDefaultPublishModel() throws Exception { + ThreadPoolExecutorEx threadPoolExecutor = Mockito.mock(ThreadPoolExecutorEx.class); + Mockito.when(threadPoolExecutor.getQueue()).thenReturn(queue); + Mockito.doReturn(10).when(queue).size(); + + MetricsBootstrapConfig metricsBootstrapConfig = Mockito.mock(MetricsBootstrapConfig.class); + ThreadPoolMetersInitializer threadPoolMetersInitializer = new ThreadPoolMetersInitializer() { + @Override + public void createThreadPoolMeters() { + createThreadPoolMeters("test", threadPoolExecutor); + } + }; + threadPoolMetersInitializer.init(registry, EventManager.getEventBus(), metricsBootstrapConfig); + PublishModelFactory factory = new PublishModelFactory(registry.getMeters()); + DefaultPublishModel model = factory.createDefaultPublishModel(); + + JSONAssert.assertEquals( + """ + {"test":{"avgTaskCount":0.0,"avgCompletedTaskCount":0.0,"currentThreadsBusy":0,"maxThreads":0,"poolSize":0,"corePoolSize":0,"queueSize":10,"rejected":0.0}}""", + JsonUtils.writeValueAsString(model.getThreadPools()), false); + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerf.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerf.java new file mode 100644 index 00000000000..97079bfe168 --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerf.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.metrics.core.publish.model.invocation; + +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestOperationPerf { + String op = "op"; + + OperationPerf opPerf = new OperationPerf(); + + @Test + public void add() { + Assertions.assertTrue(opPerf.getStages().isEmpty()); + + OperationPerf otherOpPerf = Utils.createOperationPerf(op); + opPerf.add(otherOpPerf); + + Assertions.assertEquals(op, otherOpPerf.getOperation()); + + PerfInfo perfInfo = opPerf.findStage(InvocationStageTrace.STAGE_TOTAL); + Assertions.assertEquals(10, perfInfo.getTotalRequests(), 0); + Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); + Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); + + perfInfo = opPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_BUSINESS); + Assertions.assertEquals(10, perfInfo.getTotalRequests(), 0); + Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); + Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerfGroup.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerfGroup.java new file mode 100644 index 00000000000..ff0191b14b5 --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerfGroup.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.metrics.core.publish.model.invocation; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.junit.jupiter.api.Assertions; + +import jakarta.ws.rs.core.Response.Status; +import org.junit.jupiter.api.Test; + +public class TestOperationPerfGroup { + String op = "op"; + + OperationPerfGroup group = new OperationPerfGroup(CoreConst.RESTFUL, Status.OK.name()); + + @Test + public void construct() { + Assertions.assertEquals(CoreConst.RESTFUL, group.getTransport()); + Assertions.assertEquals(Status.OK.name(), group.getStatus()); + Assertions.assertTrue(group.getOperationPerfs().isEmpty()); + Assertions.assertNull(group.getSummary()); + } + + @Test + public void addOperationPerf() { + OperationPerf opPerf = Utils.createOperationPerf(op); + group.addOperationPerf(opPerf); + group.addOperationPerf(opPerf); + + Assertions.assertTrue(group.getOperationPerfs().contains(opPerf)); + + OperationPerf summary = group.getSummary(); + + PerfInfo perfInfo = summary.findStage(InvocationStageTrace.STAGE_TOTAL); + Assertions.assertEquals(20, perfInfo.getTotalRequests(), 0); + Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); + Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); + + perfInfo = summary.findStage(InvocationStageTrace.STAGE_PROVIDER_BUSINESS); + Assertions.assertEquals(20, perfInfo.getTotalRequests(), 0); + Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); + Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestPerfInfo.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestPerfInfo.java new file mode 100644 index 00000000000..35c6d83e72c --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestPerfInfo.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.publish.model.invocation; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestPerfInfo { + @Test + public void construct() { + PerfInfo perf = new PerfInfo(); + + Assertions.assertEquals(0, perf.getTotalRequests(), 0); + Assertions.assertEquals(0, perf.getMsTotalTime(), 0); + Assertions.assertEquals(0, perf.getMsMaxLatency(), 0); + Assertions.assertEquals(0, perf.calcMsLatency(), 0); + } + + @Test + public void add_changeMax() { + PerfInfo sum = new PerfInfo(); + + PerfInfo other = new PerfInfo(); + other.setTotalRequests(10); + other.setMsTotalTime(10); + other.setMsMaxLatency(100); + sum.add(other); + + other = new PerfInfo(); + other.setTotalRequests(20); + other.setMsTotalTime(20); + other.setMsMaxLatency(200); + sum.add(other); + + Assertions.assertEquals(30, sum.getTotalRequests(), 0); + Assertions.assertEquals(30, sum.getMsTotalTime(), 0); + Assertions.assertEquals(200, sum.getMsMaxLatency(), 0); + Assertions.assertEquals(1.0, sum.calcMsLatency(), 0); + } + + @Test + public void add_notChangeMax() { + PerfInfo sum = new PerfInfo(); + + PerfInfo other = new PerfInfo(); + other.setTotalRequests(10); + other.setMsTotalTime(10); + other.setMsMaxLatency(100); + sum.add(other); + + other = new PerfInfo(); + other.setTotalRequests(20); + other.setMsTotalTime(20); + other.setMsMaxLatency(50); + sum.add(other); + + Assertions.assertEquals(30, sum.getTotalRequests(), 0); + Assertions.assertEquals(1.0, sum.calcMsLatency(), 0); + Assertions.assertEquals(100, sum.getMsMaxLatency(), 0); + } + + @Test + public void testToString() { + PerfInfo perf = new PerfInfo(); + perf.setTotalRequests(10); + perf.setMsTotalTime(10); + perf.setMsMaxLatency(100); + + Assertions.assertEquals("PerfInfo [tps=10.0, msTotalTime=10.0, msLatency=1.0, msMaxLatency=100.0]", perf.toString()); + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java new file mode 100644 index 00000000000..17a62c57569 --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.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.metrics.core.publish.model.invocation; + +import java.util.HashMap; +import java.util.List; + +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; +import org.apache.servicecomb.metrics.core.publish.PublishUtils; + +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.Statistic; +import io.micrometer.core.instrument.Tags; +import jakarta.ws.rs.core.Response.Status; + +public class Utils { + public static MeasurementNode totalStageNode = Utils.createStageNode(InvocationStageTrace.STAGE_TOTAL, 10, 10, 100); + + public static MeasurementNode executeStageNode = + Utils.createStageNode(InvocationStageTrace.STAGE_PROVIDER_BUSINESS, 10, 10, 100); + + public static Id initId = new Id("id", Tags.empty(), null, null, Type.OTHER); + + public static MeasurementNode createStageNode(String stage, + double count, + double totalTime, + double max) { + Id id = initId; + Measurement countMeasurement = new Measurement(() -> count, Statistic.COUNT); + Measurement totalTimeMeasurement = new Measurement(() -> totalTime, Statistic.TOTAL_TIME); + Measurement maxMeasurement = new Measurement(() -> max, Statistic.MAX); + + MeasurementNode stageNode = new MeasurementNode(stage, id, null); + stageNode.addChild(Statistic.COUNT.name(), id, countMeasurement); + stageNode.addChild(Statistic.TOTAL_TIME.name(), id, totalTimeMeasurement); + stageNode.addChild(Statistic.MAX.name(), id, maxMeasurement); + + return stageNode; + } + + public static MeasurementNode createStatusNode(String status, MeasurementNode... stageNodes) { + Id id = initId; + MeasurementNode statusNode = new MeasurementNode(status, id, new HashMap<>()); + MeasurementNode typeNode = new MeasurementNode(MeterInvocationConst.TAG_STAGE, id, new HashMap<>()); + MeasurementNode latencyNode = new MeasurementNode(MeterInvocationConst.TAG_DISTRIBUTION, id, + new HashMap<>()); + List measurements = latencyNode.getMeasurements(); + measurements.add(new Measurement(() -> 1, Statistic.VALUE)); + measurements.add(new Measurement(() -> 2, Statistic.VALUE)); + for (MeasurementNode stageNode : stageNodes) { + typeNode.getChildren().put(stageNode.getName(), stageNode); + } + statusNode.getChildren().put(latencyNode.getName(), latencyNode); + statusNode.getChildren().put(typeNode.getName(), typeNode); + return statusNode; + } + + public static OperationPerf createOperationPerf(String op) { + MeasurementNode statusNode = createStatusNode(Status.OK.name(), totalStageNode, executeStageNode); + return PublishUtils.createOperationPerf(op, statusNode); + } +} diff --git a/metrics/metrics-core/src/test/resources/log4j2.xml b/metrics/metrics-core/src/test/resources/log4j2.xml new file mode 100644 index 00000000000..8cee07220ec --- /dev/null +++ b/metrics/metrics-core/src/test/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/metrics/metrics-integration/metrics-prometheus/pom.xml b/metrics/metrics-integration/metrics-prometheus/pom.xml new file mode 100644 index 00000000000..20d23c9d567 --- /dev/null +++ b/metrics/metrics-integration/metrics-prometheus/pom.xml @@ -0,0 +1,51 @@ + + + + + + metrics-integration + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + metrics-prometheus + Java Chassis::Metrics::Integration::Prometheus + + + + io.prometheus + simpleclient + + + io.prometheus + simpleclient_httpserver + + + + org.apache.servicecomb + metrics-core + + + org.apache.servicecomb + foundation-test-scaffolding + + + + diff --git a/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusConfiguration.java b/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusConfiguration.java new file mode 100644 index 00000000000..cc986aad016 --- /dev/null +++ b/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusConfiguration.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.metrics.prometheus; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class PrometheusConfiguration { + @Bean + public PrometheusPublisher prometheusPublisher() { + return new PrometheusPublisher(); + } +} diff --git a/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusPublisher.java b/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusPublisher.java new file mode 100644 index 00000000000..19cd28d510b --- /dev/null +++ b/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusPublisher.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.metrics.prometheus; + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.foundation.metrics.MetricsInitializer; +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.eventbus.EventBus; + +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.prometheus.client.Collector; +import io.prometheus.client.Collector.MetricFamilySamples.Sample; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.exporter.HTTPServer; + +public class PrometheusPublisher extends Collector implements Collector.Describable, MetricsInitializer { + private static final Logger LOGGER = LoggerFactory.getLogger(PrometheusPublisher.class); + + public static final String METRICS_PROMETHEUS_ADDRESS = "servicecomb.metrics.prometheus.address"; + + private HTTPServer httpServer; + + private MeterRegistry meterRegistry; + + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + this.meterRegistry = meterRegistry; + + //prometheus default port allocation is here : https://github.com/prometheus/prometheus/wiki/Default-port-allocations + String address = environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696"); + + try { + InetSocketAddress socketAddress = getSocketAddress(address); + register(); + this.httpServer = new HTTPServer(socketAddress, CollectorRegistry.defaultRegistry, true); + + LOGGER.info("Prometheus httpServer listened : {}.", address); + } catch (Exception e) { + throw new ServiceCombException("create http publish server failed,may bad address : " + address, e); + } + } + + private InetSocketAddress getSocketAddress(String address) { + String[] hostAndPort = address.split(":"); + if (hostAndPort.length == 2) { + return new InetSocketAddress(hostAndPort[0], Integer.parseInt(hostAndPort[1])); + } + throw new ServiceCombException("create http publish server failed,bad address : " + address); + } + + @Override + public List describe() { + List familySamples = new ArrayList<>(); + + List samples = new ArrayList<>(); + + for (Meter meter : this.meterRegistry.getMeters()) { + meter.measure().forEach(measurement -> { + Sample sample = convertMeasurementToSample(meter, measurement); + samples.add(sample); + }); + } + + familySamples.add(new MetricFamilySamples("ServiceComb_Metrics", Type.UNKNOWN, "ServiceComb Metrics", samples)); + + return familySamples; + } + + protected Sample convertMeasurementToSample(Meter meter, Measurement measurement) { + String prometheusName = meter.getId().getName().replace(".", "_"); + List labelNames = new ArrayList<>(); + List labelValues = new ArrayList<>(); + + labelNames.add("appId"); + labelValues.add(BootStrapProperties.readApplication(environment)); + + for (Tag tag : meter.getId().getTags()) { + labelNames.add(tag.getKey()); + labelValues.add(tag.getValue()); + } + + return new Sample(prometheusName, labelNames, labelValues, measurement.getValue()); + } + + @Override + public List collect() { + return describe(); + } + + @Override + public void destroy() { + if (httpServer == null) { + return; + } + + httpServer.close(); + httpServer = null; + LOGGER.info("Prometheus httpServer stopped."); + } +} diff --git a/metrics/metrics-integration/metrics-prometheus/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/metrics/metrics-integration/metrics-prometheus/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..e64b469a4a9 --- /dev/null +++ b/metrics/metrics-integration/metrics-prometheus/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.metrics.prometheus.PrometheusConfiguration diff --git a/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java b/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java new file mode 100644 index 00000000000..bd9fdd0bfd7 --- /dev/null +++ b/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.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.metrics.prometheus; + +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 static org.apache.servicecomb.metrics.prometheus.PrometheusPublisher.METRICS_PROMETHEUS_ADDRESS; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +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 com.sun.net.httpserver.HttpServer; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import io.prometheus.client.exporter.HTTPServer; + +public class TestPrometheusPublisher { + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + + PrometheusPublisher publisher = new PrometheusPublisher(); + + Environment environment = Mockito.mock(Environment.class); + + @BeforeEach + public void setUp() { + publisher.setEnvironment(environment); + 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); + } + + @AfterAll + public static void teardown() { + } + + @Test + public void testBadPublishAddress() { + Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696")) + .thenReturn("a:b:c"); + Assertions.assertThrows(ServiceCombException.class, () -> { + publisher.init(meterRegistry, null, null); + }); + } + + @Test + public void testBadPublishAddress_BadPort() { + Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696")) + .thenReturn("localhost:xxxx"); + Assertions.assertThrows(ServiceCombException.class, () -> { + publisher.init(meterRegistry, null, null); + }); + } + + @Test + public void testBadPublishAddress_TooLargePort() { + Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696")) + .thenReturn("localhost:9999999"); + Assertions.assertThrows(ServiceCombException.class, () -> { + publisher.init(meterRegistry, null, null); + }); + } + + @Test + public void collect() throws IllegalAccessException, IOException { + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_APPLICATION)).thenReturn("testAppId"); + Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696")) + .thenReturn("localhost:0"); + publisher.setEnvironment(environment); + publisher.init(meterRegistry, null, new MetricsBootstrapConfig(environment)); + + Counter counter = meterRegistry.counter("count.name", "tag1", "tag1v", "tag2", "tag2v"); + counter.increment(); + + HTTPServer httpServer = (HTTPServer) FieldUtils.readField(publisher, "httpServer", true); + com.sun.net.httpserver.HttpServer server = (HttpServer) FieldUtils.readField(httpServer, "server", true); + + URL url = new URL("http://localhost:" + server.getAddress().getPort() + "/metrics"); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + try (InputStream is = conn.getInputStream()) { + Assertions.assertEquals(""" + # HELP ServiceComb_Metrics ServiceComb Metrics + # TYPE ServiceComb_Metrics untyped + count_name{appId="testAppId",tag1="tag1v",tag2="tag2v",} 1.0 + """, + IOUtils.toString(is, StandardCharsets.UTF_8)); + } + + publisher.destroy(); + } +} diff --git a/metrics/metrics-integration/pom.xml b/metrics/metrics-integration/pom.xml new file mode 100644 index 00000000000..d989f8bc2a4 --- /dev/null +++ b/metrics/metrics-integration/pom.xml @@ -0,0 +1,37 @@ + + + + + + metrics + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + metrics-integration + Java Chassis::Metrics::Integration + pom + + + metrics-prometheus + + + diff --git a/metrics/pom.xml b/metrics/pom.xml new file mode 100644 index 00000000000..95eb85051fc --- /dev/null +++ b/metrics/pom.xml @@ -0,0 +1,39 @@ + + + + + 4.0.0 + pom + + org.apache.servicecomb + java-chassis-parent + 3.4.0-SNAPSHOT + ../parents/default + + + metrics + Java Chassis::Metrics + + + metrics-core + metrics-integration + + + diff --git a/parent/pom.xml b/parent/pom.xml deleted file mode 100644 index 09fc2c6a9e5..00000000000 --- a/parent/pom.xml +++ /dev/null @@ -1,241 +0,0 @@ - - - - - 4.0.0 - - - io.servicecomb - java-chassis-dependencies - 0.3.0-SNAPSHOT - ../java-chassis-dependencies - - - java-chassis-parent - pom - - Java Chassis Parent - Java Chassis Parent - https://github.com/ServiceComb/java-chassis - - - UTF-8 - 1.8 - - -Dfile.encoding=UTF-8 - UTF-8 - - - - - - com.github.tomakehurst - wiremock-standalone - 2.6.0 - test - - - - - - - com.github.seanyinx - unit-scaffolding - 1.0.0 - test - - - org.hamcrest - hamcrest-all - test - - - org.jmockit - jmockit - test - - - org.mockito - mockito-core - test - - - junit - junit - test - - - org.hamcrest - hamcrest-core - test - - - org.powermock - powermock-api-mockito - test - - - org.powermock - powermock-module-junit4 - test - - - - javax.inject - javax.inject - - - org.springframework - spring-aspects - - - - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - ../etc/checkstyle.xml - **/demo/** - - - - - - - io.fabric8 - docker-maven-plugin - 0.20.0 - - - org.codehaus.gmaven - gmaven-plugin - 1.5 - - - org.eluder.coveralls - coveralls-maven-plugin - - - ${project.basedir}/coverage-reports/target/site/jacoco-aggregate/jacoco.xml - - - - - org.jacoco - jacoco-maven-plugin - 0.7.8 - - - *$Impl_* - - - - - default-prepare-agent - - prepare-agent - - - ${project.build.directory}/jacoco.exec - - - - default-prepare-agent-integration - pre-integration-test - - prepare-agent-integration - - - ${project.build.directory}/jacoco-it.exec - jacoco.failsafe.argLine - - - - default-report - - report - - - - default-report-integration - - report-integration - - - - - - - - - - - - org.apache.maven.plugins - maven-jxr-plugin - 2.5 - - - - - - - docker-machine - - - - - io.fabric8 - docker-maven-plugin - - - default - true - - - - - - - - - jacoco - - - - org.jacoco - jacoco-maven-plugin - - - - - - diff --git a/parents/default/pom.xml b/parents/default/pom.xml new file mode 100644 index 00000000000..b8954d029b8 --- /dev/null +++ b/parents/default/pom.xml @@ -0,0 +1,120 @@ + + + + + 4.0.0 + + + org.apache.servicecomb + java-chassis-dependencies + 3.4.0-SNAPSHOT + ../../dependencies/default + + + java-chassis-parent + pom + + Java Chassis::Parent::Default + Java Chassis Parent + https://github.com/apache/servicecomb-java-chassis + + + + org.awaitility + awaitility + test + + + org.hamcrest + hamcrest-all + test + + + org.hamcrest + hamcrest-core + test + + + org.mockito + mockito-core + test + + + net.bytebuddy + byte-buddy + test + + + com.github.seanyinx + unit-scaffolding + test + + + junit + junit + test + + + org.junit.jupiter + junit-jupiter + test + + + org.junit.platform + junit-platform-launcher + test + + + org.junit.vintage + junit-vintage-engine + test + + + org.assertj + assertj-core + test + + + org.springframework.boot + spring-boot-starter-test + + + org.springframework.boot + spring-boot-starter-logging + + + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-api + test + + + org.apache.logging.log4j + log4j-core + test + + + diff --git a/parents/pom.xml b/parents/pom.xml new file mode 100644 index 00000000000..8fce5793c5c --- /dev/null +++ b/parents/pom.xml @@ -0,0 +1,38 @@ + + + + + 4.0.0 + + + + org.apache.servicecomb + java-chassis + 3.4.0-SNAPSHOT + + + java-chassis-parents + Java Chassis::Parents + pom + + + default + + + diff --git a/pom.xml b/pom.xml index 0d60a032207..25882c4ca33 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,13 @@ + + 3.6.0 + 4.3.0 + 12.2.0 + 0.48.0 + 3.6.3 + 3.2.8 + 0.8.14 + 3.12.0 + 2.5 + 3.15.0 + 3.5.4 + 3.4.2 + 3.9.0 + 3.2.0 + 1.7.1 + 0.6.1 + 3.19.2 + 1.47.0 + 12.3.1 + 3.1.1 + 3.21.0 + 3.3.0 + 4.9.8.2 + 3.5.4 + 3.5.4 + @@ -40,23 +80,63 @@ + + + Development List + dev-subscribe@servicecomb.apache.org + dev-unsubscribe@servicecomb.apache.org + dev@servicecomb.apache.org + + + Commits List + commits-subscribe@servicecomb.apache.org + commits-unsubscribe@servicecomb.apache.org + commits@servicecomb.apache.org + + + The ServiceComb Developer Team - dev@servicecomb.io + dev@servicecomb.apache.org + + jira + https://issues.apache.org/jira/browse/SCB + + + + The Apache Software Foundation + http://www.apache.org/ + + + 2017 + - https://github.com/ServiceComb/java-chassis - scm:git:https://github.com/ServiceComb/java-chassis.git - scm:git:https://github.com/ServiceComb/java-chassis.git + https://github.com/apache/servicecomb-java-chassis + scm:git:https://github.com/apache/servicecomb-java-chassis.git + scm:git:https://github.com/apache/servicecomb-java-chassis.git v${project.version} + + + apache.releases.https + Apache Release Distribution Repository + https://repository.apache.org/service/local/staging/deploy/maven2 + + + apache.snapshots.https + Apache Development Snapshot Repository + https://repository.apache.org/content/repositories/snapshots + + + - parent - java-chassis-dependencies + dependencies + parents common foundations handlers @@ -65,41 +145,351 @@ swagger core service-registry - spring-boot-starter + tracing + edge + metrics + dynamic-config + spring-boot + solutions + clients + governance + huawei-cloud - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - + + + + kr.motd.maven + os-maven-plugin + ${os-maven-plugin.version} + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + ci/checkstyle/checkstyle.xml + ci/checkstyle/suppressions.xml + true + + + + org.apache.maven.plugins + maven-remote-resources-plugin + + + process-resource-bundles + + process + + + ${skip-remote-resource} + + org.apache:apache-jar-resource-bundle:1.4 + org.apache:apache-incubator-disclaimer-resource-bundle:1.1 + + + Apache ServiceComb + + + + + + + org.codehaus.mojo + license-maven-plugin + 2.7.1 + + + default-cli + + + true + + /org/codehaus/mojo/license/third-party-file-groupByMultiLicense.ftl + + The Apache Software License, Version 2.0|The Apache License, Version 2.0 + The Apache Software License, Version 2.0|Apache License, Version 2.0 + The Apache Software License, Version 2.0|Apache Public License 2.0 + The Apache Software License, Version 2.0|Apache 2 + The Apache Software License, Version 2.0|Apache 2.0 + The Apache Software License, Version 2.0|Apache-2.0 + The Apache Software License, Version 2.0|Apache License 2.0 + The Apache Software License, Version 2.0|Apache License, version 2.0 + 3-Clause BSD License|BSD 3-clause + 3-Clause BSD License|BSD 3-Clause + Eclipse Public License v1.0|Eclipse Public License 1.0 + Eclipse Public License v1.0|Eclipse Public License - v 1.0 + The MIT License|MIT License + + org.apache.servicecomb* + + + + + + org.apache.rat + apache-rat-plugin + + + DISCLAIMER + .travis.yml + **/*.md + **/*.MD + **/*.iml + .github/** + + **/etc/eclipse-java-google-style.xml + **/etc/intellij-java-google-style.xml + + **/resources/ssl/** + + **/src/release/licenses/** + + **/logs/**.log + **/**.log + + **/target/** + + **/resources/webroot/images/*.png + **/resources/webroot/images/*.jpg + + **/org/apache/servicecomb/foundation/common/utils/MimeTypesUtils.java + **/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java + + **/java/org/apache/servicecomb/foundation/protobuf/internal/model/ProtobufRoot.java + + **/resources/google/protobuf/**.proto + + + + + org.codehaus.mojo + exec-maven-plugin + ${exec-maven-plugin.version} + + + + java + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire-maven-plugin.version} + + + -Djdk.attach.allowAttachSelf + --add-opens java.base/java.io=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.lang.reflect=ALL-UNNAMED + --add-opens java.base/java.net=ALL-UNNAMED + --add-opens java.base/java.nio=ALL-UNNAMED + --add-opens java.base/java.nio.channels.spi=ALL-UNNAMED + --add-opens java.base/java.nio.file=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.util.concurrent=ALL-UNNAMED + --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED + --add-opens java.base/java.util.jar=ALL-UNNAMED + --add-opens java.base/java.util.stream=ALL-UNNAMED + --add-opens java.base/java.time=ALL-UNNAMED + --add-opens java.base/jdk.internal.loader=ALL-UNNAMED + --add-opens java.base/sun.net.dns=ALL-UNNAMED + --add-opens java.base/sun.nio.ch=ALL-UNNAMED + --add-opens java.base/sun.security.jca=ALL-UNNAMED + --add-opens java.xml/jdk.xml.internal=ALL-UNNAMED + --add-reads java.base=java.desktop + + ${maven.test.skip} + ${maven.test.failure.ignore} + + ${excludesFile} + + 5 + 2 + true + alphabetical + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + true + true + + + + + + com.github.spotbugs + spotbugs-maven-plugin + + true + true + Medium + ${session.executionRootDirectory}/ci/spotbugs/exclude.xml + + + + + check + + + + + + + + + org.eluder.coveralls + coveralls-maven-plugin + ${coveralls-maven-plugin.version} + + + ${project.basedir}/coverage-reports/target/site/jacoco-aggregate/jacoco.xml + + + + + maven-remote-resources-plugin + ${maven-remote-resources-plugin.version} + + + io.fabric8 + docker-maven-plugin + ${docker-maven-plugin.version} + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + *$Impl_* + + + + + default-prepare-agent + + prepare-agent + + + ${project.build.directory}/jacoco.exec + + + + default-prepare-agent-integration + pre-integration-test + + prepare-agent-integration + + + ${project.build.directory}/jacoco-it.exec + jacoco.failsafe.argLine + + + + default-report + + report + + + + default-report-integration + + report-integration + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + -parameters + true + true + + + -Xlint:all + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + ${maven-project-info-reports-plugin.version} + + + org.apache.maven.plugins + maven-site-plugin + ${site-maven-plugin.version} + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${checkstyle-maven-plugin.version} + true + + + com.puppycrawl.tools + checkstyle + ${puppycrawl-checkstyle.version} + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + ${main.class} + + + + + + com.github.spotbugs + spotbugs-maven-plugin + ${spotbug-maven-plugin.version} + + + + release + + distribution + + + false + - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - ossrh - https://oss.sonatype.org/ - false - true - + org.apache.maven.plugins + maven-deploy-plugin org.apache.maven.plugins maven-gpg-plugin - 1.6 + ${gpg-maven-plugin.version} sign-artifacts @@ -113,7 +503,7 @@ org.apache.maven.plugins maven-source-plugin - 2.2.1 + ${source-maven-plugin.version} attach-sources @@ -126,7 +516,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + ${javadoc-maven-plugin.version} attach-javadocs @@ -135,11 +525,22 @@ + + 17 + + + + created + t + Date created: + + + org.apache.maven.plugins maven-release-plugin - 2.5.3 + ${release-maven-plugin.version} true false @@ -154,13 +555,6 @@ it demo - integration-tests - - - - distribution - - java-chassis-distribution @@ -169,61 +563,89 @@ coverage-reports + + docker-machine + + + + + io.fabric8 + docker-maven-plugin + + + default + true + + + + + + + + + jacoco + + + + org.jacoco + jacoco-maven-plugin + + + + + + owasp-dependency-check + + + + org.owasp + dependency-check-maven + ${dependency-check-maven-plugin.version} + + + src/owasp-dependency-check-suppressions.xml + + 7 + false + false + false + false + false + false + false + false + false + false + false + false + + + + + aggregate + + + + + + + + + + org.owasp + dependency-check-maven + ${dependency-check-maven-plugin.version} + + + + aggregate + + + + + + + - - - - org.codehaus.mojo - exec-maven-plugin - 1.2.1 - - - - java - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.18.1 - - ${maven.test.skip} - ${maven.test.failure.ignore} - - ${excludesFile} - - 2 - true - - - - org.codehaus.mojo - findbugs-maven-plugin - 3.0.1 - - true - true - true - findbugs-exclude.xml - - - - - - - org.eluder.coveralls - coveralls-maven-plugin - 4.3.0 - - - ${project.basedir}/coverage-reports/target/site/jacoco-aggregate/jacoco.xml - - - - - - diff --git a/providers/pom.xml b/providers/pom.xml index 971b18279e6..9bd8a52f271 100644 --- a/providers/pom.xml +++ b/providers/pom.xml @@ -1,11 +1,12 @@ - + 4.0.0 - io.servicecomb + org.apache.servicecomb java-chassis-parent - 0.3.0-SNAPSHOT - ../parent + 3.4.0-SNAPSHOT + ../parents/default providers + Java Chassis::Providers pom - provider-jaxrs - provider-pojo - provider-rest-common - provider-springmvc + provider-jaxrs + provider-pojo + provider-rest-common + provider-springmvc - \ No newline at end of file + diff --git a/providers/provider-jaxrs/pom.xml b/providers/provider-jaxrs/pom.xml index 042a4c3b9fd..1bd923857c6 100644 --- a/providers/provider-jaxrs/pom.xml +++ b/providers/provider-jaxrs/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb - providers - 0.3.0-SNAPSHOT - - provider-jaxrs - - - - io.servicecomb - provider-rest-common - - - io.servicecomb - swagger-invocation-jaxrs - - - io.servicecomb - swagger-generator-jaxrs - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + + org.apache.servicecomb + providers + 3.4.0-SNAPSHOT + + provider-jaxrs + Java Chassis::Providers::JAXRS + + + org.apache.servicecomb + provider-rest-common + + + org.apache.servicecomb + swagger-invocation-jaxrs + + diff --git a/providers/provider-pojo/pom.xml b/providers/provider-pojo/pom.xml index 96e3bbd4a8b..922f544b9f5 100644 --- a/providers/provider-pojo/pom.xml +++ b/providers/provider-pojo/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - providers - 0.3.0-SNAPSHOT - - provider-pojo - - - io.servicecomb - java-chassis-core - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + 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 + providers + 3.4.0-SNAPSHOT + + provider-pojo + Java Chassis::Providers::POJO + + + org.apache.servicecomb + java-chassis-core + + + org.apache.servicecomb + foundation-test-scaffolding + test + + + org.apache.servicecomb + registry-local + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.mockito + mockito-inline + test + + diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/InstanceFactory.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/InstanceFactory.java deleted file mode 100644 index 91f7449a23d..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/InstanceFactory.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo; - -public interface InstanceFactory { - String getImplName(); - - Object create(String implValue); -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/Invoker.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/Invoker.java deleted file mode 100644 index 88b1a412e6d..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/Invoker.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; - -import org.springframework.util.StringUtils; - -import io.servicecomb.core.CseContext; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.invocation.InvocationFactory; -import io.servicecomb.core.provider.consumer.InvokerUtils; -import io.servicecomb.core.provider.consumer.ReferenceConfig; -import io.servicecomb.core.provider.consumer.ReferenceConfigUtils; -import io.servicecomb.swagger.engine.SwaggerConsumer; -import io.servicecomb.swagger.engine.SwaggerConsumerOperation; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.ExceptionFactory; - -public class Invoker implements InvocationHandler { - // 原始数据 - private String microserviceName; - - private String schemaId; - - private Class consumerIntf; - - // 生成的数据 - private SchemaMeta schemaMeta; - - private ReferenceConfig referenceConfig; - - private SwaggerConsumer swaggerConsumer; - - public Invoker(String microserviceName, String schemaId, Class consumerIntf) { - this.microserviceName = microserviceName; - this.schemaId = schemaId; - this.consumerIntf = consumerIntf; - } - - protected void prepare() { - referenceConfig = ReferenceConfigUtils.getForInvoke(microserviceName); - MicroserviceMeta microserviceMeta = referenceConfig.getMicroserviceMeta(); - - if (StringUtils.isEmpty(schemaId)) { - // 未指定schemaId,看看consumer接口是否等于契约接口 - schemaMeta = microserviceMeta.findSchemaMeta(consumerIntf); - if (schemaMeta == null) { - // 尝试用consumer接口名作为schemaId - schemaId = consumerIntf.getName(); - schemaMeta = microserviceMeta.ensureFindSchemaMeta(schemaId); - } - } else { - schemaMeta = microserviceMeta.ensureFindSchemaMeta(schemaId); - } - - this.swaggerConsumer = CseContext.getInstance().getSwaggerEnvironment().createConsumer(consumerIntf, - schemaMeta.getSwaggerIntf()); - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (swaggerConsumer == null) { - prepare(); - } - - Invocation invocation = - InvocationFactory.forConsumer(referenceConfig, schemaMeta, method.getName(), null); - - SwaggerConsumerOperation consumerOperation = swaggerConsumer.findOperation(method.getName()); - consumerOperation.getArgumentsMapper().toInvocation(args, invocation); - - Response response = InvokerUtils.innerSyncInvoke(invocation); - if (response.isSuccessed()) { - return consumerOperation.getResponseMapper().mapResponse(response); - } - - throw ExceptionFactory.convertConsumerException(response.getResult()); - } -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/PojoConfig.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/PojoConfig.java deleted file mode 100644 index 85664b7400d..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/PojoConfig.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo; - -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import com.netflix.config.DynamicPropertyFactory; - -@Configuration -class PojoConfig { - - private static final String LOADING_MODE_BLOCKING = "blocking"; - - @Bean - Executor executor() { - if (LOADING_MODE_BLOCKING.equals(loadingMode())) { - return Runnable::run; - } - - return Executors.newSingleThreadExecutor(); - } - - private String loadingMode() { - return DynamicPropertyFactory.getInstance() - .getStringProperty( - "servicecomb.rpcReference.loadingMode", - LOADING_MODE_BLOCKING) - .get(); - } -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/PojoConst.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/PojoConst.java deleted file mode 100644 index 519cec719fd..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/PojoConst.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo; - -public final class PojoConst { - private PojoConst() { - } - - public static final String FIELD_SCHEMA_ID = "schemaId"; - - public static final String SCHEMA_ID = "schema-id"; - - public static final String IMPL = "implementation"; - - public static final String FIELD_MICROSERVICE_NAME = "microserviceName"; - - public static final String MICROSERVICE_NAME = "microservice-name"; - - public static final String FIELD_INTERFACE = "consumerIntf"; - - public static final String INTERFACE = "interface"; - - public static final String POJO = "pojo"; - - public static final String SPRING = "spring"; -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/PojoConsumerProvider.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/PojoConsumerProvider.java deleted file mode 100644 index de4444083da..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/PojoConsumerProvider.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo; - -import static io.servicecomb.provider.pojo.PojoConst.POJO; - -import org.springframework.stereotype.Component; - -import io.servicecomb.core.provider.consumer.AbstractConsumerProvider; - -@Component -public class PojoConsumerProvider extends AbstractConsumerProvider { - @Override - public String getName() { - return POJO; - } -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/PojoProducerProvider.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/PojoProducerProvider.java deleted file mode 100644 index 2d942a35d7d..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/PojoProducerProvider.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo; - -import javax.inject.Inject; - -import org.springframework.stereotype.Component; - -import io.servicecomb.core.definition.schema.ProducerSchemaFactory; -import io.servicecomb.core.provider.producer.AbstractProducerProvider; -import io.servicecomb.foundation.common.RegisterManager; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.provider.pojo.instance.PojoInstanceFactory; -import io.servicecomb.provider.pojo.instance.SpringInstanceFactory; -import io.servicecomb.provider.pojo.schema.PojoProducerMeta; -import io.servicecomb.provider.pojo.schema.PojoProducers; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.Microservice; - -@Component -public class PojoProducerProvider extends AbstractProducerProvider { - private RegisterManager instanceFactoryMgr = - new RegisterManager<>("pojo instance factory manager"); - - @Inject - private ProducerSchemaFactory producerSchemaFactory; - - @Inject - private PojoProducers pojoProducers; - - public void regsiterInstanceFactory(InstanceFactory instanceFactory) { - instanceFactoryMgr.register(instanceFactory.getImplName(), instanceFactory); - } - - public PojoProducerProvider() { - regsiterInstanceFactory(new PojoInstanceFactory()); - regsiterInstanceFactory(new SpringInstanceFactory()); - } - - @Override - public void init() throws Exception { - for (PojoProducerMeta pojoProducerMeta : pojoProducers.getProcucers()) { - initPojoProducerMeta(pojoProducerMeta); - - Microservice microservice = RegistryUtils.getMicroservice(); - try { - producerSchemaFactory.getOrCreateProducerSchema( - microservice.getServiceName(), - pojoProducerMeta.getSchemaId(), - pojoProducerMeta.getInstanceClass(), - pojoProducerMeta.getInstance()); - } catch (Throwable e) { - throw new IllegalArgumentException( - "create producer schema failed, class=" + pojoProducerMeta.getInstanceClass().getName(), e); - } - } - } - - @Override - public String getName() { - return PojoConst.POJO; - } - - private void initPojoProducerMeta(PojoProducerMeta pojoProducerMeta) { - if (pojoProducerMeta.getInstance() != null) { - return; - } - - String[] nameAndValue = parseImplementation(pojoProducerMeta.getImplementation()); - - InstanceFactory factory = instanceFactoryMgr.ensureFindValue(nameAndValue[0]); - Object instance = factory.create(nameAndValue[1]); - Class instanceClass = BeanUtils.getImplClassFromBean(instance); - - pojoProducerMeta.setInstance(instance); - pojoProducerMeta.setInstanceClass(instanceClass); - } - - private String[] parseImplementation(String implementation) { - String implName = PojoConst.POJO; - String implValue = implementation; - int idx = implementation.indexOf(':'); - if (idx != -1) { - implName = implementation.substring(0, idx); - implValue = implementation.substring(idx + 1); - } - - return new String[] {implName, implValue}; - } -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/RpcReference.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/RpcReference.java deleted file mode 100644 index 9a9ea73606c..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/RpcReference.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -@Documented -@Retention(RUNTIME) -@Target(FIELD) -public @interface RpcReference { - String microserviceName(); - - String schemaId() default ""; -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/RpcSchema.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/RpcSchema.java deleted file mode 100644 index 046aa3b9654..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/RpcSchema.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo; - -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) -@Component -public @interface RpcSchema { - String schemaId() default ""; -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/instance/PojoInstanceFactory.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/instance/PojoInstanceFactory.java deleted file mode 100644 index aa4e387b041..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/instance/PojoInstanceFactory.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo.instance; - -import io.servicecomb.provider.pojo.InstanceFactory; -import io.servicecomb.provider.pojo.PojoConst; - -public class PojoInstanceFactory implements InstanceFactory { - @Override - public String getImplName() { - return PojoConst.POJO; - } - - @Override - public Object create(String className) { - try { - Class clazz = Class.forName(className); - return clazz.newInstance(); - } catch (Exception e) { - throw new Error("Fail to create instance of class:" + className, e); - } - } -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/instance/SpringInstanceFactory.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/instance/SpringInstanceFactory.java deleted file mode 100644 index cfeb3a453dd..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/instance/SpringInstanceFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo.instance; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.provider.pojo.InstanceFactory; -import io.servicecomb.provider.pojo.PojoConst; - -public class SpringInstanceFactory implements InstanceFactory { - @Override - public String getImplName() { - return PojoConst.SPRING; - } - - @Override - public Object create(String beanId) { - Object instance = BeanUtils.getBean(beanId); - if (instance == null) { - throw new Error("Fail to find bean:" + beanId); - } - - return instance; - } -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/reference/PojoReferenceMeta.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/reference/PojoReferenceMeta.java deleted file mode 100644 index 6c76bca07ef..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/reference/PojoReferenceMeta.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo.reference; - -import java.lang.reflect.Proxy; - -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; - -import io.servicecomb.core.provider.CseBeanPostProcessor.EmptyBeanPostProcessor; -import io.servicecomb.foundation.common.exceptions.ServiceCombException; -import io.servicecomb.provider.pojo.Invoker; - -public class PojoReferenceMeta implements FactoryBean, InitializingBean, EmptyBeanPostProcessor { - // 原始数据 - private String microserviceName; - - private String schemaId; - - private Class consumerIntf; - - // 根据intf创建出来的动态代理 - // TODO:未实现本地优先(本地场景下,应该跳过handler机制) - private Object proxy; - - public Object getProxy() { - return getObject(); - } - - @Override - public Object getObject() { - return proxy; - } - - @Override - public Class getObjectType() { - return consumerIntf; - } - - @Override - public boolean isSingleton() { - return true; - } - - public void setConsumerIntf(Class intf) { - this.consumerIntf = intf; - } - - public void setMicroserviceName(String microserviceName) { - this.microserviceName = microserviceName; - } - - public void setSchemaId(String schemaId) { - this.schemaId = schemaId; - } - - @Override - public void afterPropertiesSet() { - if (consumerIntf == null) { - throw new ServiceCombException( - String.format( - "microserviceName=%s, schemaid=%s, \n" - + "do not support implicit interface anymore, \n" - + "because that caused problems:\n" - + " 1.the startup process relies on other microservices\n" - + " 2.cyclic dependent microservices can not be deployed\n" - + "suggest to use @RpcReference or " - + ".", - microserviceName, - schemaId)); - } - - Invoker invoker = new Invoker(microserviceName, schemaId, consumerIntf); - proxy = Proxy.newProxyInstance(consumerIntf.getClassLoader(), new Class[] {consumerIntf}, invoker); - } -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/reference/ReferenceDefParser.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/reference/ReferenceDefParser.java deleted file mode 100644 index 7c9a6842242..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/reference/ReferenceDefParser.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo.reference; - -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; -import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.util.StringUtils; -import org.w3c.dom.Element; - -import io.servicecomb.provider.pojo.PojoConst; -import io.servicecomb.swagger.generator.core.utils.ClassUtils; - -public class ReferenceDefParser extends AbstractSingleBeanDefinitionParser { - @Override - protected Class getBeanClass(Element element) { - return PojoReferenceMeta.class; - } - - @Override - protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { - builder.addPropertyValue(PojoConst.FIELD_MICROSERVICE_NAME, - element.getAttribute(PojoConst.MICROSERVICE_NAME)); - - String schemaId = element.getAttribute(PojoConst.SCHEMA_ID); - String intf = element.getAttribute(PojoConst.INTERFACE); - - if (StringUtils.isEmpty(intf) && !StringUtils.isEmpty(schemaId)) { - // 尝试将schemaId当作接口名使用 - Class consumerIntf = ClassUtils.getClassByName(null, schemaId); - if (consumerIntf != null) { - intf = schemaId; - } - } - - builder.addPropertyValue(PojoConst.FIELD_SCHEMA_ID, schemaId); - builder.addPropertyValue(PojoConst.FIELD_INTERFACE, intf); - - if (StringUtils.isEmpty(schemaId) && StringUtils.isEmpty(intf)) { - throw new Error("schema-id and interface can not both be empty."); - } - } -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/reference/RpcReferenceProcessor.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/reference/RpcReferenceProcessor.java deleted file mode 100644 index 27fb64d8ce5..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/reference/RpcReferenceProcessor.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.provider.pojo.reference; - -import java.lang.reflect.Field; - -import org.springframework.context.ApplicationContext; -import org.springframework.context.EmbeddedValueResolverAware; -import org.springframework.stereotype.Component; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringValueResolver; - -import io.servicecomb.core.provider.CseBeanPostProcessor.ConsumerFieldProcessor; -import io.servicecomb.provider.pojo.RpcReference; - -@Component -public class RpcReferenceProcessor implements ConsumerFieldProcessor, EmbeddedValueResolverAware { - private StringValueResolver resolver; - - @Override - public void processConsumerField(ApplicationContext applicationContext, Object bean, Field field) { - RpcReference reference = field.getAnnotation(RpcReference.class); - if (reference == null) { - return; - } - - handleReferenceField(bean, field, reference); - } - - @Override - public void setEmbeddedValueResolver(StringValueResolver resolver) { - this.resolver = resolver; - } - - private void handleReferenceField(Object obj, Field field, - RpcReference reference) { - String microserviceName = reference.microserviceName(); - microserviceName = resolver.resolveStringValue(microserviceName); - - PojoReferenceMeta pojoReference = new PojoReferenceMeta(); - pojoReference.setMicroserviceName(microserviceName); - pojoReference.setSchemaId(reference.schemaId()); - pojoReference.setConsumerIntf(field.getType()); - - pojoReference.afterPropertiesSet(); - - ReflectionUtils.makeAccessible(field); - ReflectionUtils.setField(field, obj, pojoReference.getProxy()); - } -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/schema/PojoProducerMeta.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/schema/PojoProducerMeta.java deleted file mode 100644 index 0ef5dfe6114..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/schema/PojoProducerMeta.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo.schema; - -import javax.inject.Inject; - -import org.springframework.beans.factory.InitializingBean; - -import io.servicecomb.core.provider.producer.ProducerMeta; - -public class PojoProducerMeta extends ProducerMeta implements InitializingBean { - @Inject - protected PojoProducers pojoProducers; - - private String implementation; - - public String getImplementation() { - return implementation; - } - - public void setImplementation(String implementation) { - this.implementation = implementation; - } - - @Override - public void afterPropertiesSet() throws Exception { - pojoProducers.registerPojoProducer(this); - } -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/schema/PojoProducers.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/schema/PojoProducers.java deleted file mode 100644 index 33ea2e979f5..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/schema/PojoProducers.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.provider.pojo.schema; - -import java.util.Collection; - -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - -import io.servicecomb.core.provider.CseBeanPostProcessor.ProviderProcessor; -import io.servicecomb.foundation.common.RegisterManager; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.provider.pojo.RpcSchema; - -@Component -public class PojoProducers implements ProviderProcessor { - // key为schemaId - private RegisterManager pojoMgr = new RegisterManager<>("pojo service manager"); - - public void registerPojoProducer(PojoProducerMeta pojoProducer) { - pojoMgr.register(pojoProducer.getSchemaId(), pojoProducer); - } - - public Collection getProcucers() { - return pojoMgr.values(); - } - - @Override - public void processProvider(ApplicationContext applicationContext, String beanName, Object bean) { - // aop后,新的实例的父类可能是原class,也可能只是个proxy,父类不是原class - // 所以,需要先取出原class,再取标注 - Class beanCls = BeanUtils.getImplClassFromBean(bean); - RpcSchema rpcSchema = beanCls.getAnnotation(RpcSchema.class); - if (rpcSchema == null) { - return; - } - - String schemaId = rpcSchema.schemaId(); - if (StringUtils.isEmpty(schemaId)) { - Class[] intfs = beanCls.getInterfaces(); - if (intfs.length == 1) { - schemaId = intfs[0].getName(); - } else { - throw new Error("Must be schemaId or implements only one interface"); - } - } - - PojoProducerMeta pojoProducerMeta = new PojoProducerMeta(); - pojoProducerMeta.setSchemaId(schemaId); - pojoProducerMeta.setInstance(bean); - pojoProducerMeta.setInstanceClass(beanCls); - - registerPojoProducer(pojoProducerMeta); - } -} diff --git a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/schema/SchemaDefParser.java b/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/schema/SchemaDefParser.java deleted file mode 100644 index efe05c6f780..00000000000 --- a/providers/provider-pojo/src/main/java/io/servicecomb/provider/pojo/schema/SchemaDefParser.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.pojo.schema; - -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; -import org.springframework.beans.factory.xml.ParserContext; -import org.w3c.dom.Element; - -import io.servicecomb.provider.pojo.PojoConst; - -public class SchemaDefParser extends AbstractSingleBeanDefinitionParser { - @Override - protected boolean shouldGenerateId() { - return true; - } - - @Override - protected boolean shouldParseNameAsAliases() { - return false; - } - - @Override - protected Class getBeanClass(Element element) { - return PojoProducerMeta.class; - } - - @Override - protected void doParse(Element element, ParserContext parserContext, - BeanDefinitionBuilder builder) { - builder.addPropertyValue(PojoConst.FIELD_SCHEMA_ID, element.getAttribute(PojoConst.SCHEMA_ID)); - builder.addPropertyValue(PojoConst.IMPL, element.getAttribute(PojoConst.IMPL)); - } -} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/DefaultMethodMeta.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/DefaultMethodMeta.java new file mode 100644 index 00000000000..87e6efbc1d3 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/DefaultMethodMeta.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.provider.pojo; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.utils.AsyncUtils; +import org.springframework.util.ReflectionUtils; + +public class DefaultMethodMeta { + private final Map defaultMethodHandles = new ConcurrentHashMapEx<>(); + + // java11 + private final Method privateLookupIn = ReflectionUtils.findMethod(MethodHandles.class, + "privateLookupIn", Class.class, Lookup.class); + + // only for java8 + private Constructor lookupConstructor; + + public MethodHandle getOrCreateMethodHandle(Object proxy, Method method) { + return defaultMethodHandles.computeIfAbsent(method, key -> createMethodHandle(proxy, method)); + } + + protected MethodHandle createMethodHandle(Object proxy, Method method) { + try { + if (privateLookupIn != null) { + return createForJava11(proxy, method); + } + + return createForJava8(proxy, method); + } catch (Exception e) { + throw AsyncUtils.rethrow(e); + } + } + + protected MethodHandle createForJava11(Object proxy, Method method) throws Exception { + Lookup lookup = MethodHandles.lookup(); + Lookup privateLookup = (Lookup) privateLookupIn.invoke(null, method.getDeclaringClass(), lookup); + return privateLookup + .unreflectSpecial(method, method.getDeclaringClass()) + .bindTo(proxy); + } + + protected MethodHandle createForJava8(Object proxy, Method method) throws Exception { + if (lookupConstructor == null) { + lookupConstructor = ReflectionUtils.accessibleConstructor(Lookup.class, Class.class); + } + return lookupConstructor.newInstance(method.getDeclaringClass()) + .unreflectSpecial(method, method.getDeclaringClass()) + .bindTo(proxy); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/FilterInvocationCaller.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/FilterInvocationCaller.java new file mode 100644 index 00000000000..ef8829bee30 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/FilterInvocationCaller.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.provider.pojo; + +import static org.apache.servicecomb.core.provider.consumer.InvokerUtils.isAsyncMethod; + +import java.lang.reflect.Method; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.provider.consumer.InvokerUtils; +import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; + +public class FilterInvocationCaller implements InvocationCaller { + // if not a sync method, should never throw exception directly + @Override + public Object call(Method method, PojoConsumerMetaRefresher metaRefresher, PojoInvocationCreator invocationCreator, + Object[] args) { + if (isAsyncMethod(method)) { + CompletableFuture invocationFuture = invocationCreator.createAsync(method, metaRefresher, args); + return invocationFuture.thenCompose(this::doCall); + } + PojoInvocation invocation = invocationCreator.create(method, metaRefresher, args); + CompletableFuture future = CompletableFuture.completedFuture(invocation) + .thenCompose(this::doCall); + return InvokerUtils.toSync(future, invocation.getWaitTime()); + } + + protected CompletableFuture doCall(PojoInvocation invocation) { + return InvokerUtils.invoke(invocation) + .thenApply(response -> { + if (response.isSucceed()) { + return invocation.convertResponse(response); + } + throw ExceptionFactory.convertConsumerException(response.getResult()); + }); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/InstanceFactory.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/InstanceFactory.java new file mode 100644 index 00000000000..3fbb22b166c --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/InstanceFactory.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.provider.pojo; + +public interface InstanceFactory { + String getImplName(); + + Object create(String implValue); +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/InvocationCaller.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/InvocationCaller.java new file mode 100644 index 00000000000..79b1e9f80b5 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/InvocationCaller.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.provider.pojo; + +import java.lang.reflect.Method; + +public interface InvocationCaller { + Object call(Method method, PojoConsumerMetaRefresher metaRefresher, PojoInvocationCreator invocationCreator, + Object[] args); +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/Invoker.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/Invoker.java new file mode 100644 index 00000000000..ac0be460210 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/Invoker.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.provider.pojo; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import org.apache.servicecomb.core.SCBEngine; + +public class Invoker implements InvocationHandler { + public static final Method METHOD_HASH_CODE; + + public static final Method METHOD_EQUALS; + + public static final Method METHOD_TO_STRING; + + static { + try { + METHOD_HASH_CODE = Object.class.getDeclaredMethod("hashCode"); + METHOD_EQUALS = Object.class.getDeclaredMethod("equals", Object.class); + METHOD_TO_STRING = Object.class.getDeclaredMethod("toString"); + } catch (NoSuchMethodException e) { + throw new Error(e); + } + } + + protected final PojoConsumerMetaRefresher metaRefresher; + + protected final PojoInvocationCreator invocationCreator; + + protected final DefaultMethodMeta defaultMethodMeta = new DefaultMethodMeta(); + + protected InvocationCaller invocationCaller; + + @SuppressWarnings("unchecked") + public static T createProxy(String microserviceName, String schemaId, Class consumerIntf) { + Invoker invoker = new Invoker(microserviceName, schemaId, consumerIntf); + return (T) Proxy.newProxyInstance(consumerIntf.getClassLoader(), new Class[] {consumerIntf}, invoker); + } + + public Invoker(String microserviceName, String schemaId, Class consumerIntf) { + this.metaRefresher = createInvokerMeta(microserviceName, schemaId, consumerIntf); + this.invocationCreator = createInvocationCreator(); + } + + protected PojoConsumerMetaRefresher createInvokerMeta(String microserviceName, String schemaId, + Class consumerIntf) { + return new PojoConsumerMetaRefresher(microserviceName, schemaId, consumerIntf); + } + + public PojoInvocationCreator createInvocationCreator() { + return new PojoInvocationCreator(); + } + + protected InvocationCaller createInvocationCaller() { + return new FilterInvocationCaller(); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.isDefault()) { + return defaultMethodMeta.getOrCreateMethodHandle(proxy, method) + .invokeWithArguments(args); + } + if (METHOD_HASH_CODE.equals(method)) { + return objectHashCode(proxy); + } + if (METHOD_EQUALS.equals(method)) { + return objectEquals(proxy, args[0]); + } + if (METHOD_TO_STRING.equals(method)) { + return objectToString(proxy); + } + + SCBEngine.getInstance().ensureStatusUp(); + prepareInvocationCaller(); + return invocationCaller.call(method, metaRefresher, invocationCreator, args); + } + + protected void prepareInvocationCaller() { + if (invocationCaller != null) { + return; + } + + this.invocationCaller = createInvocationCaller(); + } + + private String objectClassName(Object obj) { + return obj.getClass().getName(); + } + + private int objectHashCode(Object obj) { + return System.identityHashCode(obj); + } + + private boolean objectEquals(Object obj, Object other) { + return obj == other; + } + + private String objectToString(Object obj) { + return objectClassName(obj) + '@' + Integer.toHexString(objectHashCode(obj)); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoConst.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoConst.java new file mode 100644 index 00000000000..90030f91152 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoConst.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.provider.pojo; + +public final class PojoConst { + private PojoConst() { + } + + public static final String FIELD_SCHEMA_ID = "schemaId"; + + public static final String SCHEMA_ID = "schema-id"; + + public static final String FIELD_SCHEMA_INTERFACE = "schemaInterfaceName"; + + public static final String SCHEMA_INTERFACE = "schema-interface"; + + public static final String IMPL = "implementation"; + + public static final String FIELD_MICROSERVICE_NAME = "microserviceName"; + + public static final String MICROSERVICE_NAME = "microservice-name"; + + public static final String FIELD_INTERFACE = "consumerIntf"; + + public static final String INTERFACE = "interface"; + + public static final String POJO = "pojo"; + + public static final String SPRING = "spring"; +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoConsumerMetaRefresher.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoConsumerMetaRefresher.java new file mode 100644 index 00000000000..46350d40c5e --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoConsumerMetaRefresher.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.provider.pojo; + +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.core.definition.SchemaMeta; +import org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig; +import org.apache.servicecomb.provider.pojo.definition.PojoConsumerMeta; +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +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 class PojoConsumerMetaRefresher { + private static final Logger LOGGER = LoggerFactory.getLogger(PojoConsumerMetaRefresher.class); + + protected final String microserviceName; + + // can be null, should find SchemaMeta by consumerIntf in this time + protected final String schemaId; + + protected final Class consumerIntf; + + protected SCBEngine scbEngine; + + // not always equals codec meta + // for highway, codec meta is relate to target instance + // to avoid limit producer to only allow append parameter + protected PojoConsumerMeta consumerMeta; + + public PojoConsumerMetaRefresher(String microserviceName, String schemaId, Class consumerIntf) { + this.microserviceName = microserviceName; + this.schemaId = schemaId; + this.consumerIntf = consumerIntf; + } + + public PojoConsumerMeta getLatestMeta() { + ensureStatusUp(); + ensureMetaAvailable(); + return consumerMeta; + } + + public CompletableFuture getLatestMetaAsync() { + ensureStatusUp(); + return ensureMetaAvailableAsync().thenCompose(v -> CompletableFuture.completedFuture(consumerMeta)); + } + + private void ensureStatusUp() { + if (scbEngine == null) { + if (SCBEngine.getInstance() == null) { + String message = + "The request is rejected. Cannot process the request due to SCBEngine not ready."; + LOGGER.warn(message); + throw new InvocationException(Status.SERVICE_UNAVAILABLE, new CommonExceptionData(message)); + } + + this.scbEngine = SCBEngine.getInstance(); + } + + scbEngine.ensureStatusUp(); + } + + private void ensureMetaAvailable() { + if (isNeedRefresh()) { + synchronized (this) { + if (isNeedRefresh()) { + this.consumerMeta = refreshMeta(); + } + } + } + } + + private CompletableFuture ensureMetaAvailableAsync() { + if (isNeedRefresh()) { + return refreshMetaAsync(); + } + return CompletableFuture.completedFuture(null); + } + + private boolean isNeedRefresh() { + return consumerMeta == null; + } + + protected PojoConsumerMeta refreshMeta() { + MicroserviceReferenceConfig microserviceReferenceConfig = scbEngine + .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 = findSchemaMeta(microserviceMeta); + if (schemaMeta == null) { + throw new IllegalStateException( + String.format( + "Schema not exist, microserviceName=%s, schemaId=%s, consumer interface=%s; " + + "new producer not running or not deployed.", + microserviceName, + schemaId, + consumerIntf.getName())); + } + + SwaggerConsumer swaggerConsumer = scbEngine.getSwaggerEnvironment() + .createConsumer(consumerIntf, schemaMeta.getSwagger()); + return new PojoConsumerMeta(microserviceReferenceConfig, swaggerConsumer, schemaMeta); + } + + protected CompletableFuture refreshMetaAsync() { + CompletableFuture microserviceReferenceConfigFuture = scbEngine + .getOrCreateReferenceConfigAsync(microserviceName); + return microserviceReferenceConfigFuture.thenCompose((microserviceReferenceConfig) -> { + if (microserviceReferenceConfig == null) { + return CompletableFuture.failedFuture(new InvocationException(Status.INTERNAL_SERVER_ERROR, + new CommonExceptionData(String.format("Failed to invoke service %s. Maybe service" + + " not registered or no active instance.", microserviceName)))); + } + synchronized (this) { + if (isNeedRefresh()) { + MicroserviceMeta microserviceMeta = microserviceReferenceConfig.getMicroserviceMeta(); + + SchemaMeta schemaMeta = findSchemaMeta(microserviceMeta); + if (schemaMeta == null) { + return CompletableFuture.failedFuture(new IllegalStateException( + String.format( + "Schema not exist, microserviceName=%s, schemaId=%s, consumer interface=%s; " + + "new producer not running or not deployed.", + microserviceName, + schemaId, + consumerIntf.getName()))); + } + + SwaggerConsumer swaggerConsumer = scbEngine.getSwaggerEnvironment() + .createConsumer(consumerIntf, schemaMeta.getSwagger()); + consumerMeta = new PojoConsumerMeta(microserviceReferenceConfig, swaggerConsumer, schemaMeta); + } + return CompletableFuture.completedFuture(null); + } + }); + } + + private SchemaMeta findSchemaMeta(MicroserviceMeta microserviceMeta) { + // if present schemaId, just use it + if (StringUtils.isNotEmpty(schemaId)) { + return microserviceMeta.findSchemaMeta(schemaId); + } + + // try interface name second + return microserviceMeta.findSchemaMeta(consumerIntf.getName()); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoInvocation.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoInvocation.java new file mode 100644 index 00000000000..c0d7b9b8af3 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoInvocation.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.provider.pojo; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.invocation.InvocationFactory; +import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; +import org.apache.servicecomb.provider.pojo.definition.PojoConsumerOperationMeta; +import org.apache.servicecomb.swagger.invocation.Response; + +public class PojoInvocation extends Invocation { + protected final PojoConsumerOperationMeta consumerOperationMeta; + + public PojoInvocation(PojoConsumerOperationMeta consumerOperationMeta) { + super(consumerOperationMeta.createReferenceConfig(), + consumerOperationMeta.getOperationMeta(), + consumerOperationMeta.getInvocationRuntimeType(), + null); + this.consumerOperationMeta = consumerOperationMeta; + InvocationFactory.setSrcMicroservice(this); + } + + public ReferenceConfig getReferenceConfig() { + return referenceConfig; + } + + public Object convertResponse(Response response) { + return consumerOperationMeta.getSwaggerConsumerOperation().getResponseMapper().mapResponse(response); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoInvocationCreator.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoInvocationCreator.java new file mode 100644 index 00000000000..54dd320fe68 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoInvocationCreator.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.provider.pojo; + +import java.lang.reflect.Method; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.provider.pojo.definition.PojoConsumerMeta; +import org.apache.servicecomb.provider.pojo.definition.PojoConsumerOperationMeta; + +public class PojoInvocationCreator { + public PojoInvocation create(Method method, PojoConsumerMetaRefresher metaRefresher, Object[] args) { + long startCreateInvocation = System.nanoTime(); + PojoConsumerMeta pojoConsumerMeta = metaRefresher.getLatestMeta(); + PojoConsumerOperationMeta consumerOperationMeta = pojoConsumerMeta.ensureFindOperationMeta(method); + + PojoInvocation invocation = new PojoInvocation(consumerOperationMeta); + invocation.setSuccessResponseType(consumerOperationMeta.getResponsesType()); + invocation.setInvocationArguments(consumerOperationMeta.getSwaggerConsumerOperation().toInvocationArguments(args)); + invocation.setSync(consumerOperationMeta.isSync()); + invocation.getInvocationStageTrace().startCreateInvocation(startCreateInvocation); + invocation.getInvocationStageTrace().finishCreateInvocation(); + return invocation; + } + + public CompletableFuture createAsync(Method method, PojoConsumerMetaRefresher metaRefresher, + Object[] args) { + CompletableFuture pojoConsumerMetaFuture = metaRefresher.getLatestMetaAsync(); + return pojoConsumerMetaFuture.thenCompose(pojoConsumerMeta -> { + PojoConsumerOperationMeta consumerOperationMeta = pojoConsumerMeta.ensureFindOperationMeta(method); + + PojoInvocation invocation = new PojoInvocation(consumerOperationMeta); + invocation.setSuccessResponseType(consumerOperationMeta.getResponsesType()); + invocation.setInvocationArguments( + consumerOperationMeta.getSwaggerConsumerOperation().toInvocationArguments(args)); + invocation.setSync(consumerOperationMeta.isSync()); + + return CompletableFuture.completedFuture(invocation); + }); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoProducerProvider.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoProducerProvider.java new file mode 100644 index 00000000000..b63c065618a --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoProducerProvider.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.provider.pojo; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.provider.producer.AbstractProducerProvider; +import org.apache.servicecomb.core.provider.producer.ProducerMeta; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.apache.servicecomb.provider.pojo.instance.PojoInstanceFactory; +import org.apache.servicecomb.provider.pojo.instance.SpringInstanceFactory; +import org.apache.servicecomb.provider.pojo.schema.PojoProducerMeta; +import org.apache.servicecomb.provider.pojo.schema.PojoProducers; + +public class PojoProducerProvider extends AbstractProducerProvider { + private final Map instanceFactoryMgr = new HashMap<>(); + + private void registerInstanceFactory(InstanceFactory instanceFactory) { + instanceFactoryMgr.put(instanceFactory.getImplName(), instanceFactory); + } + + public PojoProducerProvider() { + registerInstanceFactory(new PojoInstanceFactory()); + registerInstanceFactory(new SpringInstanceFactory()); + } + + @Override + public List init() { + // for some test cases, there is no spring context + if (BeanUtils.getContext() == null) { + return Collections.emptyList(); + } + + PojoProducers pojoProducers = BeanUtils.getContext().getBean(PojoProducers.class); + for (ProducerMeta producerMeta : pojoProducers.getProducerMetas()) { + PojoProducerMeta pojoProducerMeta = (PojoProducerMeta) producerMeta; + initPojoProducerMeta(pojoProducerMeta); + } + + return pojoProducers.getProducerMetas(); + } + + @Override + public String getName() { + return PojoConst.POJO; + } + + private void initPojoProducerMeta(PojoProducerMeta pojoProducerMeta) { + parseSchemaInterface(pojoProducerMeta); + parseImplementation(pojoProducerMeta); + } + + private void parseImplementation(PojoProducerMeta pojoProducerMeta) { + if (pojoProducerMeta.getInstance() != null) { + return; + } + + String implementation = pojoProducerMeta.getImplementation(); + String implName = PojoConst.POJO; + String implValue = pojoProducerMeta.getImplementation(); + int idx = implementation.indexOf(':'); + if (idx != -1) { + implName = implementation.substring(0, idx); + implValue = implementation.substring(idx + 1); + } + + InstanceFactory factory = instanceFactoryMgr.get(implName); + if (factory == null) { + throw new IllegalStateException("failed to find instance factory, name=" + implName); + } + + Object instance = factory.create(implValue); + pojoProducerMeta.setInstance(instance); + } + + private void parseSchemaInterface(PojoProducerMeta pojoProducerMeta) { + if (pojoProducerMeta.getSchemaInterface() != null || StringUtils + .isEmpty(pojoProducerMeta.getSchemaInterfaceName())) { + return; + } + + try { + Class si = Class.forName(pojoProducerMeta.getSchemaInterfaceName()); + pojoProducerMeta.setSchemaInterface(si); + } catch (Exception e) { + throw new Error("can not find schema interface " + pojoProducerMeta.getSchemaInterfaceName(), e); + } + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/ProviderPojoConfiguration.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/ProviderPojoConfiguration.java new file mode 100644 index 00000000000..81bf83a756d --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/ProviderPojoConfiguration.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.provider.pojo; + +import org.apache.servicecomb.provider.pojo.schema.PojoProducers; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ProviderPojoConfiguration { + @Bean + public static RpcReferenceBeanDefinitionRegistry rpcReferenceBeanDefinitionRegistry() { + return new RpcReferenceBeanDefinitionRegistry(); + } + + @Bean + public static PojoProducers pojoProducers() { + return new PojoProducers(); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcReference.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcReference.java new file mode 100644 index 00000000000..a800b3f24ed --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcReference.java @@ -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. + */ + +package org.apache.servicecomb.provider.pojo; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Documented +@Retention(RUNTIME) +@Target({FIELD, METHOD, ANNOTATION_TYPE}) +public @interface RpcReference { + String microserviceName(); + + String schemaId() default ""; +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcReferenceBeanDefinitionRegistry.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcReferenceBeanDefinitionRegistry.java new file mode 100644 index 00000000000..32e10077131 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcReferenceBeanDefinitionRegistry.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.provider.pojo; + +import org.apache.servicecomb.provider.pojo.reference.RpcReferenceProcessor; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; + +/** + * In order to support FactoryBean eager inject @RpcReference, add RpcReferenceProcessor + * before FactoryBean is initialized. + */ +public class RpcReferenceBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor { + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { + + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + beanFactory.addBeanPostProcessor(new RpcReferenceProcessor(beanFactory)); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcSchema.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcSchema.java new file mode 100644 index 00000000000..f9d9fbc1109 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcSchema.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.provider.pojo; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +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, ANNOTATION_TYPE}) +@Component +public @interface RpcSchema { + String schemaId() default ""; + + Class schemaInterface() default Object.class; +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/definition/PojoConsumerMeta.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/definition/PojoConsumerMeta.java new file mode 100644 index 00000000000..1de414759d8 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/definition/PojoConsumerMeta.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.provider.pojo.definition; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +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.MicroserviceReferenceConfig; +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +import jakarta.ws.rs.core.Response.Status; + +public class PojoConsumerMeta { + private final MicroserviceReferenceConfig microserviceReferenceConfig; + + private final SchemaMeta schemaMeta; + + private final Map operationMetas = new HashMap<>(); + + public PojoConsumerMeta(MicroserviceReferenceConfig microserviceReferenceConfig, SwaggerConsumer swaggerConsumer, + SchemaMeta schemaMeta) { + this.microserviceReferenceConfig = microserviceReferenceConfig; + this.schemaMeta = schemaMeta; + + for (SwaggerConsumerOperation swaggerConsumerOperation : swaggerConsumer.getOperations().values()) { + String operationId = swaggerConsumerOperation.getSwaggerOperation().getOperationId(); + // SwaggerConsumer has make sure can find operationMeta + OperationMeta operationMeta = schemaMeta.ensureFindOperation(operationId); + PojoConsumerOperationMeta pojoConsumerOperationMeta = new PojoConsumerOperationMeta(this, operationMeta, + swaggerConsumerOperation); + + operationMetas.put(swaggerConsumerOperation.getConsumerMethod(), pojoConsumerOperationMeta); + } + } + + public MicroserviceReferenceConfig getMicroserviceReferenceConfig() { + return microserviceReferenceConfig; + } + + public MicroserviceMeta getMicroserviceMeta() { + return schemaMeta.getMicroserviceMeta(); + } + + public SchemaMeta getSchemaMeta() { + return schemaMeta; + } + + public PojoConsumerOperationMeta ensureFindOperationMeta(Method method) { + PojoConsumerOperationMeta pojoConsumerOperationMeta = operationMetas.get(method); + if (pojoConsumerOperationMeta == null) { + throw new InvocationException(Status.INTERNAL_SERVER_ERROR, + String.format( + "Consumer method %s:%s not exist in contract, microserviceName=%s, schemaId=%s.", + method.getDeclaringClass().getName(), + method.getName(), + schemaMeta.getMicroserviceName(), + schemaMeta.getSchemaId())); + } + return pojoConsumerOperationMeta; + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/definition/PojoConsumerOperationMeta.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/definition/PojoConsumerOperationMeta.java new file mode 100644 index 00000000000..1d5bf5fed93 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/definition/PojoConsumerOperationMeta.java @@ -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. + */ +package org.apache.servicecomb.provider.pojo.definition; + +import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.findResponseTypeProcessor; + +import java.lang.reflect.Type; + +import jakarta.servlet.http.Part; + +import org.apache.servicecomb.core.definition.InvocationRuntimeType; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.provider.consumer.InvokerUtils; +import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; +import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.google.common.reflect.TypeToken; + +public class PojoConsumerOperationMeta { + private final PojoConsumerMeta pojoConsumerMeta; + + private final OperationMeta operationMeta; + + private final SwaggerConsumerOperation swaggerConsumerOperation; + + private JavaType responseType; + + private InvocationRuntimeType invocationRuntimeType; + + private final boolean sync; + + public PojoConsumerOperationMeta(PojoConsumerMeta pojoConsumerMeta, OperationMeta operationMeta, + SwaggerConsumerOperation swaggerConsumerOperation) { + this.pojoConsumerMeta = pojoConsumerMeta; + this.operationMeta = operationMeta; + this.swaggerConsumerOperation = swaggerConsumerOperation; + this.sync = InvokerUtils.isSyncMethod(swaggerConsumerOperation.getConsumerMethod()); + initResponseType(); + initRuntimeType(); + } + + private void initRuntimeType() { + invocationRuntimeType = operationMeta.buildBaseConsumerRuntimeType(); + invocationRuntimeType.setArgumentsMapper(swaggerConsumerOperation.getArgumentsMapper()); + invocationRuntimeType.setAssociatedClass(swaggerConsumerOperation.getConsumerClass()); + invocationRuntimeType.setAssociatedMethod(swaggerConsumerOperation.getConsumerMethod()); + } + + public PojoConsumerMeta getPojoConsumerMeta() { + return pojoConsumerMeta; + } + + public OperationMeta getOperationMeta() { + return operationMeta; + } + + public SwaggerConsumerOperation getSwaggerConsumerOperation() { + return swaggerConsumerOperation; + } + + public JavaType getResponsesType() { + return responseType; + } + + public InvocationRuntimeType getInvocationRuntimeType() { + return invocationRuntimeType; + } + + public ReferenceConfig createReferenceConfig() { + return pojoConsumerMeta.getMicroserviceReferenceConfig() + .createReferenceConfig(operationMeta); + } + + private void initResponseType() { + Type intfResponseType = + TypeToken.of(swaggerConsumerOperation.getConsumerClass()) + .resolveType(swaggerConsumerOperation.getConsumerMethod().getGenericReturnType()) + .getType(); + if (intfResponseType instanceof Class && Part.class.isAssignableFrom((Class) intfResponseType)) { + responseType = TypeFactory.defaultInstance().constructType(Part.class); + return; + } + + intfResponseType = findResponseTypeProcessor(intfResponseType).extractResponseType(intfResponseType); + if (intfResponseType != null) { + responseType = TypeFactory.defaultInstance().constructType(intfResponseType); + } + } + + public boolean isSync() { + return sync; + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/instance/PojoInstanceFactory.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/instance/PojoInstanceFactory.java new file mode 100644 index 00000000000..291d0cdc90a --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/instance/PojoInstanceFactory.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.provider.pojo.instance; + +import org.apache.servicecomb.provider.pojo.InstanceFactory; +import org.apache.servicecomb.provider.pojo.PojoConst; + +public class PojoInstanceFactory implements InstanceFactory { + @Override + public String getImplName() { + return PojoConst.POJO; + } + + @Override + public Object create(String className) { + try { + Class clazz = Class.forName(className); + return clazz.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new Error("Fail to create instance of class:" + className, e); + } + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/instance/SpringInstanceFactory.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/instance/SpringInstanceFactory.java new file mode 100644 index 00000000000..45a9f950670 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/instance/SpringInstanceFactory.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.provider.pojo.instance; + +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.apache.servicecomb.provider.pojo.InstanceFactory; +import org.apache.servicecomb.provider.pojo.PojoConst; + +public class SpringInstanceFactory implements InstanceFactory { + @Override + public String getImplName() { + return PojoConst.SPRING; + } + + @Override + public Object create(String beanId) { + Object instance = BeanUtils.getBean(beanId); + if (instance == null) { + throw new Error("Fail to find bean:" + beanId); + } + + return instance; + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/PojoReferenceMeta.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/PojoReferenceMeta.java new file mode 100644 index 00000000000..6abb8df9d9e --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/PojoReferenceMeta.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.provider.pojo.reference; + +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.apache.servicecomb.provider.pojo.Invoker; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; + +public class PojoReferenceMeta implements FactoryBean, InitializingBean { + // 原始数据 + private String microserviceName; + + private String schemaId; + + private Class consumerIntf; + + // 根据intf创建出来的动态代理 + // TODO:未实现本地优先(本地场景下,应该跳过handler机制) + private Object proxy; + + public Object getProxy() { + return getObject(); + } + + @Override + public Object getObject() { + return proxy; + } + + @Override + public Class getObjectType() { + return consumerIntf; + } + + @Override + public boolean isSingleton() { + return true; + } + + public void setConsumerIntf(Class intf) { + this.consumerIntf = intf; + } + + public void setMicroserviceName(String microserviceName) { + this.microserviceName = microserviceName; + } + + public void setSchemaId(String schemaId) { + this.schemaId = schemaId; + } + + @Override + public void afterPropertiesSet() { + if (consumerIntf == null) { + throw new ServiceCombException( + String.format( + "microserviceName=%s, schemaid=%s, \n" + + "do not support implicit interface anymore, \n" + + "because that caused problems:\n" + + " 1.the startup process relies on other microservices\n" + + " 2.cyclic dependent microservices can not be deployed\n" + + "suggest to use @RpcReference or " + + ".", + microserviceName, + schemaId)); + } + + proxy = Invoker.createProxy(microserviceName, schemaId, consumerIntf); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/ReferenceDefParser.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/ReferenceDefParser.java new file mode 100644 index 00000000000..3884b49145b --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/ReferenceDefParser.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.provider.pojo.reference; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.common.utils.ReflectUtils; +import org.apache.servicecomb.provider.pojo.PojoConst; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +public class ReferenceDefParser extends AbstractSingleBeanDefinitionParser { + @Override + protected Class getBeanClass(Element element) { + return PojoReferenceMeta.class; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + builder.addPropertyValue(PojoConst.FIELD_MICROSERVICE_NAME, + element.getAttribute(PojoConst.MICROSERVICE_NAME)); + + String schemaId = element.getAttribute(PojoConst.SCHEMA_ID); + String intf = element.getAttribute(PojoConst.INTERFACE); + + if (StringUtils.isEmpty(intf) && StringUtils.isNotEmpty(schemaId)) { + // 尝试将schemaId当作接口名使用 + Class consumerIntf = ReflectUtils.getClassByName(schemaId); + if (consumerIntf != null) { + intf = schemaId; + } + } + + builder.addPropertyValue(PojoConst.FIELD_SCHEMA_ID, schemaId); + builder.addPropertyValue(PojoConst.FIELD_INTERFACE, intf); + + if (StringUtils.isEmpty(schemaId) && StringUtils.isEmpty(intf)) { + throw new Error("schema-id and interface can not both be empty."); + } + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/RpcReferenceProcessor.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/RpcReferenceProcessor.java new file mode 100644 index 00000000000..ba5ded3f12c --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/RpcReferenceProcessor.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.provider.pojo.reference; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.util.ReflectionUtils; + +public class RpcReferenceProcessor implements BeanPostProcessor { + private final ConfigurableBeanFactory beanFactory; + + public RpcReferenceProcessor(ConfigurableBeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + // 扫描所有field,处理扩展的field标注 + ReflectionUtils.doWithFields(bean.getClass(), field -> processConsumerField(bean, field)); + ReflectionUtils.doWithMethods(bean.getClass(), method -> processConsumerMethod(bean, beanName, method)); + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + protected void processConsumerMethod(Object bean, String beanName, Method method) throws BeansException { + RpcReference reference = method.getAnnotation(RpcReference.class); + if (reference == null) { + return; + } + handleReferenceMethod(bean, beanName, method, reference); + } + + protected void processConsumerField(Object bean, Field field) { + RpcReference reference = field.getAnnotation(RpcReference.class); + if (reference == null) { + return; + } + + handleReferenceField(bean, field, reference); + } + + private void handleReferenceMethod(Object bean, String beanName, Method method, RpcReference reference) + throws BeansException { + try { + PojoReferenceMeta pojoReference = createPojoReferenceMeta(reference, method.getParameterTypes()[0]); + method.invoke(bean, pojoReference.getProxy()); + } catch (Exception e) { + throw new BeanCreationException(beanName, "", e); + } + } + + private void handleReferenceField(Object obj, Field field, + RpcReference reference) { + PojoReferenceMeta pojoReference = createPojoReferenceMeta(reference, field.getType()); + ReflectionUtils.makeAccessible(field); + ReflectionUtils.setField(field, obj, pojoReference.getProxy()); + } + + private PojoReferenceMeta createPojoReferenceMeta(RpcReference reference, Class consumerInterface) { + String microserviceName = reference.microserviceName(); + microserviceName = beanFactory.resolveEmbeddedValue(microserviceName); + + PojoReferenceMeta pojoReference = new PojoReferenceMeta(); + pojoReference.setMicroserviceName(microserviceName); + pojoReference.setSchemaId(reference.schemaId()); + pojoReference.setConsumerIntf(consumerInterface); + pojoReference.afterPropertiesSet(); + return pojoReference; + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/PojoProducerMeta.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/PojoProducerMeta.java new file mode 100644 index 00000000000..d63541cdfe3 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/PojoProducerMeta.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.provider.pojo.schema; + +import org.apache.servicecomb.core.provider.producer.ProducerMeta; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; + +import com.google.common.annotations.VisibleForTesting; + +public class PojoProducerMeta extends ProducerMeta implements InitializingBean { + @Autowired + protected PojoProducers pojoProducers; + + private String implementation; + + private String schemaInterfaceName; + + @VisibleForTesting + void setPojoProducers(PojoProducers pojoProducers) { + this.pojoProducers = pojoProducers; + } + + public String getImplementation() { + return implementation; + } + + public void setImplementation(String implementation) { + this.implementation = implementation; + } + + public String getSchemaInterfaceName() { + return schemaInterfaceName; + } + + public PojoProducerMeta setSchemaInterfaceName(String schemaInterfaceName) { + this.schemaInterfaceName = schemaInterfaceName; + return this; + } + + @Override + public void afterPropertiesSet() { + pojoProducers.registerPojoProducer(this); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/PojoProducers.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/PojoProducers.java new file mode 100644 index 00000000000..a6a5a818f0f --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/PojoProducers.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.provider.pojo.schema; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.provider.producer.ProducerMeta; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.apache.servicecomb.provider.pojo.RpcSchema; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; + +public class PojoProducers implements BeanPostProcessor { + private final List producerMetas = new ArrayList<>(); + + public synchronized void registerPojoProducer(PojoProducerMeta pojoProducer) { + producerMetas.add(pojoProducer); + } + + public List getProducerMetas() { + return producerMetas; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + processProvider(beanName, bean); + + return bean; + } + + protected void processProvider(String beanName, Object bean) { + // aop后,新的实例的父类可能是原class,也可能只是个proxy,父类不是原class + // 所以,需要先取出原class,再取标注 + Class beanCls = BeanUtils.getImplClassFromBean(bean); + if (beanCls == null) { + return; + } + RpcSchema rpcSchema = beanCls.getAnnotation(RpcSchema.class); + if (rpcSchema == null) { + return; + } + + String schemaId = rpcSchema.schemaId(); + if (StringUtils.isEmpty(schemaId)) { + Class[] intfs = beanCls.getInterfaces(); + if (intfs.length == 1) { + schemaId = intfs[0].getName(); + } else { + throw new Error("Must be schemaId or implements only one interface"); + } + } + + PojoProducerMeta pojoProducerMeta = new PojoProducerMeta(); + pojoProducerMeta.setSchemaId(schemaId); + pojoProducerMeta.setSchemaInterface(rpcSchema.schemaInterface()); + pojoProducerMeta.setInstance(bean); + + registerPojoProducer(pojoProducerMeta); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/SchemaDefParser.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/SchemaDefParser.java new file mode 100644 index 00000000000..a8ffdf137c3 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/SchemaDefParser.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.provider.pojo.schema; + +import org.apache.servicecomb.provider.pojo.PojoConst; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.w3c.dom.Element; + +public class SchemaDefParser extends AbstractSingleBeanDefinitionParser { + @Override + protected boolean shouldGenerateId() { + return true; + } + + @Override + protected boolean shouldParseNameAsAliases() { + return false; + } + + @Override + protected Class getBeanClass(Element element) { + return PojoProducerMeta.class; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, + BeanDefinitionBuilder builder) { + builder.addPropertyValue(PojoConst.FIELD_SCHEMA_ID, element.getAttribute(PojoConst.SCHEMA_ID)); + builder.addPropertyValue(PojoConst.IMPL, element.getAttribute(PojoConst.IMPL)); + builder.addPropertyValue(PojoConst.FIELD_SCHEMA_INTERFACE, element.getAttribute(PojoConst.SCHEMA_INTERFACE)); + } +} diff --git a/providers/provider-pojo/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider b/providers/provider-pojo/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider new file mode 100644 index 00000000000..b5a9f25c66a --- /dev/null +++ b/providers/provider-pojo/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider @@ -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.provider.pojo.PojoProducerProvider diff --git a/providers/provider-pojo/src/main/resources/META-INF/spring.handlers b/providers/provider-pojo/src/main/resources/META-INF/spring.handlers index 7226f5801fd..caaa7f80259 100644 --- a/providers/provider-pojo/src/main/resources/META-INF/spring.handlers +++ b/providers/provider-pojo/src/main/resources/META-INF/spring.handlers @@ -1 +1,18 @@ -http\://www.huawei.com/schema/paas/cse/rpc=io.servicecomb.foundation.common.spring.PaasNamespaceHandler \ No newline at end of file +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +http\://www.huawei.com/schema/paas/cse/rpc=org.apache.servicecomb.foundation.common.spring.PaasNamespaceHandler diff --git a/providers/provider-pojo/src/main/resources/META-INF/spring.schemas b/providers/provider-pojo/src/main/resources/META-INF/spring.schemas index 2f10ed5f64c..c7fcc7cf7e3 100644 --- a/providers/provider-pojo/src/main/resources/META-INF/spring.schemas +++ b/providers/provider-pojo/src/main/resources/META-INF/spring.schemas @@ -1,2 +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. +## --------------------------------------------------------------------------- + http\://www.huawei.com/schema/paas/cse/rpc/spring-paas-cse-rpc-1.0.xsd=META-INF/spring/spring-paas-cse-rpc-1.0.xsd -http\://www.huawei.com/schema/paas/cse/rpc/spring-paas-cse-rpc.xsd=META-INF/spring/spring-paas-cse-rpc-1.0.xsd \ No newline at end of file +http\://www.huawei.com/schema/paas/cse/rpc/spring-paas-cse-rpc.xsd=META-INF/spring/spring-paas-cse-rpc-1.0.xsd diff --git a/providers/provider-pojo/src/main/resources/META-INF/spring/namespace.properties b/providers/provider-pojo/src/main/resources/META-INF/spring/namespace.properties index 2c47e9b93e1..9482b6c6b17 100644 --- a/providers/provider-pojo/src/main/resources/META-INF/spring/namespace.properties +++ b/providers/provider-pojo/src/main/resources/META-INF/spring/namespace.properties @@ -1,11 +1,12 @@ # -# Copyright 2017 Huawei Technologies Co., Ltd +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -14,5 +15,5 @@ # limitations under the License. # -rpc-reference=io.servicecomb.provider.pojo.reference.ReferenceDefParser -rpc-schema=io.servicecomb.provider.pojo.schema.SchemaDefParser \ No newline at end of file +rpc-reference=org.apache.servicecomb.provider.pojo.reference.ReferenceDefParser +rpc-schema=org.apache.servicecomb.provider.pojo.schema.SchemaDefParser diff --git a/providers/provider-pojo/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/providers/provider-pojo/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..4edb96c93f0 --- /dev/null +++ b/providers/provider-pojo/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.provider.pojo.ProviderPojoConfiguration diff --git a/providers/provider-pojo/src/main/resources/META-INF/spring/spring-paas-cse-rpc-1.0.xsd b/providers/provider-pojo/src/main/resources/META-INF/spring/spring-paas-cse-rpc-1.0.xsd index 11222887d34..095bdc44ed0 100644 --- a/providers/provider-pojo/src/main/resources/META-INF/spring/spring-paas-cse-rpc-1.0.xsd +++ b/providers/provider-pojo/src/main/resources/META-INF/spring/spring-paas-cse-rpc-1.0.xsd @@ -1,13 +1,14 @@ + xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.huawei.com/schema/paas/cse/rpc" + elementFormDefault="qualified" attributeFormDefault="unqualified"> - - - - - - + + + + + + + - - - - - - - - + + + + + + + + diff --git a/providers/provider-pojo/src/main/resources/META-INF/spring/spring-paas-cse-rpc.xsd b/providers/provider-pojo/src/main/resources/META-INF/spring/spring-paas-cse-rpc.xsd index f38edbb64c9..481fdb6b124 100644 --- a/providers/provider-pojo/src/main/resources/META-INF/spring/spring-paas-cse-rpc.xsd +++ b/providers/provider-pojo/src/main/resources/META-INF/spring/spring-paas-cse-rpc.xsd @@ -1,13 +1,14 @@ - 4.0.0 - - io.servicecomb - providers - 0.3.0-SNAPSHOT - - provider-rest-common + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + + org.apache.servicecomb + providers + 3.4.0-SNAPSHOT + + provider-rest-common + Java Chassis::Providers::Rest Common + + + + org.apache.servicecomb + common-rest + + - - - io.servicecomb - common-rest - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.servicecomb + foundation-test-scaffolding + + + org.mockito + mockito-inline + test + + diff --git a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/GenericServletMockRequest.java b/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/GenericServletMockRequest.java deleted file mode 100644 index 7466b925997..00000000000 --- a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/GenericServletMockRequest.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.rest.common; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.Principal; -import java.util.Collection; -import java.util.Enumeration; -import java.util.HashMap; -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.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import javax.servlet.http.HttpUpgradeHandler; -import javax.servlet.http.Part; - -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.common.rest.definition.RestParam; -import io.servicecomb.core.Invocation; - -/** - * 根据Invocation和swagger operation构造一个servlet http request - */ -public class GenericServletMockRequest implements HttpServletRequest { - - private RestOperationMeta swaggerOperation; - - private Object[] args; - - public GenericServletMockRequest(Invocation invocation) { - this.swaggerOperation = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); - this.args = invocation.getArgs(); - } - - @Override - public Object getAttribute(String name) { - throw new Error("not supported method"); - } - - @Override - public Enumeration getAttributeNames() { - throw new Error("not supported method"); - } - - @Override - public String getCharacterEncoding() { - throw new Error("not supported method"); - } - - @Override - public void setCharacterEncoding(String env) throws UnsupportedEncodingException { - throw new Error("not supported method"); - } - - @Override - public int getContentLength() { - throw new Error("not supported method"); - } - - @Override - public long getContentLengthLong() { - throw new Error("not supported method"); - } - - @Override - public String getContentType() { - throw new Error("not supported method"); - } - - @Override - public ServletInputStream getInputStream() throws IOException { - throw new Error("not supported method"); - } - - @Override - public String getParameter(String name) { - RestParam param = swaggerOperation.getParamByName(name); - if (param == null) { - return null; - } - - Object value = param.getValue(args); - if (value == null) { - return null; - } - - return String.valueOf(value); - } - - @Override - public Enumeration getParameterNames() { - throw new Error("not supported method"); - } - - @Override - public String[] getParameterValues(String name) { - RestParam param = swaggerOperation.getParamByName(name); - if (param == null) { - return null; - } - - // TODO:form场景中,这里应该是有问题的 - return param.getValueAsStrings(args); - } - - @Override - public Map getParameterMap() { - Map paramMap = new HashMap(); - for (RestParam param : swaggerOperation.getParamList()) { - String[] value = param.getValueAsStrings(args); - paramMap.put(param.getParamName(), value); - } - return paramMap; - } - - @Override - public String getProtocol() { - throw new Error("not supported method"); - } - - @Override - public String getScheme() { - throw new Error("not supported method"); - } - - @Override - public String getServerName() { - throw new Error("not supported method"); - } - - @Override - public int getServerPort() { - throw new Error("not supported method"); - } - - @Override - public BufferedReader getReader() throws IOException { - throw new Error("not supported method"); - } - - @Override - public String getRemoteAddr() { - throw new Error("not supported method"); - } - - @Override - public String getRemoteHost() { - throw new Error("not supported method"); - } - - @Override - public void setAttribute(String name, Object o) { - throw new Error("not supported method"); - } - - @Override - public void removeAttribute(String name) { - throw new Error("not supported method"); - } - - @Override - public Locale getLocale() { - throw new Error("not supported method"); - } - - @Override - public Enumeration getLocales() { - throw new Error("not supported method"); - } - - @Override - public boolean isSecure() { - throw new Error("not supported method"); - } - - @Override - public RequestDispatcher getRequestDispatcher(String path) { - throw new Error("not supported method"); - } - - @Override - public String getRealPath(String path) { - throw new Error("not supported method"); - } - - @Override - public int getRemotePort() { - throw new Error("not supported method"); - } - - @Override - public String getLocalName() { - throw new Error("not supported method"); - } - - @Override - public String getLocalAddr() { - throw new Error("not supported method"); - } - - @Override - public int getLocalPort() { - throw new Error("not supported method"); - } - - @Override - public ServletContext getServletContext() { - throw new Error("not supported method"); - } - - @Override - public AsyncContext startAsync() throws IllegalStateException { - throw new Error("not supported method"); - } - - @Override - public AsyncContext startAsync(ServletRequest servletRequest, - ServletResponse servletResponse) throws IllegalStateException { - throw new Error("not supported method"); - } - - @Override - public boolean isAsyncStarted() { - throw new Error("not supported method"); - } - - @Override - public boolean isAsyncSupported() { - throw new Error("not supported method"); - } - - @Override - public AsyncContext getAsyncContext() { - throw new Error("not supported method"); - } - - @Override - public DispatcherType getDispatcherType() { - throw new Error("not supported method"); - } - - @Override - public String getAuthType() { - throw new Error("not supported method"); - } - - @Override - public Cookie[] getCookies() { - throw new Error("not supported method"); - } - - @Override - public long getDateHeader(String name) { - throw new Error("not supported method"); - } - - @Override - public String getHeader(String name) { - return getParameter(name); - } - - @Override - public Enumeration getHeaders(String name) { - throw new Error("not supported method"); - } - - @Override - public Enumeration getHeaderNames() { - throw new Error("not supported method"); - } - - @Override - public int getIntHeader(String name) { - String header = getHeader(name); - if (header == null) { - return -1; - } - - return Integer.parseInt(header); - } - - @Override - public String getMethod() { - return this.swaggerOperation.getHttpMethod(); - } - - @Override - public String getPathInfo() { - try { - return this.swaggerOperation.getPathBuilder().createPathString(args); - } catch (Exception e) { - throw new Error(e); - } - } - - @Override - public String getPathTranslated() { - throw new Error("not supported method"); - } - - @Override - public String getContextPath() { - throw new Error("not supported method"); - } - - @Override - public String getQueryString() { - throw new Error("not supported method"); - } - - @Override - public String getRemoteUser() { - throw new Error("not supported method"); - } - - @Override - public boolean isUserInRole(String role) { - throw new Error("not supported method"); - } - - @Override - public Principal getUserPrincipal() { - throw new Error("not supported method"); - } - - @Override - public String getRequestedSessionId() { - throw new Error("not supported method"); - } - - @Override - public String getRequestURI() { - throw new Error("not supported method"); - } - - @Override - public StringBuffer getRequestURL() { - throw new Error("not supported method"); - } - - @Override - public String getServletPath() { - return this.getPathInfo(); - } - - @Override - public HttpSession getSession(boolean create) { - throw new Error("not supported method"); - } - - @Override - public HttpSession getSession() { - throw new Error("not supported method"); - } - - @Override - public String changeSessionId() { - throw new Error("not supported method"); - } - - @Override - public boolean isRequestedSessionIdValid() { - throw new Error("not supported method"); - } - - @Override - public boolean isRequestedSessionIdFromCookie() { - throw new Error("not supported method"); - } - - @Override - public boolean isRequestedSessionIdFromURL() { - throw new Error("not supported method"); - } - - @Override - public boolean isRequestedSessionIdFromUrl() { - throw new Error("not supported method"); - } - - @Override - public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { - throw new Error("not supported method"); - } - - @Override - public void login(String username, String password) throws ServletException { - throw new Error("not supported method"); - } - - @Override - public void logout() throws ServletException { - throw new Error("not supported method"); - } - - @Override - public Collection getParts() throws IOException, ServletException { - throw new Error("not supported method"); - } - - @Override - public Part getPart(String name) throws IOException, ServletException { - throw new Error("not supported method"); - } - - @Override - public T upgrade(Class handlerClass) throws IOException, ServletException { - throw new Error("not supported method"); - } -} diff --git a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/ProducerHttpRequestArgMapper.java b/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/ProducerHttpRequestArgMapper.java deleted file mode 100644 index e42a583967d..00000000000 --- a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/ProducerHttpRequestArgMapper.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.rest.common; - -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.core.Invocation; -import io.servicecomb.swagger.invocation.SwaggerInvocation; -import io.servicecomb.swagger.invocation.arguments.producer.AbstractProducerContextArgMapper; - -public class ProducerHttpRequestArgMapper extends AbstractProducerContextArgMapper { - public ProducerHttpRequestArgMapper(int producerArgIdx) { - super(producerArgIdx); - } - - @Override - public Object createContextArg(SwaggerInvocation swaggerInvocation) { - Invocation invocation = (Invocation) swaggerInvocation; - // 从rest transport来 - AbstractProducerContextArgMapper httpRequestCreator = - (AbstractProducerContextArgMapper) invocation.getHandlerContext() - .get(RestConst.HTTP_REQUEST_CREATOR); - if (httpRequestCreator != null) { - return httpRequestCreator.createContextArg(invocation); - } - - // 通过args模拟request - return new GenericServletMockRequest(invocation); - } -} diff --git a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/ProducerHttpRequestArgMapperFactory.java b/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/ProducerHttpRequestArgMapperFactory.java deleted file mode 100644 index 53e23c8b7c0..00000000000 --- a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/ProducerHttpRequestArgMapperFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.rest.common; - -import javax.servlet.http.HttpServletRequest; - -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.arguments.ArgumentMapper; -import io.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; - -@Component -@Qualifier("producer") -public class ProducerHttpRequestArgMapperFactory implements ContextArgumentMapperFactory { - - @Override - public Class getContextClass() { - return HttpServletRequest.class; - } - - @Override - public ArgumentMapper create(int providerArgIdx) { - return new ProducerHttpRequestArgMapper(providerArgIdx); - } -} diff --git a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/RestConsumerProvider.java b/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/RestConsumerProvider.java deleted file mode 100644 index bbfaabd7fc7..00000000000 --- a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/RestConsumerProvider.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.rest.common; - -import org.springframework.stereotype.Component; - -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.core.provider.consumer.AbstractConsumerProvider; - -@Component -public class RestConsumerProvider extends AbstractConsumerProvider { - @Override - public String getName() { - return RestConst.REST; - } -} diff --git a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/RestProducerProvider.java b/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/RestProducerProvider.java deleted file mode 100644 index a1d38d691e3..00000000000 --- a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/RestProducerProvider.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.rest.common; - -import javax.inject.Inject; - -import org.springframework.stereotype.Component; - -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.core.definition.schema.ProducerSchemaFactory; -import io.servicecomb.core.provider.producer.AbstractProducerProvider; -import io.servicecomb.core.provider.producer.ProducerMeta; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.Microservice; - -@Component -public class RestProducerProvider extends AbstractProducerProvider { - - @Inject - protected ProducerSchemaFactory producerSchemaFactory; - - @Inject - protected RestProducers restProducers; - - @Override - public String getName() { - return RestConst.REST; - } - - @Override - public void init() throws Exception { - for (ProducerMeta producerMeta : restProducers.getProducerMetaList()) { - Microservice microservice = RegistryUtils.getMicroservice(); - producerSchemaFactory.getOrCreateProducerSchema( - microservice.getServiceName(), - producerMeta.getSchemaId(), - producerMeta.getInstanceClass(), - producerMeta.getInstance()); - } - } -} diff --git a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/RestProducers.java b/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/RestProducers.java deleted file mode 100644 index dc52302060e..00000000000 --- a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/RestProducers.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.provider.rest.common; - -import java.util.ArrayList; -import java.util.List; - -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -import io.servicecomb.core.provider.CseBeanPostProcessor.ProviderProcessor; -import io.servicecomb.core.provider.producer.ProducerMeta; -import io.servicecomb.foundation.common.utils.BeanUtils; - -@Component -public class RestProducers implements ProviderProcessor { - private List producerMetaList = new ArrayList<>(); - - public List getProducerMetaList() { - return producerMetaList; - } - - @Override - public void processProvider(ApplicationContext applicationContext, String beanName, Object bean) { - // aop后,新的实例的父类可能是原class,也可能只是个proxy,父类不是原class - // 所以,需要先取出原class,再取标注 - Class beanCls = BeanUtils.getImplClassFromBean(bean); - RestSchema restSchema = beanCls.getAnnotation(RestSchema.class); - if (restSchema == null) { - return; - } - - ProducerMeta producerMeta = new ProducerMeta(restSchema.schemaId(), bean, beanCls); - producerMetaList.add(producerMeta); - } -} diff --git a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/RestSchema.java b/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/RestSchema.java deleted file mode 100644 index 93afdbc276a..00000000000 --- a/providers/provider-rest-common/src/main/java/io/servicecomb/provider/rest/common/RestSchema.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.rest.common; - -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) -@Component -public @interface RestSchema { - String schemaId(); -} diff --git a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/InvocationToHttpServletRequest.java b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/InvocationToHttpServletRequest.java new file mode 100644 index 00000000000..bf4fe454b95 --- /dev/null +++ b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/InvocationToHttpServletRequest.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.provider.rest.common; + +import java.util.HashMap; +import java.util.Map; + +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.core.CoreConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.apache.servicecomb.foundation.vertx.http.AbstractHttpServletRequest; + +import io.vertx.core.net.SocketAddress; + +/** + * when transport is not over http, mock an HttpServletRequest from Invocation + */ +public class InvocationToHttpServletRequest extends AbstractHttpServletRequest { + private final RestOperationMeta swaggerOperation; + + private final Invocation invocation; + + public InvocationToHttpServletRequest(Invocation invocation) { + this.swaggerOperation = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); + this.invocation = invocation; + } + + private SocketAddress getSockerAddress() { + return (SocketAddress) invocation.getHandlerContext().get(CoreConst.REMOTE_ADDRESS); + } + + @Override + public String getParameter(String name) { + RestParam param = swaggerOperation.getParamByName(name); + if (param == null) { + return null; + } + + Object value = param.getValue(invocation.getSwaggerArguments()); + if (value == null) { + return null; + } + + return String.valueOf(value); + } + + @Override + public String[] getParameterValues(String name) { + RestParam param = swaggerOperation.getParamByName(name); + if (param == null) { + return null; + } + + return param.getValueAsStrings(invocation.getSwaggerArguments()); + } + + @Override + public Map getParameterMap() { + Map paramMap = new HashMap<>(); + for (RestParam param : swaggerOperation.getParamList()) { + String[] value = param.getValueAsStrings(invocation.getSwaggerArguments()); + paramMap.put(param.getParamName(), value); + } + return paramMap; + } + + @Override + public String getHeader(String name) { + return getParameter(name); + } + + @Override + public int getIntHeader(String name) { + String header = getHeader(name); + if (header == null) { + return -1; + } + + return Integer.parseInt(header); + } + + @Override + public String getMethod() { + return this.swaggerOperation.getHttpMethod(); + } + + @Override + public String getPathInfo() { + try { + return this.swaggerOperation.getPathBuilder().createPathString(invocation.getSwaggerArguments()); + } catch (Exception e) { + throw new ServiceCombException("Failed to get path info.", e); + } + } + + @Override + public String getRemoteAddr() { + return this.getSockerAddress() == null ? "" : this.getSockerAddress().host(); + } + + @Override + public String getRemoteHost() { + return this.getSockerAddress() == null ? "" : this.getSockerAddress().host(); + } + + @Override + public int getRemotePort() { + return this.getSockerAddress() == null ? 0 : this.getSockerAddress().port(); + } + + @Override + public String getContextPath() { + return ""; + } + + /** + * it's a mock httpServletRequest, contentType is unknown + * @return contentType + */ + @Override + public String getContentType() { + return null; + } + + /** + * it's a mock httpServletRequest, characterEncoding is unknown + * @return characterEncoding + */ + @Override + public String getCharacterEncoding() { + return null; + } +} diff --git a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerHttpRequestArgMapper.java b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerHttpRequestArgMapper.java new file mode 100644 index 00000000000..2e9ac697393 --- /dev/null +++ b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerHttpRequestArgMapper.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.provider.rest.common; + +import jakarta.servlet.http.HttpServletRequest; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.producer.AbstractProducerContextArgMapper; + +public class ProducerHttpRequestArgMapper extends AbstractProducerContextArgMapper { + public ProducerHttpRequestArgMapper(String invocationArgumentName, String swaggerArgumentName) { + super(invocationArgumentName, swaggerArgumentName); + } + + @Override + public Object createContextArg(SwaggerInvocation swaggerInvocation) { + Invocation invocation = (Invocation) swaggerInvocation; + // 从rest transport来 + HttpServletRequest request = (HttpServletRequest) invocation.getHandlerContext().get(RestConst.REST_REQUEST); + if (request != null) { + return request; + } + + // 通过args模拟request + return new InvocationToHttpServletRequest(invocation); + } +} diff --git a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerHttpRequestArgMapperFactory.java b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerHttpRequestArgMapperFactory.java new file mode 100644 index 00000000000..b830af1bd10 --- /dev/null +++ b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerHttpRequestArgMapperFactory.java @@ -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. + */ + +package org.apache.servicecomb.provider.rest.common; + +import jakarta.servlet.http.HttpServletRequest; + +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; +import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory; + +public class ProducerHttpRequestArgMapperFactory implements ProducerContextArgumentMapperFactory { + + @Override + public Class getContextClass() { + return HttpServletRequest.class; + } + + @Override + public ArgumentMapper create(String invocationArgumentName, String swaggerArgumentName) { + return new ProducerHttpRequestArgMapper(invocationArgumentName, swaggerArgumentName); + } +} diff --git a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketArgMapperFactory.java b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketArgMapperFactory.java new file mode 100644 index 00000000000..b0f554a8466 --- /dev/null +++ b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketArgMapperFactory.java @@ -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. + */ + +package org.apache.servicecomb.provider.rest.common; + +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; +import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory; + +import io.vertx.core.http.ServerWebSocket; + +public class ProducerServerWebSocketArgMapperFactory implements ProducerContextArgumentMapperFactory { + + @Override + public Class getContextClass() { + return ServerWebSocket.class; + } + + @Override + public ArgumentMapper create(String invocationArgumentName, String swaggerArgumentName) { + return new ProducerServerWebSocketMapper(invocationArgumentName, swaggerArgumentName); + } +} diff --git a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketMapper.java b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketMapper.java new file mode 100644 index 00000000000..21d040b5c87 --- /dev/null +++ b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketMapper.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.provider.rest.common; + +import org.apache.servicecomb.common.rest.WebSocketTransportContext; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.producer.AbstractProducerContextArgMapper; + + +public class ProducerServerWebSocketMapper extends AbstractProducerContextArgMapper { + public ProducerServerWebSocketMapper(String invocationArgumentName, String swaggerArgumentName) { + super(invocationArgumentName, swaggerArgumentName); + } + + @Override + public Object createContextArg(SwaggerInvocation invocation) { + WebSocketTransportContext context = invocation.getTransportContext(); + return context.getServerWebSocket(); + } +} diff --git a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProviderRestCommonConfiguration.java b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProviderRestCommonConfiguration.java new file mode 100644 index 00000000000..662c2ffba42 --- /dev/null +++ b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProviderRestCommonConfiguration.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.provider.rest.common; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ProviderRestCommonConfiguration { + @Bean + public static RestProducers restProducers() { + return new RestProducers(); + } +} diff --git a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestProducerProvider.java b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestProducerProvider.java new file mode 100644 index 00000000000..5bca923ce1a --- /dev/null +++ b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestProducerProvider.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.provider.rest.common; + +import java.util.List; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.core.provider.producer.AbstractProducerProvider; +import org.apache.servicecomb.core.provider.producer.ProducerMeta; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; + +public class RestProducerProvider extends AbstractProducerProvider { + @Override + public String getName() { + return RestConst.REST; + } + + @Override + public List init() { + // for some UT case, there is no spring context + if (BeanUtils.getContext() == null) { + return null; + } + + RestProducers restProducers = BeanUtils.getContext().getBean(RestProducers.class); + return restProducers.getProducerMetaList(); + } +} diff --git a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestProducers.java b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestProducers.java new file mode 100644 index 00000000000..8aed65f3ba7 --- /dev/null +++ b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestProducers.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.provider.rest.common; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.core.provider.producer.ProducerMeta; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.apache.servicecomb.foundation.common.utils.ReflectUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; + +public class RestProducers implements BeanPostProcessor, EnvironmentAware { + private final List producerMetaList = new ArrayList<>(); + + @SuppressWarnings("unchecked") + private final Class restControllerCls = (Class) ReflectUtils + .getClassByName("org.springframework.web.bind.annotation.RestController"); + + public List getProducerMetaList() { + return producerMetaList; + } + + private Environment environment; + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + processProvider(beanName, bean); + + return bean; + } + + protected void processProvider(String beanName, Object bean) { + // aop后,新的实例的父类可能是原class,也可能只是个proxy,父类不是原class + // 所以,需要先取出原class,再取标注 + Class beanCls = BeanUtils.getImplClassFromBean(bean); + if (beanCls == null) { + return; + } + RestSchema restSchema = beanCls.getAnnotation(RestSchema.class); + if (restSchema != null) { + ProducerMeta producerMeta = new ProducerMeta(restSchema.schemaId(), bean); + producerMeta.setSchemaInterface(restSchema.schemaInterface()); + producerMetaList.add(producerMeta); + return; + } + + if (restControllerCls != null && + environment.getProperty(RestConst.PROVIDER_SCAN_REST_CONTROLLER, boolean.class, true) + && beanCls.getAnnotation(restControllerCls) != null) { + ProducerMeta producerMeta = new ProducerMeta(beanCls.getName(), bean); + producerMetaList.add(producerMeta); + } + } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } +} diff --git a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestSchema.java b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestSchema.java new file mode 100644 index 00000000000..5edadcc6e86 --- /dev/null +++ b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestSchema.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.provider.rest.common; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +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, ANNOTATION_TYPE}) +@Component +public @interface RestSchema { + String schemaId(); + + Class schemaInterface() default Object.class; +} diff --git a/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider b/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider new file mode 100644 index 00000000000..ee817607b15 --- /dev/null +++ b/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider @@ -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.provider.rest.common.RestProducerProvider diff --git a/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory b/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory new file mode 100644 index 00000000000..1561d349cf1 --- /dev/null +++ b/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory @@ -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.provider.rest.common.ProducerHttpRequestArgMapperFactory +org.apache.servicecomb.provider.rest.common.ProducerServerWebSocketArgMapperFactory diff --git a/providers/provider-rest-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/providers/provider-rest-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..dfbc633bfe5 --- /dev/null +++ b/providers/provider-rest-common/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.provider.rest.common.ProviderRestCommonConfiguration diff --git a/providers/provider-rest-common/src/test/java/io/servicecomb/provider/common/MockUtil.java b/providers/provider-rest-common/src/test/java/io/servicecomb/provider/common/MockUtil.java deleted file mode 100644 index c1f6f52a846..00000000000 --- a/providers/provider-rest-common/src/test/java/io/servicecomb/provider/common/MockUtil.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.common; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Type; - -import org.mockito.Mockito; -import org.springframework.context.ApplicationContext; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import mockit.Mock; -import mockit.MockUp; - -public class MockUtil { - - private static MockUtil instance = new MockUtil(); - - private MockUtil() { - - } - - public static MockUtil getInstance() { - return instance; - } - - public void mockBeanUtils() { - - new MockUp() { - @Mock - ApplicationContext getContext() { - return Mockito.mock(ApplicationContext.class); - } - }; - } - - public void mockMethod() { - - new MockUp() { - @Mock - public Annotation[][] getParameterAnnotations() { - Annotation[][] lAnnotation = new Annotation[1][1]; - lAnnotation[0][0] = Mockito.mock(Annotation.class); - return lAnnotation; - } - - @Mock - public Type[] getGenericParameterTypes() { - Type[] lType = new Type[1]; - lType[0] = Mockito.mock(Type.class); - Mockito.when(lType[0].getTypeName()).thenReturn("javax.servlet.http.HttpServletRequest"); - return lType; - } - }; - } -} diff --git a/providers/provider-rest-common/src/test/java/io/servicecomb/provider/rest/common/TestGenericServletMockRequest.java b/providers/provider-rest-common/src/test/java/io/servicecomb/provider/rest/common/TestGenericServletMockRequest.java deleted file mode 100644 index 07282426c4c..00000000000 --- a/providers/provider-rest-common/src/test/java/io/servicecomb/provider/rest/common/TestGenericServletMockRequest.java +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.rest.common; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.common.rest.definition.RestParam; -import io.servicecomb.common.rest.definition.path.URLPathBuilder; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; - -public class TestGenericServletMockRequest { - - @Test - public void testGenericServletMockRequest() - throws Exception { - - Invocation invocation = Mockito.mock(Invocation.class); - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - RestOperationMeta swaggerOperation = Mockito.mock(RestOperationMeta.class); - Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerOperation); - GenericServletMockRequest lGenericServletMockRequest = new GenericServletMockRequest(invocation); - URLPathBuilder uRLPathBuilder = Mockito.mock(URLPathBuilder.class); - Mockito.when(swaggerOperation.getPathBuilder()).thenReturn(uRLPathBuilder); - Mockito.when(swaggerOperation.getPathBuilder().createPathString(null)).thenReturn("test"); - - RestParam param = Mockito.mock(RestParam.class); - Mockito.when(swaggerOperation.getParamByName("1")).thenReturn(param); - Mockito.when(param.getValue(null)).thenReturn("1"); - List restParam = new ArrayList<>(); - restParam.add(param); - Mockito.when(swaggerOperation.getParamList()).thenReturn(restParam); - Assert.assertEquals("1", lGenericServletMockRequest.getParameter("1")); - Assert.assertNull(lGenericServletMockRequest.getParameterValues("1")); - Assert.assertEquals("1", lGenericServletMockRequest.getHeader("1")); - Assert.assertEquals(1, lGenericServletMockRequest.getIntHeader("1")); - Assert.assertEquals("test", lGenericServletMockRequest.getServletPath()); - Mockito.when(swaggerOperation.getParamByName("test")).thenReturn(null); - Assert.assertEquals(null, lGenericServletMockRequest.getParameter("test")); - Assert.assertNull(lGenericServletMockRequest.getParameterValues("test")); - Map paramMap = new HashMap(); - paramMap.put(null, null); - Assert.assertEquals(paramMap, lGenericServletMockRequest.getParameterMap()); - try { - lGenericServletMockRequest.getAttribute(""); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getAttributeNames(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getCharacterEncoding(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.setCharacterEncoding(""); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getContentLength(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getContentLengthLong(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getContentType(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getInputStream(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getParameterNames(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getProtocol(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getScheme(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getServerName(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getServerPort(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getReader(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getRemoteAddr(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getRemoteHost(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.setAttribute("", ""); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.removeAttribute(""); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getLocale(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getLocales(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.isSecure(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getRequestDispatcher(""); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getRealPath(""); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getRemotePort(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getLocalName(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getLocalAddr(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getLocalPort(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getServletContext(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.startAsync(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.startAsync(null, null); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.isAsyncStarted(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.isAsyncSupported(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getAsyncContext(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getDispatcherType(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getAuthType(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getCookies(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getDateHeader(""); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getHeaders(""); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getHeaderNames(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getPathTranslated(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getContextPath(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getQueryString(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getRemoteUser(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.isUserInRole(""); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getUserPrincipal(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getRequestedSessionId(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getRequestURI(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getRequestURL(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getSession(true); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getSession(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.changeSessionId(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.isRequestedSessionIdValid(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.isRequestedSessionIdFromCookie(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.isRequestedSessionIdFromURL(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.isRequestedSessionIdFromUrl(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.authenticate(null); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.login("", ""); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.logout(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getParts(); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.getPart(""); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - try { - lGenericServletMockRequest.upgrade(null); - } catch (Error e) { - Assert.assertEquals("not supported method", e.getMessage()); - } - - Assert.assertNull(lGenericServletMockRequest.getMethod()); - Assert.assertEquals("test", lGenericServletMockRequest.getPathInfo()); - } -} diff --git a/providers/provider-rest-common/src/test/java/io/servicecomb/provider/rest/common/TestRestConsumerProvider.java b/providers/provider-rest-common/src/test/java/io/servicecomb/provider/rest/common/TestRestConsumerProvider.java deleted file mode 100644 index e17759d1e3b..00000000000 --- a/providers/provider-rest-common/src/test/java/io/servicecomb/provider/rest/common/TestRestConsumerProvider.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.rest.common; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.common.rest.RestConst; - -public class TestRestConsumerProvider { - - @Test - public void testInit() throws Exception { - RestConsumerProvider instance = new RestConsumerProvider(); - instance.init(); - Assert.assertEquals(RestConst.REST, instance.getName()); - } -} diff --git a/providers/provider-rest-common/src/test/java/io/servicecomb/provider/rest/common/TestRestServiceProvider.java b/providers/provider-rest-common/src/test/java/io/servicecomb/provider/rest/common/TestRestServiceProvider.java deleted file mode 100644 index 0ade45229b0..00000000000 --- a/providers/provider-rest-common/src/test/java/io/servicecomb/provider/rest/common/TestRestServiceProvider.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.rest.common; - -import java.util.HashMap; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; -import org.springframework.context.ApplicationContext; - -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.core.definition.schema.ProducerSchemaFactory; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.ReflectUtils; -import mockit.Mock; -import mockit.MockUp; - -public class TestRestServiceProvider { - - @Test - public void testInit() throws Exception { - ApplicationContext context = Mockito.mock(ApplicationContext.class); - Mockito.when(context.getBeansWithAnnotation(RestSchema.class)).thenReturn(new HashMap()); - - new MockUp() { - @Mock - ApplicationContext getContext() { - return context; - } - }; - - RestProducerProvider restProducerProvider = new RestProducerProvider(); - ReflectUtils.setField(restProducerProvider, "producerSchemaFactory", new ProducerSchemaFactory()); - ReflectUtils.setField(restProducerProvider, "restProducers", new RestProducers()); - - restProducerProvider.init(); - Assert.assertEquals(RestConst.REST, restProducerProvider.getName()); - } - - @Test - public void testInvoke() throws Exception { - // Invocation invocation = Mockito.mock(Invocation.class); - // AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - // OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - // Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - // RestProviderOperation restProviderOperationMeta = Mockito.mock(RestProviderOperation.class); - // Mockito.when(operationMeta.getExtData("rest.operation")).thenReturn(restProviderOperationMeta); - // ArgsMapper argsMapper = Mockito.mock(ArgsMapper.class); - // Mockito.when(restProviderOperationMeta.getArgsMapper()).thenReturn(argsMapper); - // try { - // RestProducerProvider.getInstance().invoke(invocation, asyncResp); - // } catch (Exception e) { - // Assert.assertEquals(null, e.getMessage()); - // } - // Assert.assertEquals(invocation.getContext(), ContextUtils.getInvocationContext().getContext()); - } -} diff --git a/providers/provider-rest-common/src/test/java/io/servicecomb/provider/rest/common/schema/TestHttpRequestParamCreator.java b/providers/provider-rest-common/src/test/java/io/servicecomb/provider/rest/common/schema/TestHttpRequestParamCreator.java deleted file mode 100644 index 8c8f7aa7517..00000000000 --- a/providers/provider-rest-common/src/test/java/io/servicecomb/provider/rest/common/schema/TestHttpRequestParamCreator.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.rest.common.schema; - -import javax.servlet.http.HttpServletRequest; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.provider.rest.common.ProducerHttpRequestArgMapper; - -public class TestHttpRequestParamCreator { - - @Test - public void testCreateMockParam() throws Exception { - ProducerHttpRequestArgMapper lHttpRequestParamCreator = new ProducerHttpRequestArgMapper(0); - Invocation invocation = Mockito.mock(Invocation.class); - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - RestOperationMeta swaggerOperation = Mockito.mock(RestOperationMeta.class); - Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerOperation); - HttpServletRequest lHttpServletRequest = - (HttpServletRequest) lHttpRequestParamCreator.createContextArg(invocation); - Assert.assertNull(lHttpServletRequest.getMethod()); - } -} diff --git a/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestInvocationToHttpServletRequest.java b/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestInvocationToHttpServletRequest.java new file mode 100644 index 00000000000..4453fc897c2 --- /dev/null +++ b/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestInvocationToHttpServletRequest.java @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.provider.rest.common; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import jakarta.servlet.http.HttpServletRequest; + +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.common.rest.definition.path.URLPathBuilder; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; + +import io.vertx.core.net.SocketAddress; +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 TestInvocationToHttpServletRequest { + Invocation invocation = Mockito.mock(Invocation.class); + + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + + RestOperationMeta swaggerOperation = Mockito.mock(RestOperationMeta.class); + + Map args; + + SocketAddress socketAddress = Mockito.mock(SocketAddress.class); + + Map handlerContext = new HashMap<>(); + + HttpServletRequest request; + + @BeforeEach + public void setup() { + handlerContext.put(CoreConst.REMOTE_ADDRESS, socketAddress); + args = new HashMap<>(); + + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerOperation); + + request = new InvocationToHttpServletRequest(invocation); + } + + @AfterEach + public void tearDown() { + + } + + @Test + public void testGetParameterNotFound() { + Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(null); + + Assertions.assertNull(request.getParameter("name")); + } + + @Test + public void testGetParameterNull() { + RestParam restParam = Mockito.mock(RestParam.class); + Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); + Mockito.when(restParam.getValue(args)).thenReturn(null); + + Assertions.assertNull(request.getParameter("name")); + } + + @Test + public void testGetParameterNormal() { + RestParam restParam = Mockito.mock(RestParam.class); + Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); + Mockito.when(restParam.getValue(args)).thenReturn("value"); + + Assertions.assertEquals("value", request.getParameter("name")); + } + + @Test + public void testGetParameterValuesNotFound() { + Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(null); + + Assertions.assertNull(request.getParameterValues("name")); + } + + @Test + public void testGetParameterValuesNormal() { + RestParam restParam = Mockito.mock(RestParam.class); + Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); + Mockito.when(restParam.getValueAsStrings(args)).thenReturn(new String[] {"value"}); + + MatcherAssert.assertThat(request.getParameterValues("name"), Matchers.arrayContaining("value")); + } + + @Test + public void testGetParameterMap() { + RestParam p1 = Mockito.mock(RestParam.class); + RestParam p2 = Mockito.mock(RestParam.class); + Mockito.when(swaggerOperation.getParamList()).thenReturn(Arrays.asList(p1, p2)); + Mockito.when(p1.getValueAsStrings(args)).thenReturn(new String[] {"v1"}); + Mockito.when(p1.getParamName()).thenReturn("p1"); + Mockito.when(p2.getValueAsStrings(args)).thenReturn(new String[] {"v2"}); + Mockito.when(p2.getParamName()).thenReturn("p2"); + + Map params = request.getParameterMap(); + MatcherAssert.assertThat(params.size(), Matchers.is(2)); + MatcherAssert.assertThat(params, Matchers.hasEntry("p1", new String[] {"v1"})); + MatcherAssert.assertThat(params, Matchers.hasEntry("p2", new String[] {"v2"})); + } + + @Test + public void testGetHeader() { + RestParam restParam = Mockito.mock(RestParam.class); + Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); + Mockito.when(restParam.getValue(args)).thenReturn("value"); + + Assertions.assertEquals("value", request.getHeader("name")); + } + + @Test + public void testGetIntHeaderNotFound() { + RestParam restParam = Mockito.mock(RestParam.class); + Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); + Mockito.when(restParam.getValue(args)).thenReturn(null); + + Assertions.assertEquals(-1, request.getIntHeader("name")); + } + + @Test + public void testGetIntHeaderNotNumber() { + RestParam restParam = Mockito.mock(RestParam.class); + Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); + Mockito.when(restParam.getValue(args)).thenReturn("value"); + + try { + request.getIntHeader("name"); + Assertions.fail("must throw exception"); + } catch (NumberFormatException e) { + Assertions.assertEquals("For input string: \"value\"", e.getMessage()); + } + } + + @Test + public void testGetIntHeaderNormal() { + RestParam restParam = Mockito.mock(RestParam.class); + Mockito.when(swaggerOperation.getParamByName("name")).thenReturn(restParam); + Mockito.when(restParam.getValue(args)).thenReturn("1"); + + Assertions.assertEquals(1, request.getIntHeader("name")); + } + + @Test + public void testGetMethod() { + Mockito.when(swaggerOperation.getHttpMethod()).thenReturn("GET"); + + Assertions.assertEquals("GET", request.getMethod()); + } + + @Test + public void testGetPathInfoNormal() throws Exception { + URLPathBuilder builder = Mockito.mock(URLPathBuilder.class); + Mockito.when(swaggerOperation.getPathBuilder()).thenReturn(builder); + Mockito.when(builder.createPathString(args)).thenReturn("/path"); + + Assertions.assertEquals("/path", request.getPathInfo()); + } + + @Test + public void testGetPathInfoException() throws Exception { + URLPathBuilder builder = Mockito.mock(URLPathBuilder.class); + Mockito.when(swaggerOperation.getPathBuilder()).thenReturn(builder); + Mockito.when(builder.createPathString(args)).thenThrow(new Exception("error")); + + try { + request.getPathInfo(); + Assertions.fail("must throw exception"); + } catch (ServiceCombException e) { + Assertions.assertEquals("Failed to get path info.", e.getMessage()); + Assertions.assertEquals("error", e.getCause().getMessage()); + } + } + + @Test + public void testGetRemoteAddress() throws Exception { + Mockito.when(socketAddress.host()).thenReturn("127.0.0.2"); + Mockito.when(socketAddress.port()).thenReturn(8088); + Mockito.when(invocation.getHandlerContext()).thenReturn(handlerContext); + String addr = request.getRemoteAddr(); + String host = request.getRemoteHost(); + int port = request.getRemotePort(); + Assertions.assertEquals(addr, "127.0.0.2"); + Assertions.assertEquals(host, "127.0.0.2"); + Assertions.assertEquals(port, 8088); + } + + @Test + public void testGetRemoteAddressEmpty() throws Exception { + Invocation invocation = Mockito.mock(Invocation.class); + + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerOperation); + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + handlerContext.remove(CoreConst.REMOTE_ADDRESS); + Mockito.when(invocation.getHandlerContext()).thenReturn(handlerContext); + InvocationToHttpServletRequest request = new InvocationToHttpServletRequest(invocation); + String addr = request.getRemoteAddr(); + String host = request.getRemoteHost(); + int port = request.getRemotePort(); + Assertions.assertEquals(addr, ""); + Assertions.assertEquals(host, ""); + Assertions.assertEquals(port, 0); + } + + @Test + public void testGetContextPath() { + InvocationToHttpServletRequest request = new InvocationToHttpServletRequest(invocation); + Assertions.assertEquals("", request.getContextPath()); + } + + @Test + public void getContentType() { + Assertions.assertNull(request.getContentType()); + } + + @Test + public void getCharacterEncoding() { + Assertions.assertNull(request.getCharacterEncoding()); + } +} diff --git a/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestProducerHttpRequestArgMapper.java b/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestProducerHttpRequestArgMapper.java new file mode 100644 index 00000000000..ba11c447897 --- /dev/null +++ b/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestProducerHttpRequestArgMapper.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.provider.rest.common; + +import java.util.HashMap; +import java.util.Map; + +import jakarta.servlet.http.HttpServletRequest; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.OperationMeta; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestProducerHttpRequestArgMapper { + Invocation invocation = Mockito.mock(Invocation.class); + + ProducerHttpRequestArgMapper mapper = new ProducerHttpRequestArgMapper("test", "test"); + + @Test + public void testGetFromContext() { + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + Map context = new HashMap<>(); + context.put(RestConst.REST_REQUEST, request); + + Mockito.when(invocation.getHandlerContext()).thenReturn(context); + + Assertions.assertSame(request, mapper.createContextArg(invocation)); + } + + @Test + public void testCreateFromInvocation() { + HttpServletRequest request = Mockito.mock(HttpServletRequest.class); + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + RestOperationMeta swaggerOperation = Mockito.mock(RestOperationMeta.class); + Map context = new HashMap<>(); + + Mockito.when(invocation.getHandlerContext()).thenReturn(context); + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerOperation); + + Assertions.assertEquals(InvocationToHttpServletRequest.class, mapper.createContextArg(invocation).getClass()); + } +} diff --git a/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestRestProducers.java b/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestRestProducers.java new file mode 100644 index 00000000000..9e70423e22f --- /dev/null +++ b/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestRestProducers.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.provider.rest.common; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.test.scaffolding.spring.SpringUtils; +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 TestRestProducers { + @RestSchema(schemaId = "test") + public static class RestSchemaForTest { + } + + static Environment environment = Mockito.mock(Environment.class); + + @BeforeAll + public static void setUpClass() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty(RestConst.PROVIDER_SCAN_REST_CONTROLLER, boolean.class, true)) + .thenReturn(true); + } + + RestProducers producer = new RestProducers(); + + @Test + public void postProcessBeforeInitialization() { + Object bean = new Object(); + Assertions.assertSame(bean, producer.postProcessBeforeInitialization(bean, "test")); + } + + @Test + public void postProcessAfterInitializationNormal() { + RestSchemaForTest bean = new RestSchemaForTest(); + Assertions.assertSame(bean, producer.postProcessAfterInitialization(bean, "")); + Assertions.assertEquals(1, producer.getProducerMetaList().size()); + } + + @Test + public void postProcessAfterInitializationNoAnnotation() { + Object bean = new Object(); + Assertions.assertSame(bean, producer.postProcessAfterInitialization(bean, "")); + Assertions.assertEquals(0, producer.getProducerMetaList().size()); + } + + @Test + public void ensureNoInject() { + SpringUtils.ensureNoInject(RestProducers.class); + } +} diff --git a/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestRestServiceProvider.java b/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestRestServiceProvider.java new file mode 100644 index 00000000000..e7713afdde9 --- /dev/null +++ b/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestRestServiceProvider.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.provider.rest.common; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.context.ApplicationContext; + +public class TestRestServiceProvider { + @Test + public void testInit() { + ApplicationContext context = Mockito.mock(ApplicationContext.class); + Mockito.when(context.getBean(RestProducers.class)).thenReturn(new RestProducers()); + try (MockedStatic beanUtilsMockedStatic = Mockito.mockStatic(BeanUtils.class)) { + beanUtilsMockedStatic.when(BeanUtils::getContext).thenReturn(context); + RestProducerProvider restProducerProvider = new RestProducerProvider(); + restProducerProvider.init(); + Assertions.assertEquals(RestConst.REST, restProducerProvider.getName()); + } + } +} diff --git a/providers/provider-rest-common/src/test/resources/log4j.properties b/providers/provider-rest-common/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/providers/provider-rest-common/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/providers/provider-springmvc/pom.xml b/providers/provider-springmvc/pom.xml index 999ee82ca11..4792027cc64 100644 --- a/providers/provider-springmvc/pom.xml +++ b/providers/provider-springmvc/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb - providers - 0.3.0-SNAPSHOT - - provider-springmvc + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + + org.apache.servicecomb + providers + 3.4.0-SNAPSHOT + + provider-springmvc + Java Chassis::Providers::Spring MVC - - - io.servicecomb - provider-rest-common - - - io.servicecomb - swagger-invocation-springmvc - - - io.servicecomb - swagger-generator-springmvc - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + + + org.apache.servicecomb + provider-rest-common + + + org.apache.servicecomb + registry-local + test + + + org.apache.servicecomb + swagger-invocation-springmvc + + + org.apache.servicecomb + swagger-generator-springmvc + + + + com.google.code.findbugs + jsr305 + + + org.apache.servicecomb + foundation-test-scaffolding + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.mockito + mockito-inline + test + + diff --git a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/AcceptableRestTemplate.java b/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/AcceptableRestTemplate.java deleted file mode 100644 index 6673e852a0f..00000000000 --- a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/AcceptableRestTemplate.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import java.net.URI; - -import org.springframework.web.client.RestTemplate; - -public abstract class AcceptableRestTemplate extends RestTemplate { - public abstract boolean isAcceptable(String uri); - - public abstract boolean isAcceptable(URI uri); -} diff --git a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java b/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java deleted file mode 100644 index d2158819d9e..00000000000 --- a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.URI; -import java.util.List; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.client.ClientHttpRequest; -import org.springframework.http.client.ClientHttpResponse; - -import io.netty.handler.codec.http.QueryStringDecoder; -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.common.rest.codec.LocalRestServerRequest; -import io.servicecomb.common.rest.codec.RestServerRequest; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.common.rest.definition.RestParam; -import io.servicecomb.common.rest.locator.OperationLocator; -import io.servicecomb.common.rest.locator.ServicePathManager; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.invocation.InvocationFactory; -import io.servicecomb.core.provider.consumer.InvokerUtils; -import io.servicecomb.core.provider.consumer.ReferenceConfig; -import io.servicecomb.core.provider.consumer.ReferenceConfigUtils; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.context.InvocationContext; -import io.servicecomb.swagger.invocation.exception.ExceptionFactory; - -public class CseClientHttpRequest extends OutputStream implements ClientHttpRequest { - - private static final Logger LOGGER = LoggerFactory.getLogger(CseClientHttpRequest.class); - - private URI uri; - - private HttpMethod method; - - private HttpHeaders httpHeaders = new HttpHeaders(); - - private InvocationContext context; - - private Object requestBody; - - public CseClientHttpRequest() { - } - - public CseClientHttpRequest(URI uri, HttpMethod method) { - this.uri = uri; - this.method = method; - } - - public InvocationContext getContext() { - return context; - } - - public void setContext(InvocationContext context) { - this.context = context; - } - - /** - * 不支持,从outputStream继承,仅仅是为了在CseHttpMessageConverter中,将requestBody保存进来而已 - */ - @Override - public void write(int b) throws IOException { - throw new Error("not support"); - } - - public void setRequestBody(Object requestBody) { - this.requestBody = requestBody; - } - - @Override - public HttpMethod getMethod() { - return method; - } - - @Override - public URI getURI() { - return uri; - } - - @Override - public HttpHeaders getHeaders() { - return httpHeaders; - } - - @Override - public OutputStream getBody() throws IOException { - return this; - } - - @Override - public ClientHttpResponse execute() throws IOException { - RequestMeta requestMeta = createRequestMeta(method.name(), uri); - - QueryStringDecoder queryStringDecoder = new QueryStringDecoder(uri.getRawSchemeSpecificPart()); - Map> queryParams = queryStringDecoder.parameters(); - - Object[] args = this.collectArguments(requestMeta, queryParams); - - // 异常流程,直接抛异常出去 - return this.invoke(requestMeta, args); - } - - /** - * 处理调用URL - * URL格式:cse://microserviceName/业务url - */ - private RequestMeta createRequestMeta(String httpMetod, URI uri) { - String microserviceName = uri.getAuthority(); - ReferenceConfig referenceConfig = ReferenceConfigUtils.getForInvoke(microserviceName); - - MicroserviceMeta microserviceMeta = referenceConfig.getMicroserviceMeta(); - ServicePathManager servicePathManager = ServicePathManager.getServicePathManager(microserviceMeta); - if (servicePathManager == null) { - throw new Error(String.format("no schema defined for %s:%s", - microserviceMeta.getAppId(), - microserviceMeta.getName())); - } - - OperationLocator locator = servicePathManager.consumerLocateOperation(uri.getPath(), httpMetod); - RestOperationMeta swaggerRestOperation = locator.getOperation(); - - Map pathParams = locator.getPathVarMap(); - return new RequestMeta(referenceConfig, swaggerRestOperation, pathParams); - } - - private CseClientHttpResponse invoke(RequestMeta requestMeta, Object[] args) { - Invocation invocation = - InvocationFactory.forConsumer(requestMeta.getReferenceConfig(), - requestMeta.getOperationMeta(), - args); - invocation.getHandlerContext().put(RestConst.REST_CLIENT_REQUEST_PATH, - this.uri.getRawPath() + "?" + this.uri.getRawQuery()); - - if (context != null) { - invocation.addContext(context); - } - - Response response = doInvoke(invocation); - - if (response.isSuccessed()) { - return new CseClientHttpResponse(response); - } - - throw ExceptionFactory.convertConsumerException((Throwable) response.getResult()); - } - - protected Response doInvoke(Invocation invocation) { - return InvokerUtils.innerSyncInvoke(invocation); - } - - private Object[] collectArguments(RequestMeta requestMeta, Map> queryParams) { - RestServerRequest mockRequest = - new LocalRestServerRequest(requestMeta.getPathParams(), queryParams, httpHeaders, requestBody); - List paramList = requestMeta.getSwaggerRestOperation().getParamList(); - Object[] args = new Object[paramList.size()]; - for (int idx = 0; idx < paramList.size(); idx++) { - RestParam param = paramList.get(idx); - try { - args[idx] = param.getParamProcessor().getValue(mockRequest); - } catch (Exception e) { - LOGGER.error("error arguments for operation " - + requestMeta.getOperationMeta().getMicroserviceQualifiedName(), - e); - throw new Error(e); - } - } - - return args; - } -} diff --git a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseClientHttpRequestFactory.java b/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseClientHttpRequestFactory.java deleted file mode 100644 index 437248720b5..00000000000 --- a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseClientHttpRequestFactory.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import java.io.IOException; -import java.net.URI; - -import org.springframework.http.HttpMethod; -import org.springframework.http.client.ClientHttpRequest; -import org.springframework.http.client.ClientHttpRequestFactory; - -public class CseClientHttpRequestFactory implements ClientHttpRequestFactory { - @Override - public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { - return new CseClientHttpRequest(uri, httpMethod); - } -} diff --git a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseClientHttpResponse.java b/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseClientHttpResponse.java deleted file mode 100644 index 8e47b5dc7cb..00000000000 --- a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseClientHttpResponse.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.Map.Entry; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.client.ClientHttpResponse; - -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.response.Headers; - -/** - * cse应答在transport层已经完成了码流到对象的转换 - * 这里是适配springmvc的机制,让调用者能拿到已经转换好的对象 - */ -public class CseClientHttpResponse implements ClientHttpResponse { - // 让springmvc client以为应答有body - // mark、reset都是有锁的,这里通过重写取消了锁 - private static final InputStream BODY_INPUT_STREAM = new InputStream() { - public boolean markSupported() { - return true; - } - - @Override - public void mark(int readlimit) { - } - - @Override - public int read() throws IOException { - return 0; - } - - public void reset() throws IOException { - } - }; - - private Response response; - - private HttpHeaders httpHeaders; - - public CseClientHttpResponse(Response response) { - this.response = response; - } - - public Object getResult() { - return response.getResult(); - } - - @Override - public InputStream getBody() throws IOException { - return BODY_INPUT_STREAM; - } - - @Override - public HttpHeaders getHeaders() { - if (httpHeaders == null) { - HttpHeaders tmpHeaders = new HttpHeaders(); - // 让spring mvc有body - tmpHeaders.setContentLength(1); - - Headers headers = response.getHeaders(); - if (headers.getHeaderMap() != null) { - for (Entry> entry : headers.getHeaderMap().entrySet()) { - for (Object value : entry.getValue()) { - tmpHeaders.add(entry.getKey(), String.valueOf(value)); - } - } - } - - httpHeaders = tmpHeaders; - } - return httpHeaders; - } - - @Override - public HttpStatus getStatusCode() throws IOException { - // TODO:springmvc不允许自定义http错误码 - return HttpStatus.valueOf(response.getStatusCode()); - } - - @Override - public int getRawStatusCode() throws IOException { - return response.getStatusCode(); - } - - @Override - public String getStatusText() throws IOException { - return response.getReasonPhrase(); - } - - @Override - public void close() { - } -} diff --git a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseHttpEntity.java b/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseHttpEntity.java deleted file mode 100644 index fd63e78e33a..00000000000 --- a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseHttpEntity.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.provider.springmvc.reference; - -import org.springframework.http.HttpEntity; -import org.springframework.util.MultiValueMap; - -import io.servicecomb.swagger.invocation.context.InvocationContext; - -public class CseHttpEntity extends HttpEntity { - - private InvocationContext context; - - public CseHttpEntity(T body) { - super(body); - } - - public CseHttpEntity(MultiValueMap headers) { - super(headers); - } - - public CseHttpEntity(T body, MultiValueMap headers) { - super(body, headers); - } - - /** - * 获取context的值 - * @return 返回 context - */ - public InvocationContext getContext() { - return context; - } - - /** - * 对context进行赋值 - * @param context context的新值 - */ - public void setContext(InvocationContext context) { - this.context = context; - } - - public void addContext(String key, String value) { - if (context == null) { - context = new InvocationContext(); - } - - context.addContext(key, value); - } -} diff --git a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseRequestCallback.java b/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseRequestCallback.java deleted file mode 100644 index dfd63c5efdc..00000000000 --- a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseRequestCallback.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.provider.springmvc.reference; - -import java.io.IOException; - -import org.springframework.http.client.ClientHttpRequest; -import org.springframework.web.client.RequestCallback; - -public class CseRequestCallback implements RequestCallback { - private Object requestBody; - - private RequestCallback orgCallback; - - public CseRequestCallback(Object requestBody, RequestCallback orgCallback) { - this.requestBody = requestBody; - this.orgCallback = orgCallback; - } - - /** - * {@inheritDoc} - */ - @Override - public void doWithRequest(ClientHttpRequest request) throws IOException { - orgCallback.doWithRequest(request); - - if (!CseHttpEntity.class.isInstance(requestBody)) { - return; - } - - CseClientHttpRequest req = (CseClientHttpRequest) request; - CseHttpEntity entity = (CseHttpEntity) requestBody; - req.setContext(entity.getContext()); - } -} diff --git a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseRestTemplate.java b/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseRestTemplate.java deleted file mode 100644 index 97f5b7b3420..00000000000 --- a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseRestTemplate.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import java.lang.reflect.Type; -import java.net.URI; -import java.util.Arrays; - -import org.springframework.web.client.CseHttpMessageConverter; -import org.springframework.web.client.RequestCallback; - -import io.servicecomb.common.rest.RestConst; - -public class CseRestTemplate extends AcceptableRestTemplate { - public CseRestTemplate() { - setMessageConverters(Arrays.asList(new CseHttpMessageConverter())); - setRequestFactory(new CseClientHttpRequestFactory()); - setUriTemplateHandler(new CseUriTemplateHandler()); - } - - @Override - protected RequestCallback httpEntityCallback(Object requestBody) { - RequestCallback callback = super.httpEntityCallback(requestBody); - CseRequestCallback cseCallback = new CseRequestCallback(requestBody, callback); - return cseCallback; - } - - @Override - protected RequestCallback httpEntityCallback(Object requestBody, Type responseType) { - RequestCallback callback = super.httpEntityCallback(requestBody, responseType); - CseRequestCallback cseCallback = new CseRequestCallback(requestBody, callback); - return cseCallback; - } - - @Override - public boolean isAcceptable(String uri) { - return uri.startsWith(RestConst.URI_PREFIX); - } - - @Override - public boolean isAcceptable(URI uri) { - return RestConst.SCHEME.equals(uri.getScheme()); - } -} diff --git a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseUriTemplateHandler.java b/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseUriTemplateHandler.java deleted file mode 100644 index dd428db235c..00000000000 --- a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/CseUriTemplateHandler.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.provider.springmvc.reference; - -import java.lang.reflect.Field; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Map; - -import org.springframework.util.ReflectionUtils; -import org.springframework.web.util.DefaultUriTemplateHandler; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; - -import io.servicecomb.common.rest.RestConst; - -/** - * 默认不支持下面第1个场景,需要做出修正 - * cse://app:ms/path to cse://app/ms/path - * cse://ms/path to cse://ms/path - */ -public class CseUriTemplateHandler extends DefaultUriTemplateHandler { - private Field hostField = ReflectionUtils.findField(UriComponentsBuilder.class, "host"); - - public CseUriTemplateHandler() { - ReflectionUtils.makeAccessible(hostField); - } - - @Override - protected URI expandInternal(String uriTemplate, Map uriVariables) { - UriComponentsBuilder uriComponentsBuilder = initUriComponentsBuilder(uriTemplate); - UriComponents uriComponents = expandAndEncode(uriComponentsBuilder, uriVariables); - return createUri(uriTemplate, uriComponentsBuilder, uriComponents); - } - - @Override - protected URI expandInternal(String uriTemplate, Object... uriVariables) { - UriComponentsBuilder uriComponentsBuilder = initUriComponentsBuilder(uriTemplate); - UriComponents uriComponents = expandAndEncode(uriComponentsBuilder, uriVariables); - return createUri(uriTemplate, uriComponentsBuilder, uriComponents); - } - - private URI createUri(String uriTemplate, UriComponentsBuilder builder, UriComponents uriComponents) { - String strUri = uriComponents.toUriString(); - - if (isCrossApp(uriTemplate, builder)) { - int idx = strUri.indexOf('/', RestConst.URI_PREFIX.length()); - strUri = strUri.substring(0, idx) + ":" + strUri.substring(idx + 1); - } - - try { - // Avoid further encoding (in the case of strictEncoding=true) - return new URI(strUri); - } catch (URISyntaxException ex) { - throw new IllegalStateException("Could not create URI object: " + ex.getMessage(), ex); - } - } - - protected boolean isCrossApp(String uriTemplate, UriComponentsBuilder builder) { - String host = (String) ReflectionUtils.getField(hostField, builder); - int pos = RestConst.URI_PREFIX.length() + host.length(); - return uriTemplate.charAt(pos) == ':'; - } -} diff --git a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/RequestMeta.java b/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/RequestMeta.java deleted file mode 100644 index 0d2e779fde8..00000000000 --- a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/RequestMeta.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import java.util.Map; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.provider.consumer.ReferenceConfig; - -/** - * 封装每一次调用的元数据 - */ -public class RequestMeta { - private ReferenceConfig referenceConfig; - - private OperationMeta operationMeta; - - private RestOperationMeta swaggerRestOperation; - - private Map pathParams; - - public RequestMeta(ReferenceConfig referenceConfig, RestOperationMeta swaggerRestOperation, - Map pathParams) { - this.referenceConfig = referenceConfig; - this.operationMeta = swaggerRestOperation.getOperationMeta(); - this.swaggerRestOperation = swaggerRestOperation; - this.pathParams = pathParams; - } - - public ReferenceConfig getReferenceConfig() { - return referenceConfig; - } - - public Map getPathParams() { - return pathParams; - } - - public RestOperationMeta getSwaggerRestOperation() { - return swaggerRestOperation; - } - - public OperationMeta getOperationMeta() { - return operationMeta; - } - - public String getOperationQualifiedName() { - return operationMeta.getSchemaQualifiedName(); - } -} diff --git a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/RestTemplateBuilder.java b/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/RestTemplateBuilder.java deleted file mode 100644 index 080ea2f9575..00000000000 --- a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/RestTemplateBuilder.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import org.springframework.web.client.RestTemplate; - -public final class RestTemplateBuilder { - private static final RestTemplateWrapper wrapper = new RestTemplateWrapper(); - - private RestTemplateBuilder() { - } - - public static RestTemplate create() { - return wrapper; - } - - public static void addAcceptableRestTemplate(int index, AcceptableRestTemplate restTemplate) { - wrapper.addAcceptableRestTemplate(index, restTemplate); - } -} diff --git a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfig.java b/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfig.java deleted file mode 100644 index 84b3789024f..00000000000 --- a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfig.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; - -@Configuration -class ServiceCombRestTemplateConfig { - @Bean - RestTemplate restTemplate() { - return RestTemplateBuilder.create(); - } -} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/AcceptableRestTemplate.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/AcceptableRestTemplate.java new file mode 100644 index 00000000000..56d639cc44e --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/AcceptableRestTemplate.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.provider.springmvc.reference; + +import java.net.URI; + +import org.springframework.web.client.RestTemplate; + +public abstract class AcceptableRestTemplate extends RestTemplate { + public abstract boolean isAcceptable(String uri); + + public abstract boolean isAcceptable(URI uri); +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CommonToHttpServletRequest.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CommonToHttpServletRequest.java new file mode 100644 index 00000000000..e1cd368eb7c --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CommonToHttpServletRequest.java @@ -0,0 +1,228 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.provider.springmvc.reference; + +import static org.apache.servicecomb.foundation.common.utils.PartUtils.getSinglePart; + +import java.io.IOException; +import java.net.HttpCookie; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.foundation.vertx.http.AbstractHttpServletRequest; + +import com.google.common.annotations.VisibleForTesting; + +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.HttpHeaders; + +// restTemplate convert parameters to invocation args. +public class CommonToHttpServletRequest extends AbstractHttpServletRequest { + private final Map> queryParams; + + private final Map> httpHeaders; + + //contains all the file key in the parts + private List fileKeys = new ArrayList<>(); + + // gen by httpHeaders + private Cookie[] cookies; + + @SuppressWarnings("unchecked") + public CommonToHttpServletRequest(Map pathParams, Map> queryParams, + Map> httpHeaders, Object bodyObject, List fileKeys) { + setAttribute(RestConst.PATH_PARAMETERS, pathParams); + this.fileKeys = fileKeys; + setAttribute(RestConst.BODY_PARAMETER, bodyObject); + + this.queryParams = queryParams; + this.httpHeaders = httpHeaders; + } + + @SuppressWarnings("unchecked") + public CommonToHttpServletRequest(Map pathParams, Map> queryParams, + Map> httpHeaders, Object bodyObject) { + this(pathParams, queryParams, httpHeaders, bodyObject, null); + } + + @Override + public String getContentType() { + return getHeader(HttpHeaders.CONTENT_TYPE); + } + + // not include form data, only for query + @Override + public String getParameter(String name) { + List queryValues = queryParams.get(name); + if (queryValues == null || queryValues.isEmpty()) { + return null; + } + + return queryValues.get(0); + } + + // not include form data, only for query + @Override + public String[] getParameterValues(String name) { + List queryValues = queryParams.get(name); + if (queryValues == null || queryValues.isEmpty()) { + return new String[0]; + } + + return queryValues.toArray(new String[0]); + } + + @Override + public String getHeader(String name) { + List headerValues = httpHeaders.get(name); + if (headerValues == null || headerValues.isEmpty()) { + return null; + } + + return headerValues.get(0); + } + + @Override + public Enumeration getHeaders(String name) { + List headerValues = httpHeaders.get(name); + if (headerValues == null || headerValues.isEmpty()) { + return Collections.emptyEnumeration(); + } + + return Collections.enumeration(headerValues); + } + + @Override + public Enumeration getHeaderNames() { + return Collections.enumeration(httpHeaders.keySet()); + } + + @Override + public Cookie[] getCookies() { + if (cookies == null) { + cookies = createCookies(); + } + + return cookies; + } + + private Cookie[] createCookies() { + List strCookies = httpHeaders.get(HttpHeaders.COOKIE); + if (strCookies == null) { + return new Cookie[] {}; + } + + List result = new ArrayList<>(); + for (String strCookie : strCookies) { + List httpCookies = HttpCookie.parse(strCookie); + for (HttpCookie httpCookie : httpCookies) { + Cookie cookie = new Cookie(httpCookie.getName(), httpCookie.getValue()); + result.add(cookie); + } + } + + return result.toArray(new Cookie[0]); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return null; + } + + @Override + public void setHeader(String name, String value) { + httpHeaders.put(name, Arrays.asList(value)); + } + + @Override + public void addHeader(String name, String value) { + List list = httpHeaders.computeIfAbsent(name, key -> new ArrayList<>()); + list.add(value); + } + + @Override + public Part getPart(String name) { + Object value = findPartInputValue(name); + return getSinglePart(name, value); + } + + @Override + public Collection getParts() { + @SuppressWarnings("unchecked") + Map form = (Map) getAttribute(RestConst.BODY_PARAMETER); + List partList = new ArrayList<>(); + filePartListWithForm(partList, form); + return partList; + } + + private void filePartListWithForm(List partList, Map form) { + for (String key : fileKeys) { + Object value = form.get(key); + if (value == null) { + continue; + } + if (Collection.class.isInstance(value)) { + Collection collection = (Collection) value; + for (Object part : collection) { + partList.add(getSinglePart(key, part)); + } + continue; + } + if (value.getClass().isArray()) { + Object[] params = (Object[]) value; + for (Object param : params) { + partList.add(getSinglePart(key, param)); + } + continue; + } + partList.add(getSinglePart(key, value)); + } + } + + protected Object findPartInputValue(String name) { + @SuppressWarnings("unchecked") + Map form = (Map) getAttribute(RestConst.BODY_PARAMETER); + Object value = form.get(name); + if (value == null) { + return null; + } + + if (Collection.class.isInstance(value)) { + Collection collection = (Collection) value; + if (collection.isEmpty()) { + return null; + } + + value = collection.iterator().next(); + } + return value; + } + + @VisibleForTesting + public List getFileKeys() { + return fileKeys; + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java new file mode 100644 index 00000000000..ba056e180fb --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java @@ -0,0 +1,269 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.provider.springmvc.reference; + +import java.io.OutputStream; +import java.lang.reflect.Type; +import java.net.URI; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.common.rest.codec.RestCodec; +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.core.Invocation; +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.invocation.InvocationFactory; +import org.apache.servicecomb.core.provider.consumer.InvokerUtils; +import org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig; +import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; +import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +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.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.http.client.ClientHttpResponse; + +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.google.common.annotations.VisibleForTesting; + +import io.netty.handler.codec.http.QueryStringDecoder; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.Response.Status; + +public class CseClientHttpRequest implements ClientHttpRequest { + + // URL format:cse://microserviceName/business url + private URI uri; + + // business url + private String path; + + private HttpMethod method; + + private HttpHeaders httpHeaders = new HttpHeaders(); + + private InvocationContext context; + + private Object requestBody; + + private Map> queryParams; + + private RequestMeta requestMeta; + + private Type responseType; + + public CseClientHttpRequest() { + } + + public CseClientHttpRequest(URI uri, HttpMethod method) { + this.uri = uri; + this.method = method; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + protected RequestMeta getRequestMeta() { + return requestMeta; + } + + @VisibleForTesting + public void setRequestMeta(RequestMeta requestMeta) { + this.requestMeta = requestMeta; + } + + public void setUri(URI uri) { + this.uri = uri; + } + + public void setMethod(HttpMethod method) { + this.method = method; + } + + protected void setQueryParams(Map> queryParams) { + this.queryParams = queryParams; + } + + public InvocationContext getContext() { + return context; + } + + public void setContext(InvocationContext context) { + this.context = context; + } + + public Type getResponseType() { + return responseType; + } + + public void setResponseType(Type responseType) { + this.responseType = responseType; + } + + public void setRequestBody(Object requestBody) { + this.requestBody = requestBody; + } + + public void setHttpHeaders(HttpHeaders headers) { + if (headers != null) { + this.httpHeaders = headers; + } + } + + @Override + public HttpMethod getMethod() { + return method; + } + + @Override + public URI getURI() { + return uri; + } + + @Override + public Map getAttributes() { + return Collections.emptyMap(); + } + + @Override + public HttpHeaders getHeaders() { + return httpHeaders; + } + + @Override + public OutputStream getBody() { + return null; + } + + @Override + public ClientHttpResponse execute() { + path = findUriPath(uri); + requestMeta = createRequestMeta(method.name(), uri); + + QueryStringDecoder queryStringDecoder = new QueryStringDecoder(uri.getRawSchemeSpecificPart()); + queryParams = queryStringDecoder.parameters(); + + Map swaggerArguments = this.collectArguments(); + + // 异常流程,直接抛异常出去 + return this.invoke(swaggerArguments); + } + + protected RequestMeta createRequestMeta(String httpMethod, URI uri) { + String microserviceName = parseMicroserviceName(uri); + + MicroserviceReferenceConfig microserviceReferenceConfig = SCBEngine.getInstance() + .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(); + + ServicePathManager servicePathManager = ServicePathManager.getServicePathManager(microserviceMeta); + if (servicePathManager == null) { + throw new Error(String.format("no schema defined for %s:%s", + microserviceMeta.getAppId(), + microserviceMeta.getMicroserviceName())); + } + + OperationLocator locator = servicePathManager.consumerLocateOperation(path, httpMethod); + RestOperationMeta swaggerRestOperation = locator.getOperation(); + + OperationMeta operationMeta = locator.getOperation().getOperationMeta(); + ReferenceConfig referenceConfig = microserviceReferenceConfig.createReferenceConfig(operationMeta); + + Map pathParams = locator.getPathVarMap(); + return new RequestMeta(referenceConfig, swaggerRestOperation, pathParams); + } + + private String parseMicroserviceName(URI uri) { + String microserviceName = uri.getAuthority(); + return microserviceName.replace(CseUriTemplateHandler.APP_SERVICE_SEPARATOR_INTERNAL, + DefinitionConst.APP_SERVICE_SEPARATOR); + } + + protected String findUriPath(URI uri) { + return uri.getRawPath(); + } + + protected Invocation prepareInvocation(Map swaggerArguments) { + Invocation invocation = + InvocationFactory.forConsumer(requestMeta.getReferenceConfig(), + requestMeta.getOperationMeta(), + requestMeta.getOperationMeta().buildBaseConsumerRuntimeType(), + swaggerArguments); + + invocation.getHandlerContext().put(RestConst.REST_CLIENT_REQUEST_PATH, + path + (this.uri.getRawQuery() == null ? "" : "?" + this.uri.getRawQuery())); + + if (context != null) { + invocation.addContext(context.getContext()); + invocation.addLocalContext(context.getLocalContext()); + } + + if (responseType != null && + !(responseType instanceof Class && Part.class.isAssignableFrom((Class) responseType))) { + invocation.setSuccessResponseType(TypeFactory.defaultInstance().constructType(responseType)); + } + + invocation.getHandlerContext().put(RestConst.CONSUMER_HEADER, httpHeaders.toSingleValueMap()); + return invocation; + } + + @VisibleForTesting + CseClientHttpResponse invoke(Map swaggerArguments) { + Invocation invocation = prepareInvocation(swaggerArguments); + Response response = doInvoke(invocation); + + if (response.isSucceed()) { + return new CseClientHttpResponse(response); + } + + throw ExceptionFactory.convertConsumerException(response.getResult()); + } + + protected Response doInvoke(Invocation invocation) { + return InvokerUtils.innerSyncInvoke(invocation); + } + + protected Map collectArguments() { + HttpServletRequest mockRequest = new CommonToHttpServletRequest(requestMeta.getPathParams(), queryParams, + httpHeaders, requestBody, + requestMeta.getSwaggerRestOperation().getFileKeys()); + // no types info, so will not convert any parameters + return RestCodec.restToArgs(mockRequest, requestMeta.getSwaggerRestOperation()); + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequestFactory.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequestFactory.java new file mode 100644 index 00000000000..f5967d74ce0 --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequestFactory.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.provider.springmvc.reference; + +import java.io.IOException; +import java.net.URI; + +import org.springframework.http.HttpMethod; +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.http.client.ClientHttpRequestFactory; + +public class CseClientHttpRequestFactory implements ClientHttpRequestFactory { + @Override + public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { + return new CseClientHttpRequest(uri, httpMethod); + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpResponse.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpResponse.java new file mode 100644 index 00000000000..e3caf103b09 --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpResponse.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.provider.springmvc.reference; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map.Entry; + +import org.apache.servicecomb.swagger.invocation.Response; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.client.ClientHttpResponse; + +import io.vertx.core.MultiMap; + +/** + * cse应答在transport层已经完成了码流到对象的转换 + * 这里是适配springmvc的机制,让调用者能拿到已经转换好的对象 + */ +public class CseClientHttpResponse implements ClientHttpResponse { + // 让springmvc client以为应答有body + // mark、reset都是有锁的,这里通过重写取消了锁 + private static final InputStream BODY_INPUT_STREAM = new InputStream() { + @Override + public boolean markSupported() { + return true; + } + + @Override + public void mark(int readlimit) { + } + + @Override + public int read() throws IOException { + return 0; + } + + @Override + public void reset() throws IOException { + } + }; + + private final Response response; + + private HttpHeaders httpHeaders; + + public CseClientHttpResponse(Response response) { + this.response = response; + } + + public Object getResult() { + return response.getResult(); + } + + @Override + public InputStream getBody() throws IOException { + return BODY_INPUT_STREAM; + } + + @Override + public HttpHeaders getHeaders() { + if (httpHeaders == null) { + HttpHeaders tmpHeaders = new HttpHeaders(); + MultiMap headers = response.getHeaders(); + if (headers != null) { + for (Entry entry : headers.entries()) { + tmpHeaders.add(entry.getKey(), entry.getValue()); + } + } + + httpHeaders = tmpHeaders; + } + return httpHeaders; + } + + @Override + public HttpStatus getStatusCode() throws IOException { + return HttpStatus.valueOf(response.getStatusCode()); + } + + @Override + @Deprecated(since = "6.0") + @SuppressWarnings("deprecations") + public int getRawStatusCode() throws IOException { + return response.getStatusCode(); + } + + @Override + public String getStatusText() throws IOException { + return response.getReasonPhrase(); + } + + @Override + public void close() { + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseHttpEntity.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseHttpEntity.java new file mode 100644 index 00000000000..d0551bc95c0 --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseHttpEntity.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.provider.springmvc.reference; + +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +import org.springframework.http.HttpEntity; +import org.springframework.util.MultiValueMap; + +public class CseHttpEntity extends HttpEntity { + + private InvocationContext context; + + public CseHttpEntity(T body) { + super(body); + } + + public CseHttpEntity(MultiValueMap headers) { + super(headers); + } + + public CseHttpEntity(T body, MultiValueMap headers) { + super(body, headers); + } + + /** + * 获取context的值 + * @return 返回 context + */ + public InvocationContext getContext() { + return context; + } + + /** + * 对context进行赋值 + * @param context context的新值 + */ + public void setContext(InvocationContext context) { + this.context = context; + } + + public void addContext(String key, String value) { + if (context == null) { + context = new InvocationContext(); + } + + context.addContext(key, value); + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseHttpMessageConverter.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseHttpMessageConverter.java new file mode 100644 index 00000000000..018abc9c3d5 --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseHttpMessageConverter.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.provider.springmvc.reference; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.List; + +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.MediaType; +import org.springframework.http.converter.GenericHttpMessageConverter; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; +import org.springframework.lang.Nullable; + +public class CseHttpMessageConverter implements GenericHttpMessageConverter { + + private static final List ALL_MEDIA_TYPE = Arrays.asList(MediaType.ALL); + + @Override + public boolean canRead(Class clazz, MediaType mediaType) { + return true; + } + + @Override + public boolean canWrite(Class clazz, MediaType mediaType) { + return true; + } + + @Override + public List getSupportedMediaTypes() { + return ALL_MEDIA_TYPE; + } + + @Override + public Object read(Class clazz, HttpInputMessage inputMessage) throws HttpMessageNotReadableException { + throw new IllegalStateException("not supported"); + } + + private Object read(HttpInputMessage inputMessage) { + throw new IllegalStateException("not supported"); + } + + @Override + public void write(Object o, MediaType contentType, + HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { + write(o, outputMessage); + } + + private void write(Object o, HttpOutputMessage outputMessage) { + CseClientHttpRequest request = (CseClientHttpRequest) outputMessage; + request.setRequestBody(o); + } + + @Override + public boolean canRead(Type type, @Nullable Class contextClass, @Nullable MediaType mediaType) { + return true; + } + + @Override + public Object read(Type type, @Nullable Class contextClass, HttpInputMessage inputMessage) + throws IOException, HttpMessageNotReadableException { + throw new IllegalStateException("not supported"); + } + + @Override + public boolean canWrite(@Nullable Type type, Class clazz, @Nullable MediaType mediaType) { + return true; + } + + @Override + public void write(Object o, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage) + throws IOException, HttpMessageNotWritableException { + write(o, outputMessage); + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseHttpMessageConverterExtractor.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseHttpMessageConverterExtractor.java new file mode 100644 index 00000000000..402c714a932 --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseHttpMessageConverterExtractor.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.provider.springmvc.reference; + +import java.io.IOException; + +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.ResponseExtractor; + +public class CseHttpMessageConverterExtractor implements ResponseExtractor { + @Override + @SuppressWarnings("unchecked") + public T extractData(ClientHttpResponse response) throws IOException { + return (T) ((CseClientHttpResponse) response).getResult(); + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRequestCallback.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRequestCallback.java new file mode 100644 index 00000000000..fd1e33cb967 --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRequestCallback.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.provider.springmvc.reference; + +import java.io.IOException; +import java.lang.reflect.Type; + +import org.apache.servicecomb.foundation.common.utils.GenericsUtils; +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.web.client.RequestCallback; + +public class CseRequestCallback implements RequestCallback { + private final Object requestBody; + + private final RequestCallback targetCallback; + + private final Type responseType; + + public CseRequestCallback(Object requestBody, RequestCallback targetCallback, Type responseType) { + this.requestBody = requestBody; + this.targetCallback = targetCallback; + this.responseType = responseType; + } + + /** + * {@inheritDoc} + */ + @Override + public void doWithRequest(ClientHttpRequest request) throws IOException { + targetCallback.doWithRequest(request); + CseClientHttpRequest req = (CseClientHttpRequest) request; + req.setResponseType(overrideResponseType()); + + if (!CseHttpEntity.class.isInstance(requestBody)) { + return; + } + + CseHttpEntity entity = (CseHttpEntity) requestBody; + req.setContext(entity.getContext()); + } + + private Type overrideResponseType() { + if (GenericsUtils.isGenericResponseType(responseType)) { + // code: List response = restTemplate + // .postForObject("/testListObjectParam", request, List.class); + // will using server schema type to deserialize + return null; + } + // code: MyObject response = .postForObject("/testListObjectParam", request, MyObject.class); + return responseType; + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseResponseEntityResponseExtractor.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseResponseEntityResponseExtractor.java new file mode 100644 index 00000000000..c6626353aa2 --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseResponseEntityResponseExtractor.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.provider.springmvc.reference; + +import java.io.IOException; +import java.lang.reflect.Type; + +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.lang.Nullable; +import org.springframework.web.client.ResponseExtractor; + +public class CseResponseEntityResponseExtractor implements ResponseExtractor> { + @Nullable + private final CseHttpMessageConverterExtractor delegate; + + public CseResponseEntityResponseExtractor(@Nullable Type responseType) { + if (responseType != null && Void.class != responseType) { + this.delegate = new CseHttpMessageConverterExtractor<>(); + } else { + this.delegate = null; + } + } + + @Override + public ResponseEntity extractData(ClientHttpResponse response) throws IOException { + if (this.delegate != null) { + T body = this.delegate.extractData(response); + return ResponseEntity.status(response.getStatusCode().value()).headers(response.getHeaders()).body(body); + } else { + return ResponseEntity.status(response.getStatusCode().value()).headers(response.getHeaders()).build(); + } + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRestTemplate.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRestTemplate.java new file mode 100644 index 00000000000..b4f3923b004 --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRestTemplate.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.provider.springmvc.reference; + +import java.lang.reflect.Type; +import java.net.URI; +import java.util.Arrays; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.Nullable; +import org.springframework.web.client.RequestCallback; +import org.springframework.web.client.ResponseExtractor; +import org.springframework.web.client.RestClientException; + +public class CseRestTemplate extends AcceptableRestTemplate { + public CseRestTemplate() { + setMessageConverters(Arrays.asList(new CseHttpMessageConverter())); + setRequestFactory(new CseClientHttpRequestFactory()); + setUriTemplateHandler(new CseUriTemplateHandler()); + } + + // GET + + @Override + @Nullable + public T getForObject(String url, Class responseType, Object... uriVariables) throws RestClientException { + RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); + CseHttpMessageConverterExtractor responseExtractor = + new CseHttpMessageConverterExtractor<>(); + return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); + } + + @Override + @Nullable + public T getForObject(String url, Class responseType, Map uriVariables) throws RestClientException { + RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); + CseHttpMessageConverterExtractor responseExtractor = + new CseHttpMessageConverterExtractor<>(); + return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); + } + + @Override + @Nullable + public T getForObject(URI url, Class responseType) throws RestClientException { + RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); + CseHttpMessageConverterExtractor responseExtractor = + new CseHttpMessageConverterExtractor<>(); + return execute(url, HttpMethod.GET, requestCallback, responseExtractor); + } + + // HEAD + // no override + + // POST + + @Override + @Nullable + public T postForObject(String url, @Nullable Object request, Class responseType, + Object... uriVariables) throws RestClientException { + + RequestCallback requestCallback = httpEntityCallback(request, responseType); + CseHttpMessageConverterExtractor responseExtractor = + new CseHttpMessageConverterExtractor<>(); + return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); + } + + @Override + @Nullable + public T postForObject(String url, @Nullable Object request, Class responseType, + Map uriVariables) throws RestClientException { + + RequestCallback requestCallback = httpEntityCallback(request, responseType); + CseHttpMessageConverterExtractor responseExtractor = + new CseHttpMessageConverterExtractor<>(); + return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); + } + + @Override + @Nullable + public T postForObject(URI url, @Nullable Object request, Class responseType) + throws RestClientException { + + RequestCallback requestCallback = httpEntityCallback(request, responseType); + CseHttpMessageConverterExtractor responseExtractor = + new CseHttpMessageConverterExtractor<>(); + return execute(url, HttpMethod.POST, requestCallback, responseExtractor); + } + + // PUT + // no override + + // PATCH + + @Override + @Nullable + public T patchForObject(String url, @Nullable Object request, Class responseType, + Object... uriVariables) throws RestClientException { + + RequestCallback requestCallback = httpEntityCallback(request, responseType); + CseHttpMessageConverterExtractor responseExtractor = + new CseHttpMessageConverterExtractor<>(); + return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables); + } + + @Override + @Nullable + public T patchForObject(String url, @Nullable Object request, Class responseType, + Map uriVariables) throws RestClientException { + + RequestCallback requestCallback = httpEntityCallback(request, responseType); + CseHttpMessageConverterExtractor responseExtractor = + new CseHttpMessageConverterExtractor<>(); + return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables); + } + + @Override + @Nullable + public T patchForObject(URI url, @Nullable Object request, Class responseType) + throws RestClientException { + + RequestCallback requestCallback = httpEntityCallback(request, responseType); + CseHttpMessageConverterExtractor responseExtractor = + new CseHttpMessageConverterExtractor<>(); + return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor); + } + + // DELETE + // no override + + // OPTIONS + // no override + + // exchange + // no override + + @Override + public ResponseExtractor> responseEntityExtractor(Type responseType) { + return new CseResponseEntityResponseExtractor<>(responseType); + } + + @Override + public RequestCallback httpEntityCallback(Object requestBody) { + RequestCallback callback = super.httpEntityCallback(requestBody); + CseRequestCallback cseCallback = new CseRequestCallback(requestBody, callback, null); + return cseCallback; + } + + @Override + public RequestCallback httpEntityCallback(Object requestBody, Type responseType) { + RequestCallback callback = super.httpEntityCallback(requestBody, responseType); + CseRequestCallback cseCallback = new CseRequestCallback(requestBody, callback, responseType); + return cseCallback; + } + + @Override + public RequestCallback acceptHeaderRequestCallback(Class responseType) { + RequestCallback callback = super.acceptHeaderRequestCallback(responseType); + CseRequestCallback cseCallback = new CseRequestCallback(null, callback, responseType); + return cseCallback; + } + + @Override + public boolean isAcceptable(String uri) { + return uri.startsWith(RestConst.URI_PREFIX) || uri.startsWith(RestConst.URI_PREFIX_NEW); + } + + @Override + public boolean isAcceptable(URI uri) { + return RestConst.SCHEME.equals(uri.getScheme()) || RestConst.SCHEME_NEW.equals(uri.getScheme()); + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseUriTemplateHandler.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseUriTemplateHandler.java new file mode 100644 index 00000000000..08f2b9da880 --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseUriTemplateHandler.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.provider.springmvc.reference; + +import java.net.URI; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.springframework.web.util.DefaultUriBuilderFactory; + +public class CseUriTemplateHandler extends DefaultUriBuilderFactory { + // Must be one of URI unreserved `ALPHA / DIGIT / "-" / "." / "_" / "~"` + public static final String APP_SERVICE_SEPARATOR_INTERNAL = "~"; + + public CseUriTemplateHandler() { + } + + @Override + public URI expand(String uriTemplate, Map uriVars) { + return super.expand(parseUrl(uriTemplate), uriVars); + } + + @Override + public URI expand(String uriTemplate, Object... uriVars) { + return super.expand(parseUrl(uriTemplate), uriVars); + } + + private static String parseUrl(String uriTemplate) { + int indexSchema = -1; + if (uriTemplate.startsWith(RestConst.URI_PREFIX)) { + indexSchema = RestConst.URI_PREFIX.length(); + } + if (uriTemplate.startsWith(RestConst.URI_PREFIX_NEW)) { + indexSchema = RestConst.URI_PREFIX_NEW.length(); + } + if (indexSchema != -1) { + int indexPath = uriTemplate.indexOf("/", indexSchema); + String host = uriTemplate.substring(indexSchema, indexPath); + host = host.replace(DefinitionConst.APP_SERVICE_SEPARATOR, APP_SERVICE_SEPARATOR_INTERNAL); + return uriTemplate.substring(0, indexSchema) + host + uriTemplate.substring(indexPath); + } + return uriTemplate; + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/RequestMeta.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/RequestMeta.java new file mode 100644 index 00000000000..be4aac4cfa2 --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/RequestMeta.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.provider.springmvc.reference; + +import java.util.Map; + +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; + +/** + * 封装每一次调用的元数据 + */ +public class RequestMeta { + private final ReferenceConfig referenceConfig; + + private final OperationMeta operationMeta; + + private final RestOperationMeta swaggerRestOperation; + + private final Map pathParams; + + public RequestMeta(ReferenceConfig referenceConfig, RestOperationMeta swaggerRestOperation, + Map pathParams) { + this.referenceConfig = referenceConfig; + this.operationMeta = swaggerRestOperation.getOperationMeta(); + this.swaggerRestOperation = swaggerRestOperation; + this.pathParams = pathParams; + } + + public ReferenceConfig getReferenceConfig() { + return referenceConfig; + } + + public Map getPathParams() { + return pathParams; + } + + public RestOperationMeta getSwaggerRestOperation() { + return swaggerRestOperation; + } + + public OperationMeta getOperationMeta() { + return operationMeta; + } + + public String getOperationQualifiedName() { + return operationMeta.getSchemaQualifiedName(); + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/RestTemplateBuilder.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/RestTemplateBuilder.java new file mode 100644 index 00000000000..f8a5ceab17f --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/RestTemplateBuilder.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.provider.springmvc.reference; + +import org.springframework.web.client.RestOperations; + +public final class RestTemplateBuilder { + private static final RestTemplateWrapper wrapper = new RestTemplateWrapper(); + + private RestTemplateBuilder() { + } + + public static RestOperations create() { + return wrapper; + } + + public static void addAcceptableRestTemplate(int index, AcceptableRestTemplate restTemplate) { + wrapper.addAcceptableRestTemplate(index, restTemplate); + } +} diff --git a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/RestTemplateWrapper.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/RestTemplateWrapper.java similarity index 95% rename from providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/RestTemplateWrapper.java rename to providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/RestTemplateWrapper.java index 22c0ab5651f..e91cfb68afd 100644 --- a/providers/provider-springmvc/src/main/java/io/servicecomb/provider/springmvc/reference/RestTemplateWrapper.java +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/RestTemplateWrapper.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +15,7 @@ * limitations under the License. */ -package io.servicecomb.provider.springmvc.reference; +package org.apache.servicecomb.provider.springmvc.reference; import java.net.URI; import java.util.ArrayList; @@ -39,9 +40,8 @@ import org.springframework.web.util.UriTemplateHandler; /** - * 用于同时支持cse调用和非cse调用 + * Wrapper class to support java chassis invocation and non java chassis invocation. */ -// TODO: 2017/7/1 what we want to expose is RestOperations instead, since some RestTemplate methods are not to be called by users class RestTemplateWrapper extends RestTemplate { private final List acceptableRestTemplates = new ArrayList<>(); diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfig.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfig.java new file mode 100644 index 00000000000..f250b9f209c --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfig.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.provider.springmvc.reference; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestOperations; + +@Configuration +class ServiceCombRestTemplateConfig { + @Bean + RestOperations restTemplate() { + return RestTemplateBuilder.create(); + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/UrlWithProviderPrefixClientHttpRequestFactory.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/UrlWithProviderPrefixClientHttpRequestFactory.java new file mode 100644 index 00000000000..8f0ec70ed00 --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/UrlWithProviderPrefixClientHttpRequestFactory.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.provider.springmvc.reference; + +import java.io.IOException; +import java.net.URI; + +import org.springframework.http.HttpMethod; +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.http.client.ClientHttpRequestFactory; + +/** + * When deploying in a container, like tomcat, users want to invoke service with full path, including container context + * root and servlet path. + */ +public class UrlWithProviderPrefixClientHttpRequestFactory implements ClientHttpRequestFactory { + static class UrlWithProviderPrefixClientHttpRequest extends CseClientHttpRequest { + private final String prefix; + + public UrlWithProviderPrefixClientHttpRequest(URI uri, HttpMethod httpMethod, String prefix) { + super(uri, httpMethod); + this.prefix = prefix; + } + + @Override + protected String findUriPath(URI uri) { + return uri.getRawPath().substring(prefix.length()); + } + } + + private final String prefix; + + public UrlWithProviderPrefixClientHttpRequestFactory(String prefix) { + this.prefix = prefix; + } + + @Override + public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { + return new UrlWithProviderPrefixClientHttpRequest(uri, httpMethod, prefix); + } +} diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/UrlWithServiceNameClientHttpRequestFactory.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/UrlWithServiceNameClientHttpRequestFactory.java new file mode 100644 index 00000000000..4da47ecce60 --- /dev/null +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/UrlWithServiceNameClientHttpRequestFactory.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.provider.springmvc.reference; + +import java.io.IOException; +import java.net.URI; + +import org.springframework.http.HttpMethod; +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.http.client.ClientHttpRequestFactory; + +// URL format:cse://business url +// business url first part is microserviceName +public class UrlWithServiceNameClientHttpRequestFactory implements ClientHttpRequestFactory { + static class UrlWithServiceNameClientHttpRequest extends CseClientHttpRequest { + public UrlWithServiceNameClientHttpRequest(URI uri, HttpMethod httpMethod) { + super(uri, httpMethod); + } + + @Override + protected String findUriPath(URI uri) { + return "/" + uri.getAuthority() + uri.getRawPath(); + } + } + + @Override + public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { + return new UrlWithServiceNameClientHttpRequest(uri, httpMethod); + } +} diff --git a/providers/provider-springmvc/src/main/java/org/springframework/web/client/CseHttpMessageConverter.java b/providers/provider-springmvc/src/main/java/org/springframework/web/client/CseHttpMessageConverter.java deleted file mode 100644 index 7a5137375da..00000000000 --- a/providers/provider-springmvc/src/main/java/org/springframework/web/client/CseHttpMessageConverter.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.web.client; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.List; - -import org.springframework.http.HttpInputMessage; -import org.springframework.http.HttpOutputMessage; -import org.springframework.http.MediaType; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.http.converter.HttpMessageNotWritableException; -import org.springframework.util.ReflectionUtils; - -import io.servicecomb.provider.springmvc.reference.CseClientHttpRequest; -import io.servicecomb.provider.springmvc.reference.CseClientHttpResponse; - -/** - * 需要访问MessageBodyClientHttpResponseWrapper - * 这是一个package级别的类,只好放在特殊的包内了 - */ -public class CseHttpMessageConverter implements HttpMessageConverter { - - private static final List ALL_MEDIA_TYPE = Arrays.asList(MediaType.ALL); - - private static final Field RESPONSE_FIELD = - ReflectionUtils.findField(MessageBodyClientHttpResponseWrapper.class, "response"); - - static { - RESPONSE_FIELD.setAccessible(true); - } - - @Override - public boolean canRead(Class clazz, MediaType mediaType) { - return true; - } - - @Override - public boolean canWrite(Class clazz, MediaType mediaType) { - return true; - } - - @Override - public List getSupportedMediaTypes() { - return ALL_MEDIA_TYPE; - } - - @Override - public Object read(Class clazz, - HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { - MessageBodyClientHttpResponseWrapper respWrapper = (MessageBodyClientHttpResponseWrapper) inputMessage; - CseClientHttpResponse resp = - (CseClientHttpResponse) ReflectionUtils.getField(RESPONSE_FIELD, respWrapper); - return resp.getResult(); - } - - @Override - public void write(Object t, MediaType contentType, - HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { - CseClientHttpRequest request = (CseClientHttpRequest) outputMessage; - request.setRequestBody(t); - } -} diff --git a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/common/MockUtil.java b/providers/provider-springmvc/src/test/java/io/servicecomb/provider/common/MockUtil.java deleted file mode 100644 index 5ed5dc578c8..00000000000 --- a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/common/MockUtil.java +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.common; - -import java.lang.reflect.Field; -import java.net.URI; -import java.util.Map; - -import org.mockito.Mockito; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; -import org.springframework.util.ReflectionUtils; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.common.rest.locator.OperationLocator; -import io.servicecomb.common.rest.locator.ServicePathManager; -import io.servicecomb.core.CseContext; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.definition.SchemaUtils; -import io.servicecomb.core.definition.schema.ConsumerSchemaFactory; -import io.servicecomb.core.provider.consumer.ConsumerProviderManager; -import io.servicecomb.core.provider.consumer.InvokerUtils; -import io.servicecomb.core.provider.consumer.ReferenceConfig; -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.provider.springmvc.reference.CseClientHttpRequest; -import io.servicecomb.provider.springmvc.reference.CseClientHttpResponse; -import io.servicecomb.provider.springmvc.reference.RequestMeta; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.InvocationException; -import io.swagger.models.Operation; -import io.swagger.models.Swagger; -import mockit.Mock; -import mockit.MockUp; - -public class MockUtil { - MicroserviceMetaManager microserviceMetaManager = new MicroserviceMetaManager(); - - private static MockUtil instance = new MockUtil(); - - private MockUtil() { - - } - - public static MockUtil getInstance() { - return instance; - } - - public void mockConsumerProviderManager() { - ConsumerProviderManager consumerProviderManager = new MockUp() { - @Mock - public ReferenceConfig getReferenceConfig(String microserviceName) { - return new ReferenceConfig(CseContext.getInstance().getConsumerSchemaFactory(), "test", "test", - "test"); - } - }.getMockInstance(); - - CseContext.getInstance().setConsumerProviderManager(consumerProviderManager); - } - - public void mockRegisterManager() throws InstantiationException, IllegalAccessException { - - MicroserviceMeta microserviceMeta = microserviceMetaManager.getOrCreateMicroserviceMeta("app:test"); - microserviceMeta.putExtData("RestServicePathManager", new ServicePathManager(microserviceMeta)); - - ConsumerSchemaFactory consumerSchemaFactory = new MockUp() { - @Mock - public MicroserviceMeta getOrCreateConsumer(String microserviceName) { - return microserviceMeta; - } - }.getMockInstance(); - CseContext.getInstance().setConsumerSchemaFactory(consumerSchemaFactory); - } - - public void mockServicePathManager() { - - new MockUp() { - @Mock - public OperationLocator locateOperation(String path, String httpMethod) { - OperationLocator locator = new OperationLocator(); - return locator; - } - }; - } - - public void mockOperationLocator() { - - new MockUp() { - @Mock - public RestOperationMeta getOperation() { - RestOperationMeta rom = new RestOperationMeta(); - rom.init(Mockito.mock(OperationMeta.class)); - - return rom; - } - }; - } - - public void mockSchemaMeta() { - - new MockUp() { - @Mock - private void initOperations() throws Exception { - } - }; - } - - public void mockBeanUtils() { - - new MockUp() { - @Mock - T getBean(String name) { - return null; - } - }; - } - - public void mockRequestMeta() { - - new MockUp() { - @Mock - public OperationMeta getOperationMeta() throws Exception { - OperationMeta om = new OperationMeta(); - om.init(new SchemaMeta(null, new MicroserviceMeta("test"), null), - this.getClass().getMethods()[0], - "path", - "get", - null); - - return om; - } - }; - } - - public void mockSchemaUtils() { - - new MockUp() { - @Mock - Class getJavaInterface(String schemaId, Swagger swagger) { - return null; - } - }; - } - - public void mockOperationMeta() { - - new MockUp() { - @Mock - private void initException(SchemaMeta schemaMeta, Operation swaggerOperation) { - } - }; - } - - public void mockInvokerUtils() { - - new MockUp() { - @Mock - Object syncInvoke(Invocation invocation) throws InvocationException { - return invocation; - } - }; - } - - public void mockReflectionUtils() { - - new MockUp() { - @Mock - Object getField(Field field, Object target) { - Response response = Response.ok(200); - return new CseClientHttpResponse(response); - } - }; - } - - public void mockCseClientHttpRequest() { - - new MockUp() { - @Mock - public void setRequestBody(Object requestBody) { - - } - }; - } - - public void mockRestTemplate() { - new MockUp() { - @Mock - public void put(String url, Object request, Object... urlVariables) throws RestClientException { - } - - @Mock - public void put(URI url, Object request) throws RestClientException { - - } - - @Mock - public void delete(URI url) throws RestClientException { - - } - - @Mock - public T getForObject(String url, Class responseType, - Object... urlVariables) throws RestClientException { - return null; - } - - @Mock - public ResponseEntity getForEntity(String url, Class responseType, - Object... urlVariables) throws RestClientException { - return null; - } - - @Mock - public T postForObject(String url, Object request, Class responseType, - Object... uriVariables) throws RestClientException { - return null; - } - - @Mock - public ResponseEntity postForEntity(String url, Object request, Class responseType, - Object... uriVariables) throws RestClientException { - return null; - } - - @Mock - public ResponseEntity exchange(String url, HttpMethod method, - HttpEntity requestEntity, Class responseType, - Object... uriVariables) throws RestClientException { - return null; - } - - @Mock - public void delete(String url, Object... urlVariables) throws RestClientException { - - } - - @Mock - public void delete(String url, Map urlVariables) throws RestClientException { - - } - - @Mock - public T getForObject(String url, Class responseType, - Map urlVariables) throws RestClientException { - return null; - } - - @Mock - public ResponseEntity getForEntity(String url, Class responseType, - Map urlVariables) throws RestClientException { - return null; - } - - @Mock - public T getForObject(URI url, Class responseType) throws RestClientException { - return null; - } - - @Mock - public ResponseEntity getForEntity(URI url, Class responseType) throws RestClientException { - return null; - } - - @Mock - public T postForObject(String url, Object request, Class responseType, - Map uriVariables) throws RestClientException { - return null; - } - - @Mock - public T postForObject(URI url, Object request, Class responseType) throws RestClientException { - return null; - } - - @Mock - public ResponseEntity postForEntity(String url, Object request, Class responseType, - Map uriVariables) throws RestClientException { - return null; - } - - @Mock - public ResponseEntity postForEntity(URI url, Object request, - Class responseType) throws RestClientException { - return null; - } - - @Mock - public void put(String url, Object request, Map urlVariables) throws RestClientException { - - } - - @Mock - public ResponseEntity exchange(String url, HttpMethod method, - HttpEntity requestEntity, Class responseType, - Map uriVariables) throws RestClientException { - return null; - } - - @Mock - public ResponseEntity exchange(String url, HttpMethod method, HttpEntity requestEntity, - ParameterizedTypeReference responseType, - Map uriVariables) throws RestClientException { - return null; - } - - @Mock - public ResponseEntity exchange(String url, HttpMethod method, HttpEntity requestEntity, - ParameterizedTypeReference responseType, Object... uriVariables) throws RestClientException { - return null; - } - - @Mock - public ResponseEntity exchange(RequestEntity requestEntity, - Class responseType) throws RestClientException { - return null; - } - - @Mock - public ResponseEntity exchange(RequestEntity requestEntity, - ParameterizedTypeReference responseType) throws RestClientException { - return null; - } - - @Mock - public ResponseEntity exchange(URI url, HttpMethod method, HttpEntity requestEntity, - Class responseType) throws RestClientException { - return null; - } - - @Mock - public ResponseEntity exchange(URI url, HttpMethod method, HttpEntity requestEntity, - ParameterizedTypeReference responseType) throws RestClientException { - return null; - } - }; - } -} diff --git a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfigTest.java b/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfigTest.java deleted file mode 100644 index f27c1a81d98..00000000000 --- a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfigTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.junit.Assert.assertThat; - -import org.junit.Test; -import org.springframework.web.client.RestTemplate; - -public class ServiceCombRestTemplateConfigTest { - - private final ServiceCombRestTemplateConfig config = new ServiceCombRestTemplateConfig(); - - @Test - public void exposesServiceCombRestTemplate() { - RestTemplate restTemplate = config.restTemplate(); - - assertThat(restTemplate, instanceOf(RestTemplateWrapper.class)); - } -} \ No newline at end of file diff --git a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseClientHttpRequest.java b/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseClientHttpRequest.java deleted file mode 100644 index ebe1f943b68..00000000000 --- a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseClientHttpRequest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.provider.springmvc.reference; - -import java.io.IOException; -import java.net.URI; -import java.util.Arrays; - -import javax.xml.ws.Holder; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.springframework.http.HttpMethod; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -import io.servicecomb.common.rest.RestEngineSchemaListener; -import io.servicecomb.core.CseContext; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.provider.consumer.ReferenceConfigUtils; -import io.servicecomb.core.unittest.UnitTestMeta; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.registry.ServiceRegistryFactory; -import io.servicecomb.swagger.invocation.Response; - -public class TestCseClientHttpRequest { - @Before - public void setup() { - ReferenceConfigUtils.setReady(true); - } - - @After - public void teardown() { - ReferenceConfigUtils.setReady(false); - } - - @RequestMapping(path = "SpringmvcImpl") - static class SpringmvcImpl { - @RequestMapping(path = "/bytes", method = RequestMethod.POST) - public byte[] bytes(@RequestBody byte[] input) { - input[0] = (byte) (input[0] + 1); - return input; - } - } - - @Test - public void testNotReady() throws IOException { - String exceptionMessage = "System is not ready for remote calls. " - + "When beans are making remote calls in initialization, it's better to " - + "implement io.servicecomb.core.BootListener and do it after EventType.AFTER_REGISTRY."; - - ReferenceConfigUtils.setReady(false); - CseClientHttpRequest client = - new CseClientHttpRequest(URI.create("cse://app:test/"), HttpMethod.POST); - - try { - client.execute(); - Assert.fail("must throw exception"); - } catch (IllegalStateException e) { - Assert.assertEquals(exceptionMessage, e.getMessage()); - } - - client.close(); - } - - @Test - public void testNormal() throws IOException { - ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal(); - serviceRegistry.init(); - RegistryUtils.setServiceRegistry(serviceRegistry); - - UnitTestMeta meta = new UnitTestMeta(); - - CseContext.getInstance() - .getSchemaListenerManager() - .setSchemaListenerList(Arrays.asList(new RestEngineSchemaListener())); - - SchemaMeta schemaMeta = meta.getOrCreateSchemaMeta(SpringmvcImpl.class); - CseContext.getInstance().getSchemaListenerManager().notifySchemaListener(schemaMeta); - - Holder holder = new Holder<>(); - CseClientHttpRequest client = - new CseClientHttpRequest(URI.create("cse://app:test/" + SpringmvcImpl.class.getSimpleName() + "/bytes"), - HttpMethod.POST) { - - /** - * {@inheritDoc} - */ - @Override - protected Response doInvoke(Invocation invocation) { - holder.value = invocation; - return Response.ok("result"); - } - }; - byte[] body = "abc".getBytes(); - client.setRequestBody(body); - - client.execute(); - client.close(); - - Assert.assertArrayEquals(body, holder.value.getSwaggerArgument(0)); - } -} diff --git a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseClientHttpRequestFactory.java b/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseClientHttpRequestFactory.java deleted file mode 100644 index 79cd17fa9d0..00000000000 --- a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseClientHttpRequestFactory.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import java.io.IOException; -import java.net.URI; - -import org.junit.Assert; -import org.junit.Test; -import org.springframework.http.HttpMethod; - -public class TestCseClientHttpRequestFactory { - - @Test - public void testCseClientHttpRequestFactory() { - - CseClientHttpRequestFactory lCseClientHttpRequestFactory = new CseClientHttpRequestFactory(); - try { - Assert.assertEquals(HttpMethod.GET, - lCseClientHttpRequestFactory.createRequest(URI.create("/test"), HttpMethod.GET).getMethod()); - } catch (IOException e) { - Assert.assertNotNull(e); - } - } -} diff --git a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseClientHttpResponse.java b/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseClientHttpResponse.java deleted file mode 100644 index 591bbdee8e6..00000000000 --- a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseClientHttpResponse.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import java.io.IOException; - -import org.junit.Assert; -import org.junit.Test; -import org.springframework.http.HttpStatus; - -import io.servicecomb.swagger.invocation.Response; - -public class TestCseClientHttpResponse { - private Object result; - - Response response = Response.ok(result); - - CseClientHttpResponse cseclientrequest = new CseClientHttpResponse(response); - - @Test - public void testGetResult() { - Assert.assertNull(cseclientrequest.getResult()); - } - - @Test - public void testGetBody() throws IOException { - Assert.assertNotNull(cseclientrequest.getBody()); - } - - @Test - public void testGetHeaders() { - Assert.assertNotNull(cseclientrequest.getHeaders()); - } - - @Test - public void testGetStatusCode() throws IOException { - Assert.assertEquals(HttpStatus.OK, cseclientrequest.getStatusCode()); - } - - @Test - public void testGetRawStatusCode() throws IOException { - Assert.assertEquals(200, cseclientrequest.getRawStatusCode()); - } - - @Test - public void testGetStatusText() throws IOException { - cseclientrequest.close(); - Assert.assertEquals(HttpStatus.OK.getReasonPhrase(), cseclientrequest.getStatusText()); - } -} diff --git a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseHttpEntity.java b/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseHttpEntity.java deleted file mode 100644 index a8aedc5d649..00000000000 --- a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseHttpEntity.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.provider.springmvc.reference; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.swagger.invocation.context.InvocationContext; - -public class TestCseHttpEntity { - @Test - public void test() { - CseHttpEntity entity = new CseHttpEntity<>(null, null); - entity.addContext("c1", "c1v"); - - Assert.assertEquals(1, entity.getContext().getContext().size()); - Assert.assertEquals("c1v", entity.getContext().getContext("c1")); - - InvocationContext context = new InvocationContext(); - context.addContext("c2", "c2v"); - entity.setContext(context); - - Assert.assertEquals(1, entity.getContext().getContext().size()); - Assert.assertEquals("c2v", entity.getContext().getContext("c2")); - } -} diff --git a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseRequestCallback.java b/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseRequestCallback.java deleted file mode 100644 index f593500d927..00000000000 --- a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseRequestCallback.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.provider.springmvc.reference; - -import java.io.IOException; - -import org.junit.Assert; -import org.junit.Test; -import org.springframework.web.client.RequestCallback; - -import mockit.Injectable; -import mockit.Mocked; - -public class TestCseRequestCallback { - @Test - public void testNormal(@Mocked RequestCallback callback) throws IOException { - CseClientHttpRequest request = new CseClientHttpRequest(); - CseRequestCallback cb = new CseRequestCallback(null, callback); - cb.doWithRequest(request); - - Assert.assertEquals(null, request.getContext()); - } - - @Test - public void testCseEntity(@Injectable CseHttpEntity entity, - @Mocked RequestCallback callback) throws IOException { - CseClientHttpRequest request = new CseClientHttpRequest(); - - entity.addContext("c1", "c2"); - CseRequestCallback cb = new CseRequestCallback(entity, callback); - cb.doWithRequest(request); - - Assert.assertEquals(entity.getContext(), request.getContext()); - } -} diff --git a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseRestTemplate.java b/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseRestTemplate.java deleted file mode 100644 index 84494c14333..00000000000 --- a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseRestTemplate.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import org.junit.Assert; -import org.junit.Test; - -public class TestCseRestTemplate { - - @Test - public void testCseRestTemplate() { - Assert.assertEquals(CseRestTemplate.class, new CseRestTemplate().getClass()); - } -} diff --git a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseUriTemplateHandler.java b/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseUriTemplateHandler.java deleted file mode 100644 index e083556df02..00000000000 --- a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestCseUriTemplateHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.provider.springmvc.reference; - -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - -public class TestCseUriTemplateHandler { - @Test - public void testCrossApp() { - CseUriTemplateHandler handler = new CseUriTemplateHandler(); - URI uri = handler.expand("cse://{ap}{p}:ms/{path}?q={query}", "ap", "p", "path", "query"); - Assert.assertEquals("cse://app:ms/path?q=query", uri.toString()); - - Map vars = new HashMap<>(); - vars.put("app", "app"); - vars.put("path", "path"); - vars.put("q", "query"); - uri = handler.expand("cse://{app}:ms/{path}?q={q}", vars); - Assert.assertEquals("cse://app:ms/path?q=query", uri.toString()); - - uri = handler.expand("cse://ms/{path}?q={query}", "path", "query"); - Assert.assertEquals("cse://ms/path?q=query", uri.toString()); - } -} diff --git a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestRequestMeta.java b/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestRequestMeta.java deleted file mode 100644 index f581b7d2a50..00000000000 --- a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestRequestMeta.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.provider.consumer.ReferenceConfig; - -public class TestRequestMeta { - ReferenceConfig referenceConfig = Mockito.mock(ReferenceConfig.class); - - RestOperationMeta swaggerRestOperation = Mockito.mock(RestOperationMeta.class); - - Map pathParams = new HashMap(); - - RequestMeta requestmeta = new RequestMeta(referenceConfig, swaggerRestOperation, pathParams); - - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - - @Test - public void testGetReferenceConfig() { - ReferenceConfig value = requestmeta.getReferenceConfig(); - Assert.assertNotNull(value); - } - - @Test - public void testGetPathParams() { - Map value = requestmeta.getPathParams(); - Assert.assertNotNull(value); - } - - @Test - public void testGetSwaggerRestOperation() { - RestOperationMeta value = requestmeta.getSwaggerRestOperation(); - Assert.assertNotNull(value); - } - - @Test - public void testGetOperationMeta() { - Assert.assertNull(requestmeta.getOperationMeta()); - } - - @Test - public void testGetOperationQualifiedName() { - Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("value"); - String qualifiedName = operationMeta.getSchemaQualifiedName(); - Assert.assertEquals("value", qualifiedName); - } -} diff --git a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestRestTemplateBuilder.java b/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestRestTemplateBuilder.java deleted file mode 100644 index e2eb8dbc6a3..00000000000 --- a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestRestTemplateBuilder.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.provider.springmvc.reference; - -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.junit.Assert.assertThat; - -import java.net.URI; - -import org.junit.Test; -import org.springframework.web.client.RestTemplate; - -import com.seanyinx.github.unit.scaffolding.Randomness; - -public class TestRestTemplateBuilder { - - private final String url = Randomness.uniquify("url"); - - private final AcceptableRestTemplate underlying = new AlwaysAcceptableRestTemplate(); - - private static class AlwaysAcceptableRestTemplate extends AcceptableRestTemplate { - - @Override - public boolean isAcceptable(String uri) { - return true; - } - - @Override - public boolean isAcceptable(URI uri) { - return true; - } - } - - @Test - public void addsRestTemplateToWrapper() { - RestTemplateBuilder.addAcceptableRestTemplate(1, underlying); - - RestTemplate restTemplate = RestTemplateBuilder.create(); - - assertThat(restTemplate, instanceOf(RestTemplateWrapper.class)); - - RestTemplateWrapper wrapper = (RestTemplateWrapper) restTemplate; - - assertThat(wrapper.getRestTemplate(url), is(underlying)); - assertThat(wrapper.getRestTemplate(URI.create(url)), is(underlying)); - } -} diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfigTest.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfigTest.java new file mode 100644 index 00000000000..c250b58415e --- /dev/null +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/ServiceCombRestTemplateConfigTest.java @@ -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. + */ + +package org.apache.servicecomb.provider.springmvc.reference; + +import static org.hamcrest.core.IsInstanceOf.instanceOf; + +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.Test; +import org.springframework.web.client.RestOperations; + +public class ServiceCombRestTemplateConfigTest { + + private final ServiceCombRestTemplateConfig config = new ServiceCombRestTemplateConfig(); + + @Test + public void exposesServiceCombRestTemplate() { + RestOperations restTemplate = config.restTemplate(); + + MatcherAssert.assertThat(restTemplate, instanceOf(RestTemplateWrapper.class)); + } +} diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCommonToHttpServletRequest.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCommonToHttpServletRequest.java new file mode 100644 index 00000000000..3b06470412a --- /dev/null +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCommonToHttpServletRequest.java @@ -0,0 +1,282 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.provider.springmvc.reference; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.HttpHeaders; + +public class TestCommonToHttpServletRequest { + @Test + public void testConstructFormTrue() { + Map forms = new HashMap<>(); + HttpServletRequest request = new CommonToHttpServletRequest(null, null, null, forms); + + Assertions.assertEquals(forms, request.getAttribute(RestConst.BODY_PARAMETER)); + } + + @Test + public void testConstructFormFalse() { + Object body = new Object(); + HttpServletRequest request = new CommonToHttpServletRequest(null, null, null, body); + + Assertions.assertEquals(body, request.getAttribute(RestConst.BODY_PARAMETER)); + } + + @Test + public void testConstructNormal() { + List fileKeys = new ArrayList<>(); + fileKeys.add("test1"); + fileKeys.add("test2"); + HttpServletRequest request = new CommonToHttpServletRequest(null, null, null, null, fileKeys); + Assertions.assertEquals(2, ((CommonToHttpServletRequest) request).getFileKeys().size()); + Assertions.assertEquals("test1", ((CommonToHttpServletRequest) request).getFileKeys().get(0)); + Assertions.assertEquals("test2", ((CommonToHttpServletRequest) request).getFileKeys().get(1)); + } + + @Test + public void testConstructPath() { + Map pathParams = new HashMap<>(); + HttpServletRequest request = new CommonToHttpServletRequest(pathParams, null, null, null); + + Assertions.assertEquals(pathParams, request.getAttribute(RestConst.PATH_PARAMETERS)); + } + + @Test + public void testGetContentType() { + Map> httpHeaders = new HashMap<>(); + httpHeaders.put(HttpHeaders.CONTENT_TYPE, Arrays.asList("json")); + + HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); + Assertions.assertEquals("json", request.getContentType()); + } + + @Test + public void testGetParameterNormal() { + Map> queryParams = new HashMap<>(); + queryParams.put("name", Arrays.asList("value")); + + HttpServletRequest request = new CommonToHttpServletRequest(null, queryParams, null, null); + Assertions.assertEquals("value", request.getParameter("name")); + } + + @Test + public void testGetParameterEmpty() { + Map> queryParams = new HashMap<>(); + queryParams.put("name", Arrays.asList()); + + HttpServletRequest request = new CommonToHttpServletRequest(null, queryParams, null, null); + Assertions.assertNull(request.getParameter("name")); + } + + @Test + public void testGetParameterNull() { + Map> queryParams = new HashMap<>(); + + HttpServletRequest request = new CommonToHttpServletRequest(null, queryParams, null, null); + Assertions.assertNull(request.getParameter("name")); + } + + @Test + public void testGetParameterValuesNormal() { + Map> queryParams = new HashMap<>(); + queryParams.put("name", Arrays.asList("value")); + + HttpServletRequest request = new CommonToHttpServletRequest(null, queryParams, null, null); + MatcherAssert.assertThat(request.getParameterValues("name"), Matchers.arrayContaining("value")); + } + + @Test + public void testGetParameterValuesEmpty() { + Map> queryParams = new HashMap<>(); + queryParams.put("name", Arrays.asList()); + + HttpServletRequest request = new CommonToHttpServletRequest(null, queryParams, null, null); + Assertions.assertArrayEquals(new String[0], request.getParameterValues("name")); + } + + @Test + public void testGetParameterValuesNull() { + Map> queryParams = new HashMap<>(); + + HttpServletRequest request = new CommonToHttpServletRequest(null, queryParams, null, null); + Assertions.assertArrayEquals(new String[0], request.getParameterValues("name")); + } + + @Test + public void testGetHeaderNormal() { + Map> httpHeaders = new HashMap<>(); + httpHeaders.put("name", Arrays.asList("value")); + + HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); + Assertions.assertEquals("value", request.getHeader("name")); + } + + @Test + public void testGetHeaderEmpty() { + Map> httpHeaders = new HashMap<>(); + httpHeaders.put("name", Arrays.asList()); + + HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); + Assertions.assertNull(request.getHeader("name")); + } + + @Test + public void testGetHeaderNamesNormal() { + Map> httpHeaders = new HashMap<>(); + httpHeaders.put("name", Arrays.asList("value")); + + HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); + MatcherAssert.assertThat(Collections.list(request.getHeaderNames()), Matchers.contains("name")); + } + + @Test + public void testGetHeaderNamesEmpty() { + Map> httpHeaders = new HashMap<>(); + + HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); + Assertions.assertFalse(request.getHeaderNames().hasMoreElements()); + } + + @Test + public void testGetHeaderNull() { + Map> httpHeaders = new HashMap<>(); + + HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); + Assertions.assertNull(request.getHeader("name")); + } + + @Test + public void testGetHeadersNormal() { + Map> httpHeaders = new HashMap<>(); + httpHeaders.put("name", Arrays.asList("value")); + + HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); + MatcherAssert.assertThat(Collections.list(request.getHeaders("name")), Matchers.contains("value")); + } + + @Test + public void testGetHeadersEmpty() { + Map> httpHeaders = new HashMap<>(); + httpHeaders.put("name", Arrays.asList()); + + HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); + Assertions.assertFalse(request.getHeaders("name").hasMoreElements()); + } + + @Test + public void testGetHeadersNull() { + Map> httpHeaders = new HashMap<>(); + + HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); + Assertions.assertFalse(request.getHeaders("name").hasMoreElements()); + } + + @Test + public void testGetCookiesNull() { + Map> httpHeaders = new HashMap<>(); + + HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); + Assertions.assertEquals(0, request.getCookies().length); + } + + @Test + public void testGetCookiesNormal() { + Map> httpHeaders = new HashMap<>(); + httpHeaders.put(HttpHeaders.COOKIE, Arrays.asList("k1=v1;k2=v2;")); + + HttpServletRequest request = new CommonToHttpServletRequest(null, null, httpHeaders, null); + Cookie[] cookies = request.getCookies(); + Assertions.assertSame(cookies, request.getCookies()); + Assertions.assertEquals(1, cookies.length); + Assertions.assertEquals("k1", cookies[0].getName()); + Assertions.assertEquals("v1", cookies[0].getValue()); + } + + @Test + public void testGetInputStream() throws IOException { + HttpServletRequest request = new CommonToHttpServletRequest(null, null, null, null); + Assertions.assertNull(request.getInputStream()); + } + + @Test + public void testSetHeader() { + Map> httpHeaders = new HashMap<>(); + HttpServletRequestEx request = new CommonToHttpServletRequest(null, null, httpHeaders, null); + request.setHeader("name", "v1"); + request.setHeader("name", "v2"); + Assertions.assertEquals("v2", request.getHeader("name")); + } + + @Test + public void testAddHeader() { + Map> httpHeaders = new HashMap<>(); + HttpServletRequestEx request = new CommonToHttpServletRequest(null, null, httpHeaders, null); + request.addHeader("name", "v1"); + request.addHeader("name", "v2"); + MatcherAssert.assertThat(Collections.list(request.getHeaders("name")), Matchers.contains("v1", "v2")); + } + + @Test + public void testGetParts() { + List restParams = new ArrayList<>(); + restParams.add("test1"); + restParams.add("test2"); + File file1 = new File("file1.txt"); + File file2 = new File("file2.txt"); + File[] files = {file1, file2}; + List list = Arrays.asList(files); + Map objectMap = new HashMap<>(); + objectMap.put("test1", list); + objectMap.put("test2", files); + objectMap.put("test3", list); + objectMap.put("test4", "haha"); + + Map pathParams = new HashMap<>(); + HttpServletRequest request = new CommonToHttpServletRequest(pathParams, null, null, objectMap, restParams); + try { + Collection tmpParts = request.getParts(); + ArrayList parts = new ArrayList<>(tmpParts); + Assertions.assertEquals(4, parts.size()); + Assertions.assertEquals("test1", parts.get(0).getName()); + Assertions.assertEquals("test1", parts.get(1).getName()); + Assertions.assertEquals("test2", parts.get(2).getName()); + Assertions.assertEquals("test2", parts.get(3).getName()); + } catch (Throwable e) { + Assertions.fail("should not throw exception"); + } + } +} diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseClientHttpRequestFactory.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseClientHttpRequestFactory.java new file mode 100644 index 00000000000..35b8950de63 --- /dev/null +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseClientHttpRequestFactory.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.provider.springmvc.reference; + +import java.io.IOException; +import java.net.URI; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpMethod; + +public class TestCseClientHttpRequestFactory { + + @Test + public void testCseClientHttpRequestFactory() { + + CseClientHttpRequestFactory lCseClientHttpRequestFactory = new CseClientHttpRequestFactory(); + try { + Assertions.assertEquals(HttpMethod.GET, + lCseClientHttpRequestFactory.createRequest(URI.create("/test"), HttpMethod.GET).getMethod()); + } catch (IOException e) { + Assertions.assertNotNull(e); + } + } +} diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseClientHttpResponse.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseClientHttpResponse.java new file mode 100644 index 00000000000..46d65e28357 --- /dev/null +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseClientHttpResponse.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.provider.springmvc.reference; + +import java.io.IOException; + +import org.apache.servicecomb.swagger.invocation.Response; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; + +public class TestCseClientHttpResponse { + private Object result; + + Response response = Response.ok(result); + + CseClientHttpResponse cseclientrequest = new CseClientHttpResponse(response); + + @Test + public void testGetResult() { + Assertions.assertNull(cseclientrequest.getResult()); + } + + @Test + public void testGetBody() throws IOException { + Assertions.assertNotNull(cseclientrequest.getBody()); + } + + @Test + public void testGetHeaders() { + Assertions.assertNotNull(cseclientrequest.getHeaders()); + } + + @Test + public void testGetStatusCode() throws IOException { + Assertions.assertEquals(HttpStatus.OK, cseclientrequest.getStatusCode()); + } + + @Test + public void testGetRawStatusCode() throws IOException { + Assertions.assertEquals(200, cseclientrequest.getRawStatusCode()); + } + + @Test + public void testGetStatusText() throws IOException { + cseclientrequest.close(); + Assertions.assertEquals(HttpStatus.OK.getReasonPhrase(), cseclientrequest.getStatusText()); + } +} diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseHttpEntity.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseHttpEntity.java new file mode 100644 index 00000000000..e057c70ec4b --- /dev/null +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseHttpEntity.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.provider.springmvc.reference; + +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestCseHttpEntity { + @Test + public void test() { + CseHttpEntity entity = new CseHttpEntity<>(null, null); + entity.addContext("c1", "c1v"); + + Assertions.assertEquals(1, entity.getContext().getContext().size()); + Assertions.assertEquals("c1v", entity.getContext().getContext("c1")); + + InvocationContext context = new InvocationContext(); + context.addContext("c2", "c2v"); + entity.setContext(context); + + Assertions.assertEquals(1, entity.getContext().getContext().size()); + Assertions.assertEquals("c2v", entity.getContext().getContext("c2")); + } +} diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseRequestCallback.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseRequestCallback.java new file mode 100644 index 00000000000..ce2d11257c1 --- /dev/null +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseRequestCallback.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.provider.springmvc.reference; + +import java.io.IOException; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.web.client.RequestCallback; + +public class TestCseRequestCallback { + @Test + public void testNormal() throws IOException { + RequestCallback callback = Mockito.mock(RequestCallback.class); + CseClientHttpRequest request = new CseClientHttpRequest(); + CseRequestCallback cb = new CseRequestCallback(null, callback, null); + cb.doWithRequest(request); + + Assertions.assertNull(request.getContext()); + } + + @Test + public void testCseEntity() throws IOException { + CseHttpEntity entity = Mockito.mock(CseHttpEntity.class); + RequestCallback callback = Mockito.mock(RequestCallback.class); + CseClientHttpRequest request = new CseClientHttpRequest(); + + entity.addContext("c1", "c2"); + CseRequestCallback cb = new CseRequestCallback(entity, callback, null); + cb.doWithRequest(request); + + Assertions.assertEquals(entity.getContext(), request.getContext()); + } +} diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseRestTemplate.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseRestTemplate.java new file mode 100644 index 00000000000..85957c30602 --- /dev/null +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseRestTemplate.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.provider.springmvc.reference; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestCseRestTemplate { + + @Test + public void testCseRestTemplate() { + Assertions.assertEquals(CseRestTemplate.class, new CseRestTemplate().getClass()); + } +} diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseUriTemplateHandler.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseUriTemplateHandler.java new file mode 100644 index 00000000000..9407ded239c --- /dev/null +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseUriTemplateHandler.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.provider.springmvc.reference; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestCseUriTemplateHandler { + @Test + public void testCrossApp() { + CseUriTemplateHandler handler = new CseUriTemplateHandler(); + URI uri = handler.expand("cse://{ap}{p}:ms/{path}?q={query}", "ap", "p", "path", "query"); + Assertions.assertEquals("cse://app~ms/path?q=query", uri.toString()); + + Map vars = new HashMap<>(); + vars.put("app", "app"); + vars.put("path", "path"); + vars.put("q", "query"); + uri = handler.expand("cse://{app}:ms/{path}?q={q}", vars); + Assertions.assertEquals("cse://app~ms/path?q=query", uri.toString()); + + uri = handler.expand("cse://ms/{path}?q={query}", "path", "query"); + Assertions.assertEquals("cse://ms/path?q=query", uri.toString()); + } + + @Test + public void testCrossAppServiceComb() { + CseUriTemplateHandler handler = new CseUriTemplateHandler(); + URI uri = handler.expand("servicecomb://{ap}{p}:ms/{path}?q={query}", "ap", "p", "path", "query"); + Assertions.assertEquals("servicecomb://app~ms/path?q=query", uri.toString()); + + Map vars = new HashMap<>(); + vars.put("app", "app"); + vars.put("path", "path"); + vars.put("q", "query"); + uri = handler.expand("servicecomb://{app}:ms/{path}?q={q}", vars); + Assertions.assertEquals("servicecomb://app~ms/path?q=query", uri.toString()); + + uri = handler.expand("servicecomb://ms/{path}?q={query}", "path", "query"); + Assertions.assertEquals("servicecomb://ms/path?q=query", uri.toString()); + } +} diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestRequestMeta.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestRequestMeta.java new file mode 100644 index 00000000000..cabcbde4678 --- /dev/null +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestRequestMeta.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.provider.springmvc.reference; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestRequestMeta { + ReferenceConfig referenceConfig = Mockito.mock(ReferenceConfig.class); + + RestOperationMeta swaggerRestOperation = Mockito.mock(RestOperationMeta.class); + + Map pathParams = new HashMap<>(); + + RequestMeta requestmeta = new RequestMeta(referenceConfig, swaggerRestOperation, pathParams); + + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + + @Test + public void testGetReferenceConfig() { + ReferenceConfig value = requestmeta.getReferenceConfig(); + Assertions.assertNotNull(value); + } + + @Test + public void testGetPathParams() { + Map value = requestmeta.getPathParams(); + Assertions.assertNotNull(value); + } + + @Test + public void testGetSwaggerRestOperation() { + RestOperationMeta value = requestmeta.getSwaggerRestOperation(); + Assertions.assertNotNull(value); + } + + @Test + public void testGetOperationMeta() { + Assertions.assertNull(requestmeta.getOperationMeta()); + } + + @Test + public void testGetOperationQualifiedName() { + Mockito.when(operationMeta.getSchemaQualifiedName()).thenReturn("value"); + String qualifiedName = operationMeta.getSchemaQualifiedName(); + Assertions.assertEquals("value", qualifiedName); + } +} diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestRestTemplateBuilder.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestRestTemplateBuilder.java new file mode 100644 index 00000000000..0bd76111ba9 --- /dev/null +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestRestTemplateBuilder.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.provider.springmvc.reference; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsInstanceOf.instanceOf; + +import java.net.URI; + +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.Test; +import org.springframework.web.client.RestOperations; + +import com.seanyinx.github.unit.scaffolding.Randomness; + +public class TestRestTemplateBuilder { + + private final String url = Randomness.uniquify("url"); + + private final AcceptableRestTemplate underlying = new AlwaysAcceptableRestTemplate(); + + private static class AlwaysAcceptableRestTemplate extends AcceptableRestTemplate { + + @Override + public boolean isAcceptable(String uri) { + return true; + } + + @Override + public boolean isAcceptable(URI uri) { + return true; + } + } + + @Test + public void addsRestTemplateToWrapper() { + RestTemplateBuilder.addAcceptableRestTemplate(1, underlying); + + RestOperations restTemplate = RestTemplateBuilder.create(); + + MatcherAssert.assertThat(restTemplate, instanceOf(RestTemplateWrapper.class)); + + RestTemplateWrapper wrapper = (RestTemplateWrapper) restTemplate; + + MatcherAssert.assertThat(wrapper.getRestTemplate(url), is(underlying)); + MatcherAssert.assertThat(wrapper.getRestTemplate(URI.create(url)), is(underlying)); + } +} diff --git a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestRestTemplateWrapper.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestRestTemplateWrapper.java similarity index 78% rename from providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestRestTemplateWrapper.java rename to providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestRestTemplateWrapper.java index b044028c01e..736c0d0dfbf 100644 --- a/providers/provider-springmvc/src/test/java/io/servicecomb/provider/springmvc/reference/TestRestTemplateWrapper.java +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestRestTemplateWrapper.java @@ -1,11 +1,12 @@ /* - * Copyright 2017 Huawei Technologies Co., Ltd + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,14 +15,13 @@ * limitations under the License. */ -package io.servicecomb.provider.springmvc.reference; +package org.apache.servicecomb.provider.springmvc.reference; import static com.seanyinx.github.unit.scaffolding.Randomness.uniquify; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -44,8 +44,9 @@ import java.util.Map; import java.util.Set; -import org.junit.Before; -import org.junit.Test; +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -59,7 +60,7 @@ import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.ResponseExtractor; import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.DefaultUriTemplateHandler; +import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.web.util.UriTemplateHandler; public class TestRestTemplateWrapper { @@ -76,8 +77,7 @@ public class TestRestTemplateWrapper { private final String param2 = uniquify("param2"); - @SuppressWarnings("serial") - private final Map paramsMap = new HashMap() { + private final Map paramsMap = new HashMap<>() { { put(uniquify("key1"), param1); put(uniquify("key2"), param2); @@ -94,7 +94,7 @@ public class TestRestTemplateWrapper { private final List httpMethods = asList(GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE); - @Before + @BeforeEach public void setUp() throws Exception { when(underlying.isAcceptable(url)).thenReturn(true); when(underlying.isAcceptable(uri)).thenReturn(true); @@ -108,17 +108,17 @@ public void headForHeadersWithUnderlyingRestTemplate() { when(underlying.headForHeaders(url, param1, param2)).thenReturn(expected); actual = wrapper.headForHeaders(url, param1, param2); - assertThat(actual, is(expected)); + MatcherAssert.assertThat(actual, is(expected)); verify(underlying).headForHeaders(url, param1, param2); when(underlying.headForHeaders(url, paramsMap)).thenReturn(expected); actual = wrapper.headForHeaders(url, paramsMap); - assertThat(actual, is(expected)); + MatcherAssert.assertThat(actual, is(expected)); verify(underlying).headForHeaders(url, paramsMap); when(underlying.headForHeaders(uri)).thenReturn(expected); actual = wrapper.headForHeaders(uri); - assertThat(actual, is(expected)); + MatcherAssert.assertThat(actual, is(expected)); verify(underlying).headForHeaders(uri); } @@ -129,17 +129,17 @@ public void optionsForAllowWithUnderlyingRestTemplate() { when(underlying.optionsForAllow(url, param1, param2)).thenReturn(expected); actual = wrapper.optionsForAllow(url, param1, param2); - assertThat(actual, is(expected)); + MatcherAssert.assertThat(actual, is(expected)); verify(underlying).optionsForAllow(url, param1, param2); when(underlying.optionsForAllow(url, paramsMap)).thenReturn(expected); actual = wrapper.optionsForAllow(url, paramsMap); - assertThat(actual, is(expected)); + MatcherAssert.assertThat(actual, is(expected)); verify(underlying).optionsForAllow(url, paramsMap); when(underlying.optionsForAllow(uri)).thenReturn(expected); actual = wrapper.optionsForAllow(uri); - assertThat(actual, is(expected)); + MatcherAssert.assertThat(actual, is(expected)); verify(underlying).optionsForAllow(uri); } @@ -149,17 +149,17 @@ public void getForObjectWithUnderlyingRestTemplate() { when(underlying.getForObject(url, String.class, param1, param2)).thenReturn(response); actual = wrapper.getForObject(url, String.class, param1, param2); - assertThat(actual, is(response)); + MatcherAssert.assertThat(actual, is(response)); verify(underlying).getForObject(url, String.class, param1, param2); when(underlying.getForObject(url, String.class, paramsMap)).thenReturn(response); actual = wrapper.getForObject(url, String.class, paramsMap); - assertThat(actual, is(response)); + MatcherAssert.assertThat(actual, is(response)); verify(underlying).getForObject(url, String.class, paramsMap); when(underlying.getForObject(uri, String.class)).thenReturn(response); actual = wrapper.getForObject(uri, String.class); - assertThat(actual, is(response)); + MatcherAssert.assertThat(actual, is(response)); verify(underlying).getForObject(uri, String.class); } @@ -169,17 +169,17 @@ public void getForEntityWithUnderlyingRestTemplate() { when(underlying.getForEntity(url, String.class, param1, param2)).thenReturn(responseEntity); actual = wrapper.getForEntity(url, String.class, param1, param2); - assertThat(actual, is(responseEntity)); + MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).getForEntity(url, String.class, param1, param2); when(underlying.getForEntity(url, String.class, paramsMap)).thenReturn(responseEntity); actual = wrapper.getForEntity(url, String.class, paramsMap); - assertThat(actual, is(responseEntity)); + MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).getForEntity(url, String.class, paramsMap); when(underlying.getForEntity(uri, String.class)).thenReturn(responseEntity); actual = wrapper.getForEntity(uri, String.class); - assertThat(actual, is(responseEntity)); + MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).getForEntity(uri, String.class); } @@ -189,17 +189,17 @@ public void postForObjectWithUnderlyingRestTemplate() { when(underlying.postForObject(url, requestEntity, String.class, param1, param2)).thenReturn(response); actual = wrapper.postForObject(url, requestEntity, String.class, param1, param2); - assertThat(actual, is(response)); + MatcherAssert.assertThat(actual, is(response)); verify(underlying).postForObject(url, requestEntity, String.class, param1, param2); when(underlying.postForObject(url, requestEntity, String.class, paramsMap)).thenReturn(response); actual = wrapper.postForObject(url, requestEntity, String.class, paramsMap); - assertThat(actual, is(response)); + MatcherAssert.assertThat(actual, is(response)); verify(underlying).postForObject(url, requestEntity, String.class, paramsMap); when(underlying.postForObject(uri, requestEntity, String.class)).thenReturn(response); actual = wrapper.postForObject(uri, requestEntity, String.class); - assertThat(actual, is(response)); + MatcherAssert.assertThat(actual, is(response)); verify(underlying).postForObject(uri, requestEntity, String.class); } @@ -209,17 +209,17 @@ public void postForEntityWithUnderlyingRestTemplate() { when(underlying.postForEntity(url, requestEntity, String.class, param1, param2)).thenReturn(responseEntity); actual = wrapper.postForEntity(url, requestEntity, String.class, param1, param2); - assertThat(actual, is(responseEntity)); + MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).postForEntity(url, requestEntity, String.class, param1, param2); when(underlying.postForEntity(url, requestEntity, String.class, paramsMap)).thenReturn(responseEntity); actual = wrapper.postForEntity(url, requestEntity, String.class, paramsMap); - assertThat(actual, is(responseEntity)); + MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).postForEntity(url, requestEntity, String.class, paramsMap); when(underlying.postForEntity(uri, requestEntity, String.class)).thenReturn(responseEntity); actual = wrapper.postForEntity(uri, requestEntity, String.class); - assertThat(actual, is(responseEntity)); + MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).postForEntity(uri, requestEntity, String.class); } @@ -229,17 +229,17 @@ public void postForLocationWithUnderlyingRestTemplate() { when(underlying.postForLocation(url, requestEntity, param1, param2)).thenReturn(uri); actual = wrapper.postForLocation(url, requestEntity, param1, param2); - assertThat(actual, is(uri)); + MatcherAssert.assertThat(actual, is(uri)); verify(underlying).postForLocation(url, requestEntity, param1, param2); when(underlying.postForLocation(url, requestEntity, paramsMap)).thenReturn(uri); actual = wrapper.postForLocation(url, requestEntity, paramsMap); - assertThat(actual, is(uri)); + MatcherAssert.assertThat(actual, is(uri)); verify(underlying).postForLocation(url, requestEntity, paramsMap); when(underlying.postForLocation(uri, requestEntity)).thenReturn(uri); actual = wrapper.postForLocation(uri, requestEntity); - assertThat(actual, is(uri)); + MatcherAssert.assertThat(actual, is(uri)); verify(underlying).postForLocation(uri, requestEntity); } @@ -255,17 +255,17 @@ public void executeWithUnderlyingRestTemplate() { when(underlying.execute(url, method, requestCallback, responseExtractor, param1, param2)) .thenReturn(responseEntity); actual = wrapper.execute(url, method, requestCallback, responseExtractor, param1, param2); - assertThat(actual, is(responseEntity)); + MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).execute(url, method, requestCallback, responseExtractor, param1, param2); when(underlying.execute(url, method, requestCallback, responseExtractor, paramsMap)).thenReturn(responseEntity); actual = wrapper.execute(url, method, requestCallback, responseExtractor, paramsMap); - assertThat(actual, is(responseEntity)); + MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).execute(url, method, requestCallback, responseExtractor, paramsMap); when(underlying.execute(uri, method, requestCallback, responseExtractor)).thenReturn(responseEntity); actual = wrapper.execute(uri, method, requestCallback, responseExtractor); - assertThat(actual, is(responseEntity)); + MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).execute(uri, method, requestCallback, responseExtractor); } } @@ -277,53 +277,53 @@ public void exchangeWithUnderlyingRestTemplate() { for (HttpMethod method : httpMethods) { when(underlying.exchange(url, method, requestEntity, String.class, param1, param2)).thenReturn(responseEntity); actual = wrapper.exchange(url, method, requestEntity, String.class, param1, param2); - assertThat(actual, is(responseEntity)); + MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).exchange(url, method, requestEntity, String.class, param1, param2); when(underlying.exchange(url, method, requestEntity, String.class, paramsMap)).thenReturn(responseEntity); actual = wrapper.exchange(url, method, requestEntity, String.class, paramsMap); - assertThat(actual, is(responseEntity)); + MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).exchange(url, method, requestEntity, String.class, paramsMap); when(underlying.exchange(uri, method, requestEntity, String.class)).thenReturn(responseEntity); actual = wrapper.exchange(uri, method, requestEntity, String.class); - assertThat(actual, is(responseEntity)); + MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).exchange(uri, method, requestEntity, String.class); RequestEntity request = new RequestEntity<>(method, uri); when(underlying.exchange(request, String.class)).thenReturn(responseEntity); actual = wrapper.exchange(request, String.class); - assertThat(actual, is(responseEntity)); + MatcherAssert.assertThat(actual, is(responseEntity)); verify(underlying).exchange(request, String.class); } } @Test public void exchangeUsingParameterizedTypeWithUnderlyingRestTemplate() { - ParameterizedTypeReference> typeReference = new ParameterizedTypeReference>() { + ParameterizedTypeReference> typeReference = new ParameterizedTypeReference<>() { }; ResponseEntity> actual; for (HttpMethod method : httpMethods) { when(underlying.exchange(url, method, requestEntity, typeReference, param1, param2)).thenReturn(typedResponse); actual = wrapper.exchange(url, method, requestEntity, typeReference, param1, param2); - assertThat(actual, is(typedResponse)); + MatcherAssert.assertThat(actual, is(typedResponse)); verify(underlying).exchange(url, method, requestEntity, typeReference, param1, param2); when(underlying.exchange(url, method, requestEntity, typeReference, paramsMap)).thenReturn(typedResponse); actual = wrapper.exchange(url, method, requestEntity, typeReference, paramsMap); - assertThat(actual, is(typedResponse)); + MatcherAssert.assertThat(actual, is(typedResponse)); verify(underlying).exchange(url, method, requestEntity, typeReference, paramsMap); when(underlying.exchange(uri, method, requestEntity, typeReference)).thenReturn(typedResponse); actual = wrapper.exchange(uri, method, requestEntity, typeReference); - assertThat(actual, is(typedResponse)); + MatcherAssert.assertThat(actual, is(typedResponse)); verify(underlying).exchange(uri, method, requestEntity, typeReference); RequestEntity request = new RequestEntity<>(method, uri); when(underlying.exchange(request, typeReference)).thenReturn(typedResponse); actual = wrapper.exchange(request, typeReference); - assertThat(actual, is(typedResponse)); + MatcherAssert.assertThat(actual, is(typedResponse)); verify(underlying).exchange(request, typeReference); } } @@ -360,8 +360,8 @@ public void setInterceptorsWithUnderlying() { wrapper.setInterceptors(interceptors); - assertThat(wrapper.getInterceptors(), contains(interceptor1, interceptor2)); - assertThat(wrapper.defaultRestTemplate.getInterceptors(), contains(interceptor1, interceptor2)); + MatcherAssert.assertThat(wrapper.getInterceptors(), contains(interceptor1, interceptor2)); + MatcherAssert.assertThat(wrapper.defaultRestTemplate.getInterceptors(), contains(interceptor1, interceptor2)); verify(underlying, never()).setInterceptors(interceptors); } @@ -371,8 +371,8 @@ public void doNotSetRequestFactoryWithUnderlying() { wrapper.setRequestFactory(requestFactory); - assertThat(wrapper.getRequestFactory(), is(requestFactory)); - assertThat(wrapper.defaultRestTemplate.getRequestFactory(), is(requestFactory)); + MatcherAssert.assertThat(wrapper.getRequestFactory(), is(requestFactory)); + MatcherAssert.assertThat(wrapper.defaultRestTemplate.getRequestFactory(), is(requestFactory)); verify(underlying, never()).setRequestFactory(requestFactory); } @@ -383,8 +383,8 @@ public void setErrorHandlerWithUnderlying() { wrapper.setErrorHandler(errorHandler); - assertThat(wrapper.getErrorHandler(), is(errorHandler)); - assertThat(wrapper.defaultRestTemplate.getErrorHandler(), is(errorHandler)); + MatcherAssert.assertThat(wrapper.getErrorHandler(), is(errorHandler)); + MatcherAssert.assertThat(wrapper.defaultRestTemplate.getErrorHandler(), is(errorHandler)); verify(underlying).setErrorHandler(errorHandler); } @@ -395,8 +395,8 @@ public void setDefaultUriVariablesWithUnderlying() { wrapper.setDefaultUriVariables(uriVariables); - assertThat(defaultUriVariablesOf(wrapper), is(uriVariables)); - assertThat(defaultUriVariablesOf(wrapper.defaultRestTemplate), is(uriVariables)); + MatcherAssert.assertThat(defaultUriVariablesOf(wrapper), is(uriVariables)); + MatcherAssert.assertThat(defaultUriVariablesOf(wrapper.defaultRestTemplate), is(uriVariables)); verify(underlying).setDefaultUriVariables(uriVariables); } @@ -407,39 +407,38 @@ public void dotNotSetUriTemplateHandlerWithUnderlying() { wrapper.setUriTemplateHandler(uriTemplateHandler); - assertThat(wrapper.getUriTemplateHandler(), is(uriTemplateHandler)); - assertThat(wrapper.defaultRestTemplate.getUriTemplateHandler(), is(uriTemplateHandler)); + MatcherAssert.assertThat(wrapper.getUriTemplateHandler(), is(uriTemplateHandler)); + MatcherAssert.assertThat(wrapper.defaultRestTemplate.getUriTemplateHandler(), is(uriTemplateHandler)); verify(underlying, never()).setUriTemplateHandler(uriTemplateHandler); } - @SuppressWarnings("unchecked") @Test public void setMessageConvertersWithUnderlying() { ByteArrayHttpMessageConverter messageConverter = mock(ByteArrayHttpMessageConverter.class); wrapper.setMessageConverters(singletonList(messageConverter)); - assertThat(wrapper.getMessageConverters(), contains(messageConverter)); - assertThat(wrapper.defaultRestTemplate.getMessageConverters(), contains(messageConverter)); + MatcherAssert.assertThat(wrapper.getMessageConverters(), contains(messageConverter)); + MatcherAssert.assertThat(wrapper.defaultRestTemplate.getMessageConverters(), contains(messageConverter)); verify(underlying, never()).setMessageConverters(singletonList(messageConverter)); } @Test public void getsAcceptableRestTemplate() { - assertThat(wrapper.getRestTemplate(uri), is(underlying)); - assertThat(wrapper.getRestTemplate(url), is(underlying)); + MatcherAssert.assertThat(wrapper.getRestTemplate(uri), is(underlying)); + MatcherAssert.assertThat(wrapper.getRestTemplate(url), is(underlying)); } @Test public void getsDefaultRestTemplate() { reset(underlying); - assertThat(wrapper.getRestTemplate(uri), is(wrapper.defaultRestTemplate)); - assertThat(wrapper.getRestTemplate(url), is(wrapper.defaultRestTemplate)); + MatcherAssert.assertThat(wrapper.getRestTemplate(uri), is(wrapper.defaultRestTemplate)); + MatcherAssert.assertThat(wrapper.getRestTemplate(url), is(wrapper.defaultRestTemplate)); } private Map defaultUriVariablesOf(RestTemplate wrapper1) { - return ((DefaultUriTemplateHandler) wrapper1.getUriTemplateHandler()).getDefaultUriVariables(); + return ((DefaultUriBuilderFactory) wrapper1.getUriTemplateHandler()).getDefaultUriVariables(); } } diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestUrlWithProviderPrefixClientHttpRequestFactory.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestUrlWithProviderPrefixClientHttpRequestFactory.java new file mode 100644 index 00000000000..4c3ca6b6cb1 --- /dev/null +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestUrlWithProviderPrefixClientHttpRequestFactory.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.provider.springmvc.reference; + +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.invocation.InvocationFactory; +import org.apache.servicecomb.provider.springmvc.reference.UrlWithProviderPrefixClientHttpRequestFactory.UrlWithProviderPrefixClientHttpRequest; +import org.apache.servicecomb.swagger.invocation.Response; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.http.HttpMethod; + +public class TestUrlWithProviderPrefixClientHttpRequestFactory { + UrlWithProviderPrefixClientHttpRequestFactory factory = new UrlWithProviderPrefixClientHttpRequestFactory("/a/b/c"); + + URI uri = URI.create("cse://ms/a/b/c/v1/path"); + + @Test + public void findUriPath() throws IOException { + UrlWithProviderPrefixClientHttpRequest request = + (UrlWithProviderPrefixClientHttpRequest) factory.createRequest(uri, HttpMethod.GET); + + Assertions.assertEquals("/v1/path", request.findUriPath(uri)); + } + + @Test + @SuppressWarnings("unchecked") + public void invoke_checkPath() { + Invocation invocation = Mockito.mock(Invocation.class); + RequestMeta requestMeta = Mockito.mock(RequestMeta.class); + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + Mockito.when(requestMeta.getOperationMeta()).thenReturn(operationMeta); + Map handlerContext = new HashMap<>(); + UrlWithProviderPrefixClientHttpRequest request = new UrlWithProviderPrefixClientHttpRequest(uri, HttpMethod.GET, + "/a/b/c") { + @Override + protected Response doInvoke(Invocation invocation) { + return Response.ok(null); + } + }; + + Mockito.when(invocation.getHandlerContext()).thenReturn(handlerContext); + try (MockedStatic invocationFactoryMockedStatic = Mockito.mockStatic(InvocationFactory.class)) { + invocationFactoryMockedStatic.when(() -> InvocationFactory.forConsumer(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(invocation); + request.setRequestMeta(requestMeta); + request.setPath(request.findUriPath(uri)); + + request.invoke(new HashMap<>()); + + Assertions.assertEquals("/v1/path", handlerContext.get(RestConst.REST_CLIENT_REQUEST_PATH)); + } + } +} diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestUrlWithServiceNameClientHttpRequestFactory.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestUrlWithServiceNameClientHttpRequestFactory.java new file mode 100644 index 00000000000..883bb3a393d --- /dev/null +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestUrlWithServiceNameClientHttpRequestFactory.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.provider.springmvc.reference; + +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.invocation.InvocationFactory; +import org.apache.servicecomb.provider.springmvc.reference.UrlWithServiceNameClientHttpRequestFactory.UrlWithServiceNameClientHttpRequest; +import org.apache.servicecomb.swagger.invocation.Response; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.http.HttpMethod; + +public class TestUrlWithServiceNameClientHttpRequestFactory { + UrlWithServiceNameClientHttpRequestFactory factory = new UrlWithServiceNameClientHttpRequestFactory(); + + URI uri = URI.create("cse://ms/v1/path"); + + @Test + public void findUriPath() throws IOException { + UrlWithServiceNameClientHttpRequest request = + (UrlWithServiceNameClientHttpRequest) factory.createRequest(uri, HttpMethod.GET); + + Assertions.assertEquals("/ms/v1/path", request.findUriPath(uri)); + } + + @Test + @SuppressWarnings("unchecked") + public void invoke_checkPath() { + Invocation invocation = Mockito.mock(Invocation.class); + RequestMeta requestMeta = Mockito.mock(RequestMeta.class); + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + Mockito.when(requestMeta.getOperationMeta()).thenReturn(operationMeta); + Map handlerContext = new HashMap<>(); + UrlWithServiceNameClientHttpRequest request = new UrlWithServiceNameClientHttpRequest(uri, HttpMethod.GET) { + @Override + protected Response doInvoke(Invocation invocation) { + return Response.ok(null); + } + }; + + Mockito.when(invocation.getHandlerContext()).thenReturn(handlerContext); + try (MockedStatic invocationFactoryMockedStatic = Mockito.mockStatic(InvocationFactory.class)) { + invocationFactoryMockedStatic.when(() -> InvocationFactory.forConsumer(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(invocation); + request.setRequestMeta(requestMeta); + request.setPath(request.findUriPath(uri)); + request.invoke(new HashMap<>()); + + Assertions.assertEquals("/ms/v1/path", handlerContext.get(RestConst.REST_CLIENT_REQUEST_PATH)); + } + } +} diff --git a/providers/provider-springmvc/src/test/java/org/springframework/web/client/TestCseHttpMessageConverter.java b/providers/provider-springmvc/src/test/java/org/springframework/web/client/TestCseHttpMessageConverter.java deleted file mode 100644 index 73d686a29b8..00000000000 --- a/providers/provider-springmvc/src/test/java/org/springframework/web/client/TestCseHttpMessageConverter.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.web.client; - -import java.io.IOException; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; -import org.springframework.http.HttpOutputMessage; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.http.converter.HttpMessageNotWritableException; - -import io.servicecomb.provider.common.MockUtil; -import io.servicecomb.provider.springmvc.reference.CseClientHttpRequest; - -public class TestCseHttpMessageConverter { - - @Test - public void testAll() { - MockUtil.getInstance().mockReflectionUtils(); - MockUtil.getInstance().mockCseClientHttpRequest(); - CseHttpMessageConverter lCseHttpMessageConverter = new CseHttpMessageConverter(); - lCseHttpMessageConverter.canWrite(null, null); - lCseHttpMessageConverter.getSupportedMediaTypes(); - try { - lCseHttpMessageConverter.read(this.getClass(), null); - } catch (HttpMessageNotReadableException e) { - } catch (IOException e) { - } - try { - HttpOutputMessage httpOutputMessage = Mockito.mock(CseClientHttpRequest.class); - lCseHttpMessageConverter.write(null, null, httpOutputMessage); - } catch (HttpMessageNotWritableException | IOException e) { - } - - Assert.assertEquals(true, lCseHttpMessageConverter.canRead(null, null)); - } -} diff --git a/providers/provider-springmvc/src/test/resources/log4j2.xml b/providers/provider-springmvc/src/test/resources/log4j2.xml new file mode 100644 index 00000000000..8cee07220ec --- /dev/null +++ b/providers/provider-springmvc/src/test/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/providers/provider-springmvc/src/test/resources/microservice.yaml b/providers/provider-springmvc/src/test/resources/microservice.yaml new file mode 100644 index 00000000000..5ff7ed9ae41 --- /dev/null +++ b/providers/provider-springmvc/src/test/resources/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: + filter-chains: + consumer: + default: empty + producer: + default: empty diff --git a/samples/README.md b/samples/README.md index f79a7b4e1ba..751a1d0aa93 100644 --- a/samples/README.md +++ b/samples/README.md @@ -1,24 +1 @@ -# samples - -It's the samples of Java chassis -1. pojo sample -2. jaxrs sample -3. spring mvc sample -4. code first sample -5. customer handler sample - -## 1. Start the ServiceComb/Service Center - -[how to start the service center](http://servicecomb.io/docs/start-sc/) - -## 2.Start the Microservice server - -```bash -mvn test -Pserver -``` - -## 3.Start the Microservice client - -```bash -mvn test -Pclient -``` +java-chassis samples are moved to [servicecomb-samples](https://github.com/apache/servicecomb-samples/tree/master/java-chassis-samples) diff --git a/samples/codefirst-sample/codefirst-consumer/pom.xml b/samples/codefirst-sample/codefirst-consumer/pom.xml deleted file mode 100644 index b37f2e23350..00000000000 --- a/samples/codefirst-sample/codefirst-consumer/pom.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - codefirst-sample - 0.3.0-SNAPSHOT - - codefirst-consumer - - - UTF-8 - - - - - io.servicecomb.samples - commmon-schema - - - io.servicecomb - provider-pojo - - - io.servicecomb - transport-highway - - - io.servicecomb - transport-rest-vertx - - - org.slf4j - slf4j-log4j12 - - - \ No newline at end of file diff --git a/samples/codefirst-sample/codefirst-consumer/src/main/java/io/servicecomb/samples/codefirst/consumer/CodeFirstConsumerMain.java b/samples/codefirst-sample/codefirst-consumer/src/main/java/io/servicecomb/samples/codefirst/consumer/CodeFirstConsumerMain.java deleted file mode 100644 index 988bdcc97eb..00000000000 --- a/samples/codefirst-sample/codefirst-consumer/src/main/java/io/servicecomb/samples/codefirst/consumer/CodeFirstConsumerMain.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.samples.codefirst.consumer; - -import org.springframework.stereotype.Component; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.provider.pojo.RpcReference; -import io.servicecomb.samples.common.schema.Hello; -import io.servicecomb.samples.common.schema.models.Person; - -@Component -public class CodeFirstConsumerMain { - - @RpcReference(microserviceName = "codefirst", schemaId = "codeFirstJaxrsHello") - private static Hello jaxrsHello; - - @RpcReference(microserviceName = "codefirst", schemaId = "codeFirstSpringmvcHello") - private static Hello springmvcHello; - - @RpcReference(microserviceName = "codefirst", schemaId = "codeFirstHello") - private static Hello hello; - - public static void main(String[] args) throws Exception { - init(); - System.out.println(hello.sayHi("Java Chassis")); - System.out.println(jaxrsHello.sayHi("Java Chassis")); - System.out.println(springmvcHello.sayHi("Java Chassis")); - Person person = new Person(); - person.setName("ServiceComb/Java Chassis"); - System.out.println(hello.sayHello(person)); - System.out.println(jaxrsHello.sayHello(person)); - System.out.println(springmvcHello.sayHello(person)); - } - - public static void init() throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/samples/codefirst-sample/codefirst-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml b/samples/codefirst-sample/codefirst-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml deleted file mode 100644 index d31df8b5955..00000000000 --- a/samples/codefirst-sample/codefirst-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - diff --git a/samples/codefirst-sample/codefirst-consumer/src/main/resources/config/log4j.demo.properties b/samples/codefirst-sample/codefirst-consumer/src/main/resources/config/log4j.demo.properties deleted file mode 100644 index 0324fbb1032..00000000000 --- a/samples/codefirst-sample/codefirst-consumer/src/main/resources/config/log4j.demo.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=target/logs/ -paas.logs.file=sample.log - -log4j.rootLogger=INFO,paas,stdout diff --git a/samples/codefirst-sample/codefirst-consumer/src/main/resources/microservice.yaml b/samples/codefirst-sample/codefirst-consumer/src/main/resources/microservice.yaml deleted file mode 100644 index 765e7040b84..00000000000 --- a/samples/codefirst-sample/codefirst-consumer/src/main/resources/microservice.yaml +++ /dev/null @@ -1,14 +0,0 @@ -APPLICATION_ID: codefirsttest -service_description: - name: codefirstClient - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:30100 - isolation: - Consumer: - enabled: false - references: - codefirst: - version-rule: 0.0.1 diff --git a/samples/codefirst-sample/codefirst-provider/pom.xml b/samples/codefirst-sample/codefirst-provider/pom.xml deleted file mode 100644 index 2c10d3fe523..00000000000 --- a/samples/codefirst-sample/codefirst-provider/pom.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - codefirst-sample - 0.3.0-SNAPSHOT - - codefirst-provider - - - UTF-8 - - - - - io.servicecomb - provider-pojo - - - io.servicecomb - provider-springmvc - - - io.servicecomb - transport-highway - - - io.servicecomb - transport-rest-vertx - - - org.slf4j - slf4j-log4j12 - - - io.servicecomb.samples - commmon-schema - - - \ No newline at end of file diff --git a/samples/codefirst-sample/codefirst-provider/src/main/java/io/servicecomb/samples/codefirst/provider/CodeFIrstJaxrsHelloImpl.java b/samples/codefirst-sample/codefirst-provider/src/main/java/io/servicecomb/samples/codefirst/provider/CodeFIrstJaxrsHelloImpl.java deleted file mode 100644 index 3097613ed06..00000000000 --- a/samples/codefirst-sample/codefirst-provider/src/main/java/io/servicecomb/samples/codefirst/provider/CodeFIrstJaxrsHelloImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.samples.codefirst.provider; - - -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; - -import io.servicecomb.provider.pojo.RpcSchema; -import io.servicecomb.samples.common.schema.Hello; -import io.servicecomb.samples.common.schema.models.Person; - -@RpcSchema(schemaId = "codeFirstJaxrsHello") -@Path("/codefirstjaxrshello") -@Produces(MediaType.APPLICATION_JSON) -public class CodeFIrstJaxrsHelloImpl implements Hello { - - @Path("/sayhi") - @POST - @Override - public String sayHi(String name) { - return "Jaxrs Hello " + name; - } - - @Path("/sayhello") - @POST - @Override - public String sayHello(Person person) { - return "Jaxrs Hello person " + person.getName(); - } -} diff --git a/samples/codefirst-sample/codefirst-provider/src/main/java/io/servicecomb/samples/codefirst/provider/CodeFirstPojoHelloImpl.java b/samples/codefirst-sample/codefirst-provider/src/main/java/io/servicecomb/samples/codefirst/provider/CodeFirstPojoHelloImpl.java deleted file mode 100644 index 49544c5d7e9..00000000000 --- a/samples/codefirst-sample/codefirst-provider/src/main/java/io/servicecomb/samples/codefirst/provider/CodeFirstPojoHelloImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.samples.codefirst.provider; - -import io.servicecomb.provider.pojo.RpcSchema; -import io.servicecomb.samples.common.schema.Hello; -import io.servicecomb.samples.common.schema.models.Person; - -@RpcSchema(schemaId = "codeFirstHello") -public class CodeFirstPojoHelloImpl implements Hello { - - @Override - public String sayHi(String name) { - return "Pojo Hello " + name; - } - - @Override - public String sayHello(Person person) { - return "Pojo Hello person " + person.getName(); - } -} diff --git a/samples/codefirst-sample/codefirst-provider/src/main/java/io/servicecomb/samples/codefirst/provider/CodeFirstProviderMain.java b/samples/codefirst-sample/codefirst-provider/src/main/java/io/servicecomb/samples/codefirst/provider/CodeFirstProviderMain.java deleted file mode 100644 index 52da613d6cd..00000000000 --- a/samples/codefirst-sample/codefirst-provider/src/main/java/io/servicecomb/samples/codefirst/provider/CodeFirstProviderMain.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.samples.codefirst.provider; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class CodeFirstProviderMain { - - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/samples/codefirst-sample/codefirst-provider/src/main/java/io/servicecomb/samples/codefirst/provider/CodeFirstSpringmvcHelloImpl.java b/samples/codefirst-sample/codefirst-provider/src/main/java/io/servicecomb/samples/codefirst/provider/CodeFirstSpringmvcHelloImpl.java deleted file mode 100644 index 89a6e42b3be..00000000000 --- a/samples/codefirst-sample/codefirst-provider/src/main/java/io/servicecomb/samples/codefirst/provider/CodeFirstSpringmvcHelloImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.samples.codefirst.provider; - - -import javax.ws.rs.core.MediaType; - -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; - -import io.servicecomb.provider.pojo.RpcSchema; -import io.servicecomb.samples.common.schema.Hello; -import io.servicecomb.samples.common.schema.models.Person; - -@RpcSchema(schemaId = "codeFirstSpringmvcHello") -@RequestMapping(path = "/springmvchello", produces = MediaType.APPLICATION_JSON) -public class CodeFirstSpringmvcHelloImpl implements Hello { - - @Override - @RequestMapping(path = "/sayhi", method = RequestMethod.POST) - public String sayHi(@RequestParam(name = "name") String name) { - return "Spring mvc Hello " + name; - } - - @Override - @RequestMapping(path = "/sayhello", method = RequestMethod.POST) - public String sayHello(@RequestBody Person person) { - return "Spring mvc Hello person " + person.getName(); - } -} diff --git a/samples/codefirst-sample/codefirst-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml b/samples/codefirst-sample/codefirst-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml deleted file mode 100644 index 33cdd2205ca..00000000000 --- a/samples/codefirst-sample/codefirst-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - diff --git a/samples/codefirst-sample/codefirst-provider/src/main/resources/config/log4j.demo.properties b/samples/codefirst-sample/codefirst-provider/src/main/resources/config/log4j.demo.properties deleted file mode 100644 index 0324fbb1032..00000000000 --- a/samples/codefirst-sample/codefirst-provider/src/main/resources/config/log4j.demo.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=target/logs/ -paas.logs.file=sample.log - -log4j.rootLogger=INFO,paas,stdout diff --git a/samples/codefirst-sample/codefirst-provider/src/main/resources/microservice.yaml b/samples/codefirst-sample/codefirst-provider/src/main/resources/microservice.yaml deleted file mode 100644 index 9efa727baea..00000000000 --- a/samples/codefirst-sample/codefirst-provider/src/main/resources/microservice.yaml +++ /dev/null @@ -1,12 +0,0 @@ -APPLICATION_ID: codefirsttest -service_description: - name: codefirst - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:30100 - rest: - address: 0.0.0.0:8080 - highway: - address: 0.0.0.0:7070 diff --git a/samples/codefirst-sample/pom.xml b/samples/codefirst-sample/pom.xml deleted file mode 100644 index 7ac8ced334d..00000000000 --- a/samples/codefirst-sample/pom.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - samples - 0.3.0-SNAPSHOT - - codefirst-sample - pom - - - UTF-8 - - - - codefirst-provider - codefirst-consumer - - \ No newline at end of file diff --git a/samples/commmon-schema/pom.xml b/samples/commmon-schema/pom.xml deleted file mode 100644 index 4d021870bbe..00000000000 --- a/samples/commmon-schema/pom.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - samples - 0.3.0-SNAPSHOT - - commmon-schema - \ No newline at end of file diff --git a/samples/commmon-schema/src/main/java/io/servicecomb/samples/common/schema/Hello.java b/samples/commmon-schema/src/main/java/io/servicecomb/samples/common/schema/Hello.java deleted file mode 100644 index 27d7fab80ec..00000000000 --- a/samples/commmon-schema/src/main/java/io/servicecomb/samples/common/schema/Hello.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.samples.common.schema; - - -import io.servicecomb.samples.common.schema.models.Person; - -public interface Hello { - - String sayHi(String name); - - String sayHello(Person person); -} diff --git a/samples/commmon-schema/src/main/java/io/servicecomb/samples/common/schema/models/Person.java b/samples/commmon-schema/src/main/java/io/servicecomb/samples/common/schema/models/Person.java deleted file mode 100644 index eb58d61425c..00000000000 --- a/samples/commmon-schema/src/main/java/io/servicecomb/samples/common/schema/models/Person.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.samples.common.schema.models; - -public class Person { - - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/samples/custom-handler-sample/custom-handler-consumer/pom.xml b/samples/custom-handler-sample/custom-handler-consumer/pom.xml deleted file mode 100644 index 40bef96299b..00000000000 --- a/samples/custom-handler-sample/custom-handler-consumer/pom.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - custom-handler-sample - 0.3.0-SNAPSHOT - - custom-handler-consumer - - - - io.servicecomb - provider-pojo - - - - io.servicecomb - transport-highway - - - io.servicecomb - transport-rest-vertx - - - - io.servicecomb - handler-bizkeeper - - - io.servicecomb - handler-loadbalance - - - - org.slf4j - slf4j-log4j12 - - - io.servicecomb.samples - commmon-schema - - - \ No newline at end of file diff --git a/samples/custom-handler-sample/custom-handler-consumer/src/main/java/io/servicecomb/samples/customerhandler/consumer/CustomHandlerCustomerMain.java b/samples/custom-handler-sample/custom-handler-consumer/src/main/java/io/servicecomb/samples/customerhandler/consumer/CustomHandlerCustomerMain.java deleted file mode 100644 index c1271f33c8d..00000000000 --- a/samples/custom-handler-sample/custom-handler-consumer/src/main/java/io/servicecomb/samples/customerhandler/consumer/CustomHandlerCustomerMain.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.samples.customerhandler.consumer; - -import org.springframework.stereotype.Component; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.provider.pojo.RpcReference; -import io.servicecomb.samples.common.schema.Hello; -import io.servicecomb.samples.common.schema.models.Person; - -@Component -public class CustomHandlerCustomerMain { - - @RpcReference(microserviceName = "customerhandler", schemaId = "hello") - private static Hello hello; - - public static void main(String[] args) throws Exception { - init(); - System.out.println(hello.sayHi("Java Chassis")); - Person person = new Person(); - person.setName("ServiceComb/Java Chassis"); - System.out.println(hello.sayHello(person)); - } - - public static void init() throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/samples/custom-handler-sample/custom-handler-consumer/src/main/java/io/servicecomb/samples/customerhandler/handlers/MyHandler.java b/samples/custom-handler-sample/custom-handler-consumer/src/main/java/io/servicecomb/samples/customerhandler/handlers/MyHandler.java deleted file mode 100644 index 25719f31add..00000000000 --- a/samples/custom-handler-sample/custom-handler-consumer/src/main/java/io/servicecomb/samples/customerhandler/handlers/MyHandler.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.samples.customerhandler.handlers; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.handler.impl.AbstractHandler; -import io.servicecomb.swagger.invocation.AsyncResponse; - -public class MyHandler extends AbstractHandler { - - private static final Logger LOGGER = LoggerFactory.getLogger(MyHandler.class); - - @Override - public void handle(Invocation invocation, AsyncResponse asyncResponse) throws Exception { - //code before - - LOGGER.info("It's my handler! \r\n"); - - invocation.next(response -> { - // code after - asyncResponse.handle(response); - }); - } -} diff --git a/samples/custom-handler-sample/custom-handler-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml b/samples/custom-handler-sample/custom-handler-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml deleted file mode 100644 index 3f5a15eafd9..00000000000 --- a/samples/custom-handler-sample/custom-handler-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - diff --git a/samples/custom-handler-sample/custom-handler-consumer/src/main/resources/config/cse.handler.xml b/samples/custom-handler-sample/custom-handler-consumer/src/main/resources/config/cse.handler.xml deleted file mode 100644 index 987abbe3740..00000000000 --- a/samples/custom-handler-sample/custom-handler-consumer/src/main/resources/config/cse.handler.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - \ No newline at end of file diff --git a/samples/custom-handler-sample/custom-handler-consumer/src/main/resources/config/log4j.demo.properties b/samples/custom-handler-sample/custom-handler-consumer/src/main/resources/config/log4j.demo.properties deleted file mode 100644 index 0324fbb1032..00000000000 --- a/samples/custom-handler-sample/custom-handler-consumer/src/main/resources/config/log4j.demo.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=target/logs/ -paas.logs.file=sample.log - -log4j.rootLogger=INFO,paas,stdout diff --git a/samples/custom-handler-sample/custom-handler-consumer/src/main/resources/microservice.yaml b/samples/custom-handler-sample/custom-handler-consumer/src/main/resources/microservice.yaml deleted file mode 100644 index 641c4df1544..00000000000 --- a/samples/custom-handler-sample/custom-handler-consumer/src/main/resources/microservice.yaml +++ /dev/null @@ -1,18 +0,0 @@ -APPLICATION_ID: chstest -service_description: - name: chsClient - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:30100 - handler: - chain: - Consumer: - default: bizkeeper-consumer,loadbalance, myhandler - isolation: - Consumer: - enabled: false - references: - chs: - version-rule: 0.0.1 diff --git a/samples/custom-handler-sample/custom-handler-provider/pom.xml b/samples/custom-handler-sample/custom-handler-provider/pom.xml deleted file mode 100644 index 6252b36234b..00000000000 --- a/samples/custom-handler-sample/custom-handler-provider/pom.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - custom-handler-sample - 0.3.0-SNAPSHOT - - custom-handler-provider - - - - io.servicecomb - provider-pojo - - - - io.servicecomb - transport-highway - - - io.servicecomb - transport-rest-vertx - - - - io.servicecomb - handler-loadbalance - - - io.servicecomb - handler-flowcontrol-qps - - - - org.slf4j - slf4j-log4j12 - - - io.servicecomb.samples - commmon-schema - - - \ No newline at end of file diff --git a/samples/custom-handler-sample/custom-handler-provider/src/main/java/io/servicecomb/samples/customerhandler/provider/CustomHandlerProviderMain.java b/samples/custom-handler-sample/custom-handler-provider/src/main/java/io/servicecomb/samples/customerhandler/provider/CustomHandlerProviderMain.java deleted file mode 100644 index faad2d117e8..00000000000 --- a/samples/custom-handler-sample/custom-handler-provider/src/main/java/io/servicecomb/samples/customerhandler/provider/CustomHandlerProviderMain.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.samples.customerhandler.provider; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class CustomHandlerProviderMain { - - public static void main(String[] args) - throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/samples/custom-handler-sample/custom-handler-provider/src/main/java/io/servicecomb/samples/customerhandler/provider/HelloImpl.java b/samples/custom-handler-sample/custom-handler-provider/src/main/java/io/servicecomb/samples/customerhandler/provider/HelloImpl.java deleted file mode 100644 index 3c994f4f877..00000000000 --- a/samples/custom-handler-sample/custom-handler-provider/src/main/java/io/servicecomb/samples/customerhandler/provider/HelloImpl.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.samples.customerhandler.provider; - - -import io.servicecomb.provider.pojo.RpcSchema; -import io.servicecomb.samples.common.schema.Hello; -import io.servicecomb.samples.common.schema.models.Person; - -@RpcSchema(schemaId = "hello") -public class HelloImpl implements Hello { - - @Override - public String sayHi(String name) { - return "Hello " + name; - } - - @Override - public String sayHello(Person person) { - return "Hello person " + person.getName(); - } -} diff --git a/samples/custom-handler-sample/custom-handler-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml b/samples/custom-handler-sample/custom-handler-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml deleted file mode 100644 index 9574fa9613f..00000000000 --- a/samples/custom-handler-sample/custom-handler-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/samples/custom-handler-sample/custom-handler-provider/src/main/resources/config/log4j.demo.properties b/samples/custom-handler-sample/custom-handler-provider/src/main/resources/config/log4j.demo.properties deleted file mode 100644 index 0324fbb1032..00000000000 --- a/samples/custom-handler-sample/custom-handler-provider/src/main/resources/config/log4j.demo.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=target/logs/ -paas.logs.file=sample.log - -log4j.rootLogger=INFO,paas,stdout diff --git a/samples/custom-handler-sample/custom-handler-provider/src/main/resources/microservice.yaml b/samples/custom-handler-sample/custom-handler-provider/src/main/resources/microservice.yaml deleted file mode 100644 index 0ea5799214f..00000000000 --- a/samples/custom-handler-sample/custom-handler-provider/src/main/resources/microservice.yaml +++ /dev/null @@ -1,12 +0,0 @@ -APPLICATION_ID: chstest -service_description: - name: customerhandler - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:30100 - rest: - address: 0.0.0.0:8080 - highway: - address: 0.0.0.0:7070 diff --git a/samples/custom-handler-sample/pom.xml b/samples/custom-handler-sample/pom.xml deleted file mode 100644 index 4b1482c3d64..00000000000 --- a/samples/custom-handler-sample/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - samples - 0.3.0-SNAPSHOT - - custom-handler-sample - pom - - custom-handler-consumer - custom-handler-provider - - \ No newline at end of file diff --git a/samples/jaxrs-sample/jaxrs-consumer/pom.xml b/samples/jaxrs-sample/jaxrs-consumer/pom.xml deleted file mode 100644 index 4e392f0a927..00000000000 --- a/samples/jaxrs-sample/jaxrs-consumer/pom.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - jaxrs-sample - 0.3.0-SNAPSHOT - - jaxrs-consumer - - - io.servicecomb - handler-bizkeeper - - - io.servicecomb - handler-loadbalance - - - io.servicecomb - transport-highway - - - io.servicecomb - transport-rest-vertx - - - io.servicecomb - handler-flowcontrol-qps - - - io.servicecomb - provider-pojo - - - org.slf4j - slf4j-log4j12 - - - io.servicecomb.samples - commmon-schema - - - \ No newline at end of file diff --git a/samples/jaxrs-sample/jaxrs-consumer/src/main/java/io/servicecomb/samples/jaxrs/consumer/JaxrsConsumerMain.java b/samples/jaxrs-sample/jaxrs-consumer/src/main/java/io/servicecomb/samples/jaxrs/consumer/JaxrsConsumerMain.java deleted file mode 100644 index 6ca99ebf603..00000000000 --- a/samples/jaxrs-sample/jaxrs-consumer/src/main/java/io/servicecomb/samples/jaxrs/consumer/JaxrsConsumerMain.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.samples.jaxrs.consumer; - -import org.springframework.stereotype.Component; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.provider.pojo.RpcReference; -import io.servicecomb.samples.common.schema.Hello; -import io.servicecomb.samples.common.schema.models.Person; - -@Component -public class JaxrsConsumerMain { - - @RpcReference(microserviceName = "jaxrs", schemaId = "jaxrsHello") - private static Hello hello; - - public static void main(String[] args) throws Exception { - init(); - System.out.println(hello.sayHi("Java Chassis")); - Person person = new Person(); - person.setName("ServiceComb/Java Chassis"); - System.out.println(hello.sayHello(person)); - } - - public static void init() throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/samples/jaxrs-sample/jaxrs-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml b/samples/jaxrs-sample/jaxrs-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml deleted file mode 100644 index 79c07534160..00000000000 --- a/samples/jaxrs-sample/jaxrs-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/samples/jaxrs-sample/jaxrs-consumer/src/main/resources/config/log4j.demo.properties b/samples/jaxrs-sample/jaxrs-consumer/src/main/resources/config/log4j.demo.properties deleted file mode 100644 index 0324fbb1032..00000000000 --- a/samples/jaxrs-sample/jaxrs-consumer/src/main/resources/config/log4j.demo.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=target/logs/ -paas.logs.file=sample.log - -log4j.rootLogger=INFO,paas,stdout diff --git a/samples/jaxrs-sample/jaxrs-consumer/src/main/resources/microservice.yaml b/samples/jaxrs-sample/jaxrs-consumer/src/main/resources/microservice.yaml deleted file mode 100644 index c682cdd6625..00000000000 --- a/samples/jaxrs-sample/jaxrs-consumer/src/main/resources/microservice.yaml +++ /dev/null @@ -1,18 +0,0 @@ -APPLICATION_ID: jaxrstest-sample -service_description: - name: helloClient - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:30100 - handler: - chain: - Consumer: - default: bizkeeper-consumer,loadbalance - isolation: - Consumer: - enabled: false - references: - jaxrs: - version-rule: 0.0.1 diff --git a/samples/jaxrs-sample/jaxrs-consumer/src/main/resources/microservices/hello/hello.yaml b/samples/jaxrs-sample/jaxrs-consumer/src/main/resources/microservices/hello/hello.yaml deleted file mode 100644 index 5162661aca7..00000000000 --- a/samples/jaxrs-sample/jaxrs-consumer/src/main/resources/microservices/hello/hello.yaml +++ /dev/null @@ -1,28 +0,0 @@ -swagger: '2.0' -info: - title: hello - version: 1.0.0 - x-java-interface: io.servicecomb.samples.jaxrs.Hello -basePath: /pojo/rest/hello -produces: - - application/json - -paths: - /sayhi: - post: - operationId: sayHi - parameters: - - name: name - in: body - required: true - schema: - type: string - responses: - 200: - description: 正确返回 - schema: - type: string - default: - description: 默认返回 - schema: - type: string \ No newline at end of file diff --git a/samples/jaxrs-sample/jaxrs-provider/pom.xml b/samples/jaxrs-sample/jaxrs-provider/pom.xml deleted file mode 100644 index 5b98ec2ada4..00000000000 --- a/samples/jaxrs-sample/jaxrs-provider/pom.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - jaxrs-sample - 0.3.0-SNAPSHOT - - jaxrs-provider - - - io.servicecomb - handler-bizkeeper - - - io.servicecomb - handler-loadbalance - - - io.servicecomb - handler-flowcontrol-qps - - - io.servicecomb - transport-highway - - - io.servicecomb - transport-rest-vertx - - - io.servicecomb - provider-rest-common - - - org.slf4j - slf4j-log4j12 - - - io.servicecomb.samples - commmon-schema - - - \ No newline at end of file diff --git a/samples/jaxrs-sample/jaxrs-provider/src/main/java/io/servicecomb/samples/jaxrs/provider/JaxrsHelloImpl.java b/samples/jaxrs-sample/jaxrs-provider/src/main/java/io/servicecomb/samples/jaxrs/provider/JaxrsHelloImpl.java deleted file mode 100644 index 4634abd1bdf..00000000000 --- a/samples/jaxrs-sample/jaxrs-provider/src/main/java/io/servicecomb/samples/jaxrs/provider/JaxrsHelloImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.samples.jaxrs.provider; - - -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; - -import io.servicecomb.provider.rest.common.RestSchema; -import io.servicecomb.samples.common.schema.Hello; -import io.servicecomb.samples.common.schema.models.Person; - -@RestSchema(schemaId = "jaxrsHello") -@Path("/jaxrshello") -@Produces(MediaType.APPLICATION_JSON) -public class JaxrsHelloImpl implements Hello { - - @Path("/sayhi") - @POST - @Override - public String sayHi(String name) { - return "Hello " + name; - } - - @Path("/sayhello") - @POST - @Override - public String sayHello(Person person) { - return "Hello person " + person.getName(); - } -} diff --git a/samples/jaxrs-sample/jaxrs-provider/src/main/java/io/servicecomb/samples/jaxrs/provider/JaxrsProviderMain.java b/samples/jaxrs-sample/jaxrs-provider/src/main/java/io/servicecomb/samples/jaxrs/provider/JaxrsProviderMain.java deleted file mode 100644 index 73201e195fb..00000000000 --- a/samples/jaxrs-sample/jaxrs-provider/src/main/java/io/servicecomb/samples/jaxrs/provider/JaxrsProviderMain.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.samples.jaxrs.provider; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class JaxrsProviderMain { - - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/samples/jaxrs-sample/jaxrs-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml b/samples/jaxrs-sample/jaxrs-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml deleted file mode 100644 index def470ec4bb..00000000000 --- a/samples/jaxrs-sample/jaxrs-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/samples/jaxrs-sample/jaxrs-provider/src/main/resources/config/log4j.demo.properties b/samples/jaxrs-sample/jaxrs-provider/src/main/resources/config/log4j.demo.properties deleted file mode 100644 index 0324fbb1032..00000000000 --- a/samples/jaxrs-sample/jaxrs-provider/src/main/resources/config/log4j.demo.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=target/logs/ -paas.logs.file=sample.log - -log4j.rootLogger=INFO,paas,stdout diff --git a/samples/jaxrs-sample/jaxrs-provider/src/main/resources/microservice.yaml b/samples/jaxrs-sample/jaxrs-provider/src/main/resources/microservice.yaml deleted file mode 100644 index a58004913fd..00000000000 --- a/samples/jaxrs-sample/jaxrs-provider/src/main/resources/microservice.yaml +++ /dev/null @@ -1,12 +0,0 @@ -APPLICATION_ID: jaxrstest-sample -service_description: - name: jaxrs - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:30100 - rest: - address: 0.0.0.0:8080 - highway: - address: 0.0.0.0:7070 diff --git a/samples/jaxrs-sample/jaxrs-provider/src/main/resources/microservices/hello/hello.yaml b/samples/jaxrs-sample/jaxrs-provider/src/main/resources/microservices/hello/hello.yaml deleted file mode 100644 index 5162661aca7..00000000000 --- a/samples/jaxrs-sample/jaxrs-provider/src/main/resources/microservices/hello/hello.yaml +++ /dev/null @@ -1,28 +0,0 @@ -swagger: '2.0' -info: - title: hello - version: 1.0.0 - x-java-interface: io.servicecomb.samples.jaxrs.Hello -basePath: /pojo/rest/hello -produces: - - application/json - -paths: - /sayhi: - post: - operationId: sayHi - parameters: - - name: name - in: body - required: true - schema: - type: string - responses: - 200: - description: 正确返回 - schema: - type: string - default: - description: 默认返回 - schema: - type: string \ No newline at end of file diff --git a/samples/jaxrs-sample/pom.xml b/samples/jaxrs-sample/pom.xml deleted file mode 100644 index bb5e9d2a15b..00000000000 --- a/samples/jaxrs-sample/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - samples - 0.3.0-SNAPSHOT - - jaxrs-sample - pom - - jaxrs-provider - jaxrs-consumer - - \ No newline at end of file diff --git a/samples/pojo-sample/pojo-consumer/pom.xml b/samples/pojo-sample/pojo-consumer/pom.xml deleted file mode 100644 index 35621f16bb2..00000000000 --- a/samples/pojo-sample/pojo-consumer/pom.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - pojo-sample - 0.3.0-SNAPSHOT - - pojo-consumer - - - io.servicecomb - handler-bizkeeper - - - io.servicecomb - handler-loadbalance - - - io.servicecomb - transport-highway - - - io.servicecomb - transport-rest-vertx - - - io.servicecomb - handler-flowcontrol-qps - - - io.servicecomb - provider-pojo - - - org.slf4j - slf4j-log4j12 - - - io.servicecomb.samples - commmon-schema - - - \ No newline at end of file diff --git a/samples/pojo-sample/pojo-consumer/src/main/java/io/servicecomb/samples/pojo/Compute.java b/samples/pojo-sample/pojo-consumer/src/main/java/io/servicecomb/samples/pojo/Compute.java deleted file mode 100644 index 3e69858d787..00000000000 --- a/samples/pojo-sample/pojo-consumer/src/main/java/io/servicecomb/samples/pojo/Compute.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.samples.pojo; - -public interface Compute { - - int add(int a, int b); - - int multi(int a, int b); - - int sub(int a, int b); - - int divide(int a, int b); -} diff --git a/samples/pojo-sample/pojo-consumer/src/main/java/io/servicecomb/samples/pojo/consumer/PojoConsumerMain.java b/samples/pojo-sample/pojo-consumer/src/main/java/io/servicecomb/samples/pojo/consumer/PojoConsumerMain.java deleted file mode 100644 index 57f54777a76..00000000000 --- a/samples/pojo-sample/pojo-consumer/src/main/java/io/servicecomb/samples/pojo/consumer/PojoConsumerMain.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.samples.pojo.consumer; - -import org.springframework.stereotype.Component; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.provider.pojo.RpcReference; -import io.servicecomb.samples.common.schema.Hello; -import io.servicecomb.samples.common.schema.models.Person; -import io.servicecomb.samples.pojo.Compute; - -@Component -public class PojoConsumerMain { - - @RpcReference(microserviceName = "hello", schemaId = "hello") - private static Hello hello; - - @RpcReference(microserviceName = "hello", schemaId = "codeFirstCompute") - public static Compute compute; - - public static void main(String[] args) - throws Exception { - init(); - System.out.println(hello.sayHi("Java Chassis")); - Person person = new Person(); - person.setName("ServiceComb/Java Chassis"); - System.out.println(hello.sayHello(person)); - System.out.println("a=1, b=2, result=" + compute.add(1, 2)); - } - - public static void init() - throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/samples/pojo-sample/pojo-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml b/samples/pojo-sample/pojo-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml deleted file mode 100644 index 5a192b2c5ea..00000000000 --- a/samples/pojo-sample/pojo-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/samples/pojo-sample/pojo-consumer/src/main/resources/config/log4j.demo.properties b/samples/pojo-sample/pojo-consumer/src/main/resources/config/log4j.demo.properties deleted file mode 100644 index 0324fbb1032..00000000000 --- a/samples/pojo-sample/pojo-consumer/src/main/resources/config/log4j.demo.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=target/logs/ -paas.logs.file=sample.log - -log4j.rootLogger=INFO,paas,stdout diff --git a/samples/pojo-sample/pojo-consumer/src/main/resources/microservice.yaml b/samples/pojo-sample/pojo-consumer/src/main/resources/microservice.yaml deleted file mode 100644 index 35be933ab17..00000000000 --- a/samples/pojo-sample/pojo-consumer/src/main/resources/microservice.yaml +++ /dev/null @@ -1,18 +0,0 @@ -APPLICATION_ID: pojo-sample -service_description: - name: helloClient - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:30100 - handler: - chain: - Consumer: - default: bizkeeper-consumer,loadbalance - isolation: - Consumer: - enabled: false - references: - hello: - version-rule: 0.0.1 diff --git a/samples/pojo-sample/pojo-consumer/src/main/resources/microservices/hello/hello.yaml b/samples/pojo-sample/pojo-consumer/src/main/resources/microservices/hello/hello.yaml deleted file mode 100644 index 932e1ed385c..00000000000 --- a/samples/pojo-sample/pojo-consumer/src/main/resources/microservices/hello/hello.yaml +++ /dev/null @@ -1,55 +0,0 @@ -swagger: '2.0' -info: - title: hello - version: 1.0.0 - x-java-interface: io.servicecomb.samples.pojo.Hello -basePath: /pojo/rest/hello -produces: - - application/json - -paths: - /sayhi: - post: - operationId: sayHi - parameters: - - name: name - in: body - required: true - schema: - type: string - responses: - 200: - description: 正确返回 - schema: - type: string - default: - description: 默认返回 - schema: - type: string - /sayhello: - post: - operationId: sayHello - parameters: - - name: person - in: body - required: true - schema: - $ref: "#/definitions/Person" - responses: - 200: - description: 正确返回 - schema: - type: string - default: - description: 默认返回 - schema: - type: string -definitions: - Person: - type: "object" - properties: - name: - type: "string" - description: "person name" - xml: - name: "Person" \ No newline at end of file diff --git a/samples/pojo-sample/pojo-provider/pom.xml b/samples/pojo-sample/pojo-provider/pom.xml deleted file mode 100644 index a18bf68e62a..00000000000 --- a/samples/pojo-sample/pojo-provider/pom.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - pojo-sample - 0.3.0-SNAPSHOT - - pojo-provider - - - io.servicecomb - handler-bizkeeper - - - io.servicecomb - handler-loadbalance - - - io.servicecomb - transport-highway - - - io.servicecomb - transport-rest-vertx - - - io.servicecomb - handler-flowcontrol-qps - - - io.servicecomb - provider-pojo - - - org.slf4j - slf4j-log4j12 - - - io.servicecomb.samples - commmon-schema - - - \ No newline at end of file diff --git a/samples/pojo-sample/pojo-provider/src/main/java/io/servicecomb/samples/pojo/Compute.java b/samples/pojo-sample/pojo-provider/src/main/java/io/servicecomb/samples/pojo/Compute.java deleted file mode 100644 index 3e69858d787..00000000000 --- a/samples/pojo-sample/pojo-provider/src/main/java/io/servicecomb/samples/pojo/Compute.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.samples.pojo; - -public interface Compute { - - int add(int a, int b); - - int multi(int a, int b); - - int sub(int a, int b); - - int divide(int a, int b); -} diff --git a/samples/pojo-sample/pojo-provider/src/main/java/io/servicecomb/samples/pojo/provider/CodeFirstComputeImpl.java b/samples/pojo-sample/pojo-provider/src/main/java/io/servicecomb/samples/pojo/provider/CodeFirstComputeImpl.java deleted file mode 100644 index 66f5e29f3e6..00000000000 --- a/samples/pojo-sample/pojo-provider/src/main/java/io/servicecomb/samples/pojo/provider/CodeFirstComputeImpl.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.samples.pojo.provider; - - -import io.servicecomb.provider.pojo.RpcSchema; -import io.servicecomb.samples.pojo.Compute; - -@RpcSchema(schemaId = "codeFirstCompute") -public class CodeFirstComputeImpl implements Compute { - @Override - public int add(int a, int b) { - return a + b; - } - - @Override - public int multi(int a, int b) { - return a * b; - } - - @Override - public int sub(int a, int b) { - return a - b; - } - - @Override - public int divide(int a, int b) { - if (b != 0) { - return a / b; - } - return 0; - } -} diff --git a/samples/pojo-sample/pojo-provider/src/main/java/io/servicecomb/samples/pojo/provider/HelloImpl.java b/samples/pojo-sample/pojo-provider/src/main/java/io/servicecomb/samples/pojo/provider/HelloImpl.java deleted file mode 100644 index 06950757365..00000000000 --- a/samples/pojo-sample/pojo-provider/src/main/java/io/servicecomb/samples/pojo/provider/HelloImpl.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.samples.pojo.provider; - - -import io.servicecomb.provider.pojo.RpcSchema; -import io.servicecomb.samples.common.schema.Hello; -import io.servicecomb.samples.common.schema.models.Person; - -@RpcSchema(schemaId = "hello") -public class HelloImpl implements Hello { - - @Override - public String sayHi(String name) { - return "Hello " + name; - } - - @Override - public String sayHello(Person person) { - return "Hello person " + person.getName(); - } -} diff --git a/samples/pojo-sample/pojo-provider/src/main/java/io/servicecomb/samples/pojo/provider/PojoProviderMain.java b/samples/pojo-sample/pojo-provider/src/main/java/io/servicecomb/samples/pojo/provider/PojoProviderMain.java deleted file mode 100644 index 180aaeb0bd2..00000000000 --- a/samples/pojo-sample/pojo-provider/src/main/java/io/servicecomb/samples/pojo/provider/PojoProviderMain.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.samples.pojo.provider; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class PojoProviderMain { - - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/samples/pojo-sample/pojo-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml b/samples/pojo-sample/pojo-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml deleted file mode 100644 index 2acd82877ea..00000000000 --- a/samples/pojo-sample/pojo-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/samples/pojo-sample/pojo-provider/src/main/resources/config/log4j.demo.properties b/samples/pojo-sample/pojo-provider/src/main/resources/config/log4j.demo.properties deleted file mode 100644 index 0324fbb1032..00000000000 --- a/samples/pojo-sample/pojo-provider/src/main/resources/config/log4j.demo.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=target/logs/ -paas.logs.file=sample.log - -log4j.rootLogger=INFO,paas,stdout diff --git a/samples/pojo-sample/pojo-provider/src/main/resources/microservice.yaml b/samples/pojo-sample/pojo-provider/src/main/resources/microservice.yaml deleted file mode 100644 index f63880c6bd6..00000000000 --- a/samples/pojo-sample/pojo-provider/src/main/resources/microservice.yaml +++ /dev/null @@ -1,12 +0,0 @@ -APPLICATION_ID: pojo-sample -service_description: - name: hello - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:30100 - rest: - address: 0.0.0.0:8080 - highway: - address: 0.0.0.0:7070 diff --git a/samples/pojo-sample/pojo-provider/src/main/resources/microservices/hello/hello.yaml b/samples/pojo-sample/pojo-provider/src/main/resources/microservices/hello/hello.yaml deleted file mode 100644 index 932e1ed385c..00000000000 --- a/samples/pojo-sample/pojo-provider/src/main/resources/microservices/hello/hello.yaml +++ /dev/null @@ -1,55 +0,0 @@ -swagger: '2.0' -info: - title: hello - version: 1.0.0 - x-java-interface: io.servicecomb.samples.pojo.Hello -basePath: /pojo/rest/hello -produces: - - application/json - -paths: - /sayhi: - post: - operationId: sayHi - parameters: - - name: name - in: body - required: true - schema: - type: string - responses: - 200: - description: 正确返回 - schema: - type: string - default: - description: 默认返回 - schema: - type: string - /sayhello: - post: - operationId: sayHello - parameters: - - name: person - in: body - required: true - schema: - $ref: "#/definitions/Person" - responses: - 200: - description: 正确返回 - schema: - type: string - default: - description: 默认返回 - schema: - type: string -definitions: - Person: - type: "object" - properties: - name: - type: "string" - description: "person name" - xml: - name: "Person" \ No newline at end of file diff --git a/samples/pojo-sample/pom.xml b/samples/pojo-sample/pom.xml deleted file mode 100644 index 187f65400bd..00000000000 --- a/samples/pojo-sample/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - samples - 0.3.0-SNAPSHOT - - pojo-sample - pom - - pojo-provider - pojo-consumer - - \ No newline at end of file diff --git a/samples/pom.xml b/samples/pom.xml deleted file mode 100644 index 2caa94fec94..00000000000 --- a/samples/pom.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - samples - pom - 0.3.0-SNAPSHOT - - codefirst-sample - custom-handler-sample - jaxrs-sample - pojo-sample - springmvc-sample - commmon-schema - - - - - - io.servicecomb - java-chassis-dependencies - 0.3.0-SNAPSHOT - pom - import - - - io.servicecomb.samples - commmon-schema - 0.3.0-SNAPSHOT - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.1 - - -parameters - UTF-8 - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - - diff --git a/samples/springmvc-sample/pom.xml b/samples/springmvc-sample/pom.xml deleted file mode 100644 index a0a17afcc4c..00000000000 --- a/samples/springmvc-sample/pom.xml +++ /dev/null @@ -1,14 +0,0 @@ - - 4.0.0 - - io.servicecomb.samples - samples - 0.3.0-SNAPSHOT - - springmvc-sample - pom - - springmvc-provider - springmvc-consumer - - \ No newline at end of file diff --git a/samples/springmvc-sample/springmvc-consumer/pom.xml b/samples/springmvc-sample/springmvc-consumer/pom.xml deleted file mode 100644 index 7f8f23309e0..00000000000 --- a/samples/springmvc-sample/springmvc-consumer/pom.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - springmvc-sample - 0.3.0-SNAPSHOT - - springmvc-consumer - - - io.servicecomb - handler-bizkeeper - - - io.servicecomb - handler-loadbalance - - - io.servicecomb - transport-highway - - - io.servicecomb - transport-rest-vertx - - - io.servicecomb - handler-flowcontrol-qps - - - io.servicecomb - provider-springmvc - - - io.servicecomb - provider-pojo - - - org.slf4j - slf4j-log4j12 - - - io.servicecomb.samples - commmon-schema - - - \ No newline at end of file diff --git a/samples/springmvc-sample/springmvc-consumer/src/main/java/io/servicecomb/samples/springmvc/consumer/SpringmvcConsumerMain.java b/samples/springmvc-sample/springmvc-consumer/src/main/java/io/servicecomb/samples/springmvc/consumer/SpringmvcConsumerMain.java deleted file mode 100644 index 443bb62fbf2..00000000000 --- a/samples/springmvc-sample/springmvc-consumer/src/main/java/io/servicecomb/samples/springmvc/consumer/SpringmvcConsumerMain.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.samples.springmvc.consumer; - -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.provider.pojo.RpcReference; -import io.servicecomb.provider.springmvc.reference.RestTemplateBuilder; -import io.servicecomb.samples.common.schema.Hello; -import io.servicecomb.samples.common.schema.models.Person; - -@Component -public class SpringmvcConsumerMain { - - private static RestTemplate restTemplate = RestTemplateBuilder.create(); - - @RpcReference(microserviceName = "springmvc", schemaId = "springmvcHello") - private static Hello hello; - - public static void main(String[] args) throws Exception { - init(); - Person person = new Person(); - person.setName("ServiceComb/Java Chassis"); - - // RestTemplate Consumer or POJO Consumer. You can choose whatever you like - // RestTemplate Consumer - String sayHiResult = - restTemplate.postForObject("cse://springmvc/springmvchello/sayhi?name=Java Chassis", null, String.class); - String sayHelloResult = restTemplate.postForObject("cse://springmvc/springmvchello/sayhello", person, String.class); - System.out.println("RestTemplate Consumer or POJO Consumer. You can choose whatever you like."); - System.out.println("RestTemplate consumer sayhi services: " + sayHiResult); - System.out.println("RestTemplate consumer sayhello services: " + sayHelloResult); - - // POJO Consumer - System.out.println("POJO consumer sayhi services: " + hello.sayHi("Java Chassis")); - System.out.println("POJO consumer sayhi services: " + hello.sayHello(person)); - } - - public static void init() throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/samples/springmvc-sample/springmvc-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml b/samples/springmvc-sample/springmvc-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml deleted file mode 100644 index 5a192b2c5ea..00000000000 --- a/samples/springmvc-sample/springmvc-consumer/src/main/resources/META-INF/spring/pojo.consumer.bean.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/samples/springmvc-sample/springmvc-consumer/src/main/resources/config/log4j.demo.properties b/samples/springmvc-sample/springmvc-consumer/src/main/resources/config/log4j.demo.properties deleted file mode 100644 index 0324fbb1032..00000000000 --- a/samples/springmvc-sample/springmvc-consumer/src/main/resources/config/log4j.demo.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=target/logs/ -paas.logs.file=sample.log - -log4j.rootLogger=INFO,paas,stdout diff --git a/samples/springmvc-sample/springmvc-consumer/src/main/resources/microservice.yaml b/samples/springmvc-sample/springmvc-consumer/src/main/resources/microservice.yaml deleted file mode 100644 index a5359ada323..00000000000 --- a/samples/springmvc-sample/springmvc-consumer/src/main/resources/microservice.yaml +++ /dev/null @@ -1,18 +0,0 @@ -APPLICATION_ID: springmvc-sample -service_description: - name: springmvcConsumer - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:30100 - handler: - chain: - Consumer: - default: bizkeeper-consumer,loadbalance - isolation: - Consumer: - enabled: false - references: - springmvc: - version-rule: 0.0.1 diff --git a/samples/springmvc-sample/springmvc-consumer/src/main/resources/microservices/hello/hello.yaml b/samples/springmvc-sample/springmvc-consumer/src/main/resources/microservices/hello/hello.yaml deleted file mode 100644 index be8ea88dda4..00000000000 --- a/samples/springmvc-sample/springmvc-consumer/src/main/resources/microservices/hello/hello.yaml +++ /dev/null @@ -1,55 +0,0 @@ -swagger: '2.0' -info: - title: hello - version: 1.0.0 - x-java-interface: io.servicecomb.samples.springmvc.Hello -basePath: /pojo/rest/hello -produces: - - application/json - -paths: - /sayhi: - post: - operationId: sayHi - parameters: - - name: name - in: body - required: true - schema: - type: string - responses: - 200: - description: 正确返回 - schema: - type: string - default: - description: 默认返回 - schema: - type: string - /sayhello: - post: - operationId: sayHello - parameters: - - name: person - in: body - required: true - schema: - $ref: "#/definitions/Person" - responses: - 200: - description: 正确返回 - schema: - type: string - default: - description: 默认返回 - schema: - type: string -definitions: - Person: - type: "object" - properties: - name: - type: "string" - description: "person name" - xml: - name: "Person" \ No newline at end of file diff --git a/samples/springmvc-sample/springmvc-provider/pom.xml b/samples/springmvc-sample/springmvc-provider/pom.xml deleted file mode 100644 index ee4e427e404..00000000000 --- a/samples/springmvc-sample/springmvc-provider/pom.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - 4.0.0 - - io.servicecomb.samples - springmvc-sample - 0.3.0-SNAPSHOT - - springmvc-provider - - - io.servicecomb - handler-bizkeeper - - - io.servicecomb - handler-loadbalance - - - io.servicecomb - transport-highway - - - io.servicecomb - transport-rest-vertx - - - io.servicecomb - handler-flowcontrol-qps - - - io.servicecomb - provider-springmvc - - - org.slf4j - slf4j-log4j12 - - - io.servicecomb.samples - commmon-schema - - - \ No newline at end of file diff --git a/samples/springmvc-sample/springmvc-provider/src/main/java/io/servicecomb/samples/springmvc/provider/SpringmvcHelloImpl.java b/samples/springmvc-sample/springmvc-provider/src/main/java/io/servicecomb/samples/springmvc/provider/SpringmvcHelloImpl.java deleted file mode 100644 index 9430b6e2ae1..00000000000 --- a/samples/springmvc-sample/springmvc-provider/src/main/java/io/servicecomb/samples/springmvc/provider/SpringmvcHelloImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.samples.springmvc.provider; - - -import javax.ws.rs.core.MediaType; - -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; - -import io.servicecomb.provider.rest.common.RestSchema; -import io.servicecomb.samples.common.schema.Hello; -import io.servicecomb.samples.common.schema.models.Person; - -@RestSchema(schemaId = "springmvcHello") -@RequestMapping(path = "/springmvchello", produces = MediaType.APPLICATION_JSON) -public class SpringmvcHelloImpl implements Hello { - - @Override - @RequestMapping(path = "/sayhi", method = RequestMethod.POST) - public String sayHi(@RequestParam(name = "name") String name) { - return "Hello " + name; - } - - @Override - @RequestMapping(path = "/sayhello", method = RequestMethod.POST) - public String sayHello(@RequestBody Person person) { - return "Hello person " + person.getName(); - } -} diff --git a/samples/springmvc-sample/springmvc-provider/src/main/java/io/servicecomb/samples/springmvc/provider/SpringmvcProviderMain.java b/samples/springmvc-sample/springmvc-provider/src/main/java/io/servicecomb/samples/springmvc/provider/SpringmvcProviderMain.java deleted file mode 100644 index 83c5f92b95d..00000000000 --- a/samples/springmvc-sample/springmvc-provider/src/main/java/io/servicecomb/samples/springmvc/provider/SpringmvcProviderMain.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.samples.springmvc.provider; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class SpringmvcProviderMain { - - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - } -} diff --git a/samples/springmvc-sample/springmvc-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml b/samples/springmvc-sample/springmvc-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml deleted file mode 100644 index 2acd82877ea..00000000000 --- a/samples/springmvc-sample/springmvc-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/samples/springmvc-sample/springmvc-provider/src/main/resources/config/log4j.demo.properties b/samples/springmvc-sample/springmvc-provider/src/main/resources/config/log4j.demo.properties deleted file mode 100644 index 0324fbb1032..00000000000 --- a/samples/springmvc-sample/springmvc-provider/src/main/resources/config/log4j.demo.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=target/logs/ -paas.logs.file=sample.log - -log4j.rootLogger=INFO,paas,stdout diff --git a/samples/springmvc-sample/springmvc-provider/src/main/resources/microservice.yaml b/samples/springmvc-sample/springmvc-provider/src/main/resources/microservice.yaml deleted file mode 100644 index 4a4402053fc..00000000000 --- a/samples/springmvc-sample/springmvc-provider/src/main/resources/microservice.yaml +++ /dev/null @@ -1,12 +0,0 @@ -APPLICATION_ID: springmvc-sample -service_description: - name: springmvc - version: 0.0.1 -cse: - service: - registry: - address: http://127.0.0.1:30100 - rest: - address: 0.0.0.0:8080 - highway: - address: 0.0.0.0:7070 diff --git a/samples/springmvc-sample/springmvc-provider/src/main/resources/microservices/hello/hello.yaml b/samples/springmvc-sample/springmvc-provider/src/main/resources/microservices/hello/hello.yaml deleted file mode 100644 index be8ea88dda4..00000000000 --- a/samples/springmvc-sample/springmvc-provider/src/main/resources/microservices/hello/hello.yaml +++ /dev/null @@ -1,55 +0,0 @@ -swagger: '2.0' -info: - title: hello - version: 1.0.0 - x-java-interface: io.servicecomb.samples.springmvc.Hello -basePath: /pojo/rest/hello -produces: - - application/json - -paths: - /sayhi: - post: - operationId: sayHi - parameters: - - name: name - in: body - required: true - schema: - type: string - responses: - 200: - description: 正确返回 - schema: - type: string - default: - description: 默认返回 - schema: - type: string - /sayhello: - post: - operationId: sayHello - parameters: - - name: person - in: body - required: true - schema: - $ref: "#/definitions/Person" - responses: - 200: - description: 正确返回 - schema: - type: string - default: - description: 默认返回 - schema: - type: string -definitions: - Person: - type: "object" - properties: - name: - type: "string" - description: "person name" - xml: - name: "Person" \ No newline at end of file diff --git a/scripts/change_pom.py b/scripts/change_pom.py deleted file mode 100644 index 13285e428a5..00000000000 --- a/scripts/change_pom.py +++ /dev/null @@ -1,42 +0,0 @@ -import os - -try: - import xml.etree.cElementTree as ET -except: - import xml.etree.ElementTree as ET - -dir_path = os.path.dirname(os.path.realpath(__file__)) -project_dir = os.path.dirname(dir_path) -VERSION = os.getenv("VERSION") - - -def change_pom(pom_path): - print('changing' + pom_path) - tree = ET.parse(pom_path) - ET.register_namespace('', "http://maven.apache.org/POM/4.0.0") - root = tree.getroot() - POM_NS = "{http://maven.apache.org/POM/4.0.0}" - print 'replace first level vesion' - for child in tree.iterfind('%sversion' % (POM_NS)): - print 'change from ' + child.text + " to " + VERSION - child.text = VERSION - print 'replace second level vesion' - for child in tree.iterfind('%sparent' % (POM_NS)): - if child.find('%sgroupId' % POM_NS).text == "io.servicecomb": - if child.find('%sversion' % POM_NS) is not None: - print 'change module version from ' + child.find('%sversion' % POM_NS).text + " to " + VERSION - child.find('%sversion' % POM_NS).text = VERSION - print 'replace cse dependency version' - for child in tree.iter('%sdependency' % (POM_NS)): - if child.find('%sgroupId' % POM_NS).text == "io.servicecomb": - if child.find('%sversion' % POM_NS) is not None: - print 'change module version from ' + child.find('%sversion' % POM_NS).text + " to " + VERSION - child.find('%sversion' % POM_NS).text = VERSION - tree.write(pom_path) - - -if __name__ == "__main__": - for root, dirs, files in os.walk(project_dir): - for file in files: - if file.endswith("pom.xml"): - change_pom(os.path.join(root, file)) diff --git a/service-registry/pom.xml b/service-registry/pom.xml index 290c9d87cd4..252d89eb433 100644 --- a/service-registry/pom.xml +++ b/service-registry/pom.xml @@ -1,12 +1,13 @@ - - io.servicecomb - java-chassis-parent - 0.3.0-SNAPSHOT - ../parent - - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + org.apache.servicecomb + java-chassis-parent + 3.4.0-SNAPSHOT + ../parents/default + + 4.0.0 - service-registry + service-registry-parent + Java Chassis::Service Registry + pom - - - io.servicecomb - foundation-vertx - - - io.servicecomb - foundation-config - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + + registry-local + registry-service-center + registry-lightweight + registry-zero-config + registry-nacos + registry-zookeeper + registry-etcd + registry-consul + diff --git a/service-registry/registry-consul/pom.xml b/service-registry/registry-consul/pom.xml new file mode 100644 index 00000000000..380b9ea45fc --- /dev/null +++ b/service-registry/registry-consul/pom.xml @@ -0,0 +1,54 @@ + + + + + 4.0.0 + + org.apache.servicecomb + service-registry-parent + 3.4.0-SNAPSHOT + + + registry-consul + Java Chassis::Service Registry::Consul + + + + org.kiwiproject + consul-client + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + java-chassis-core + + + com.google.code.gson + gson + + + diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConfiguration.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConfiguration.java new file mode 100644 index 00000000000..36422f0f110 --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConfiguration.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.registry.consul; + +import com.google.common.net.HostAndPort; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.registry.RegistrationId; +import org.apache.servicecomb.registry.consul.config.ConsulDiscoveryProperties; +import org.apache.servicecomb.registry.consul.config.ConsulProperties; +import org.kiwiproject.consul.Consul; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +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; + +@ConditionalOnProperty(prefix = "servicecomb.registry.consul", name = "enabled", matchIfMissing = true) +@Configuration +public class ConsulConfiguration { + + @Bean + @ConfigurationProperties(prefix = ConsulConst.CONSUL_REGISTRY_PREFIX) + @ConditionalOnMissingBean + public ConsulProperties consulProperties() { + return new ConsulProperties(); + } + + @Bean + @ConfigurationProperties(prefix = ConsulConst.CONSUL_DISCOVERY_REGISTRY_PREFIX) + @ConditionalOnMissingBean + public ConsulDiscoveryProperties consulDiscoveryProperties() { + return new ConsulDiscoveryProperties(); + } + + @Bean + @ConditionalOnBean(value = {ConsulProperties.class, ConsulDiscoveryProperties.class}) + @ConditionalOnMissingBean + public Consul consulClient(ConsulProperties consulProperties, ConsulDiscoveryProperties consulDiscoveryProperties) { + Consul.Builder builder = Consul.builder() + .withHostAndPort(HostAndPort.fromParts(consulProperties.getHost(), consulProperties.getPort())); + if (StringUtils.isNotBlank(consulDiscoveryProperties.getAclToken())) { + builder.withAclToken(consulDiscoveryProperties.getAclToken()); + } + return builder.build(); + } + + @Bean + @ConditionalOnBean(value = {Consul.class, Environment.class, RegistrationId.class, DataCenterProperties.class}) + @ConditionalOnMissingBean + public ConsulDiscovery consulDiscovery() { + return new ConsulDiscovery(); + } + + @Bean + @ConditionalOnBean(value = {Consul.class, Environment.class, RegistrationId.class, DataCenterProperties.class}) + @ConditionalOnMissingBean + public ConsulRegistration consulRegistration() { + return new ConsulRegistration(); + } +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConst.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConst.java new file mode 100644 index 00000000000..7074c34a3e0 --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConst.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.registry.consul; + +public class ConsulConst { + + public static final String CONSUL_REGISTRY_NAME = "consul-registry"; + + public static final String CONSUL_REGISTRY_PREFIX = "servicecomb.registry.consul"; + + public static final String CONSUL_DISCOVERY_REGISTRY_PREFIX = "servicecomb.registry.consul.discovery"; + + public static final String CONSUL_DEFAULT_ENVIRONMENT = "production"; +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscovery.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscovery.java new file mode 100644 index 00000000000..ce16ccc47b3 --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscovery.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.consul; + +import com.google.common.collect.Lists; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotNull; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.registry.api.Discovery; +import org.apache.servicecomb.registry.consul.config.ConsulDiscoveryProperties; +import org.apache.servicecomb.registry.consul.config.ConsulProperties; +import org.kiwiproject.consul.Consul; +import org.kiwiproject.consul.HealthClient; +import org.kiwiproject.consul.cache.ServiceHealthCache; +import org.kiwiproject.consul.cache.ServiceHealthKey; +import org.kiwiproject.consul.model.health.Service; +import org.kiwiproject.consul.model.health.ServiceHealth; +import org.kiwiproject.consul.option.Options; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ConsulDiscovery implements Discovery { + + private static final Logger LOGGER = LoggerFactory.getLogger(ConsulDiscovery.class); + + @Resource + private ConsulProperties consulProperties; + + @Resource + private ConsulDiscoveryProperties consulDiscoveryProperties; + + @Resource + private Consul consulClient; + + @Resource + private Environment environment; + + private List consulDiscoveryInstanceList; + + private InstanceChangedListener instanceChangedListener; + + private ServiceHealthCache svHealth; + + @Override + public String name() { + return ConsulConst.CONSUL_REGISTRY_NAME; + } + + @Override + public boolean enabled(String application, String serviceName) { + return consulDiscoveryProperties.isEnabled(); + } + + @Override + public List findServiceInstances(String application, String serviceName) { + LOGGER.info("findServiceInstances application:{}, serviceName:{}", application, serviceName); + consulDiscoveryInstanceList = getInstances(serviceName); + return consulDiscoveryInstanceList; + } + + @Override + public List findServices(String application) { + LOGGER.info("ConsulDiscovery findServices(application={})", application); + Map> response = consulClient.catalogClient().getServices().getResponse(); + if (!CollectionUtils.isEmpty(response)) { + return Lists.newArrayList(response.keySet()); + } + return Lists.newArrayList(); + } + + @Override + public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { + this.instanceChangedListener = instanceChangedListener; + } + + @Override + public boolean enabled() { + return consulDiscoveryProperties.isEnabled(); + } + + @Override + public void init() { + LOGGER.info("ConsulDiscovery init"); + } + + @Override + public void run() { + LOGGER.info("ConsulDiscovery run"); + String serviceName = BootStrapProperties.readServiceName(environment); + HealthClient healthClient = consulClient.healthClient(); + svHealth = ServiceHealthCache.newCache(healthClient, serviceName, true, Options.BLANK_QUERY_OPTIONS, + consulDiscoveryProperties.getWatchSeconds()); + svHealth.addListener((Map newValues) -> instanceChangedListener.onInstanceChanged( + name(), BootStrapProperties.readApplication(environment), serviceName, getInstances(serviceName))); + svHealth.start(); + } + + @Override + public void destroy() { + if (svHealth != null) { + svHealth.stop(); + } + String serviceId = consulDiscoveryProperties.getServiceId(); + LOGGER.info("ConsulDiscovery destroy consul service id={}", serviceId); + if (consulClient != null) { + LOGGER.info("ConsulDiscovery consulClient destroy"); + consulClient.agentClient().deregister(serviceId); + consulClient.destroy(); + } + } + + public List getInstances(@NotNull final String serviceName) { + List instances = new ArrayList<>(); + addInstancesToList(instances, serviceName); + return instances; + } + + private void addInstancesToList(List instances, String serviceName) { + List healthServices = getHealthServices(serviceName); + if (!CollectionUtils.isEmpty(healthServices)) { + for (ServiceHealth serviceHealth : healthServices) { + Service service = serviceHealth.getService(); + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + ConsulInstance consulInstance = gson.fromJson(service.getMeta().get("meta"), ConsulInstance.class); + instances.add(new ConsulDiscoveryInstance(consulInstance)); + } + } + } + + private List getHealthServices(String serviceName) { + HealthClient healthClient = consulClient.healthClient(); + return healthClient.getHealthyServiceInstances(serviceName).getResponse(); + } +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscoveryInstance.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscoveryInstance.java new file mode 100644 index 00000000000..f07e4a40543 --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscoveryInstance.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.consul; + +import org.apache.servicecomb.registry.api.DiscoveryInstance; + +public class ConsulDiscoveryInstance extends ConsulInstance implements DiscoveryInstance { + + public ConsulDiscoveryInstance(ConsulInstance consulInstance) { + super(consulInstance); + } + + @Override + public String getRegistryName() { + return ConsulConst.CONSUL_REGISTRY_NAME; + } +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulInstance.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulInstance.java new file mode 100644 index 00000000000..722b3bbe28e --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulInstance.java @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.consul; + +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstance; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ConsulInstance implements MicroserviceInstance { + private String serviceId; + + private String instanceId; + + private String environment; + + private String application; + + private String serviceName; + + private String alias; + + private String version; + + private String description; + + private DataCenterInfo dataCenterInfo; + + private List endpoints = new ArrayList<>(); + + private Map schemas = new HashMap<>(); + + private Map properties = new HashMap<>(); + + private MicroserviceInstanceStatus status; + + public ConsulInstance() { + + } + + public ConsulInstance(ConsulInstance other) { + this.serviceId = other.serviceId; + this.instanceId = other.instanceId; + this.environment = other.environment; + this.application = other.application; + this.serviceName = other.serviceName; + this.alias = other.alias; + this.version = other.version; + this.description = other.description; + this.dataCenterInfo = other.dataCenterInfo; + this.endpoints = other.endpoints; + this.schemas = other.schemas; + this.properties = other.properties; + this.status = other.status; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public void setApplication(String application) { + this.application = application; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setDataCenterInfo(DataCenterInfo dataCenterInfo) { + this.dataCenterInfo = dataCenterInfo; + } + + public void setEndpoints(List endpoints) { + this.endpoints = endpoints; + } + + public void setSchemas(Map schemas) { + this.schemas = schemas; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public void setStatus(MicroserviceInstanceStatus status) { + this.status = status; + } + + @Override + public String getEnvironment() { + return this.environment; + } + + @Override + public String getApplication() { + return this.application; + } + + @Override + public String getServiceName() { + return this.serviceName; + } + + @Override + public String getAlias() { + return alias; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public DataCenterInfo getDataCenterInfo() { + return dataCenterInfo == null ? new DataCenterInfo() : dataCenterInfo; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public Map getProperties() { + return properties; + } + + @Override + public Map getSchemas() { + return schemas; + } + + @Override + public List getEndpoints() { + return endpoints; + } + + public void addSchema(String schemaId, String content) { + this.schemas.put(schemaId, content); + } + + public void addEndpoint(String endpoint) { + this.endpoints.add(endpoint); + } + + public void addProperty(String key, String value) { + this.properties.put(key, value); + } + + @Override + public String getInstanceId() { + return instanceId; + } + + @Override + public String getServiceId() { + return serviceId; + } + + @Override + public MicroserviceInstanceStatus getStatus() { + return this.status; + } +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistration.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistration.java new file mode 100644 index 00000000000..6458d250fe7 --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistration.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.registry.consul; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import jakarta.annotation.Resource; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.invocation.endpoint.EndpointUtils; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.registry.RegistrationId; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.Registration; +import org.apache.servicecomb.registry.consul.config.ConsulDiscoveryProperties; +import org.kiwiproject.consul.AgentClient; +import org.kiwiproject.consul.Consul; +import org.kiwiproject.consul.model.agent.ImmutableRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +public class ConsulRegistration implements Registration { + + private static final Logger LOGGER = LoggerFactory.getLogger(ConsulRegistration.class); + + private ConsulInstance consulInstance; + + @Resource + private ConsulDiscoveryProperties consulDiscoveryProperties; + + @Resource + private Consul consulClient; + + @Resource + private Environment environment; + + @Resource + private DataCenterProperties dataCenterProperties; + + @Resource + private RegistrationId registrationId; + + @Value("${servicecomb.rest.address:127.0.0.1:8080}") + private String restAddress; + + private ImmutableRegistration.Builder registrationBuilder; + + @Override + public String name() { + return ConsulConst.CONSUL_REGISTRY_NAME; + } + + @Override + public ConsulRegistrationInstance getMicroserviceInstance() { + return new ConsulRegistrationInstance(consulInstance); + } + + @Override + public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { + consulInstance.setStatus(status); + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + Map meta = new HashMap<>(); + meta.put("meta", gson.toJson(consulInstance)); + registrationBuilder.meta(meta); + ImmutableRegistration newService = registrationBuilder.build(); + AgentClient agentClient = consulClient.agentClient(); + agentClient.register(newService); + return true; + } + + @Override + public void addSchema(String schemaId, String content) { + if (consulDiscoveryProperties.isEnableSwaggerRegistration()) { + consulInstance.addSchema(schemaId, content); + } + } + + @Override + public void addEndpoint(String endpoint) { + consulInstance.addEndpoint(endpoint); + } + + @Override + public void addProperty(String key, String value) { + consulInstance.addProperty(key, value); + } + + @Override + public boolean enabled() { + return consulDiscoveryProperties.isEnabled(); + } + + @Override + public void init() { + LOGGER.info("ConsulRegistration init"); + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = ConsulConst.CONSUL_DEFAULT_ENVIRONMENT; + } + String instanceId = registrationId.getInstanceId(); + consulInstance = new ConsulInstance(); + consulInstance.setInstanceId(instanceId); + consulInstance.setEnvironment(env); + consulInstance.setApplication(BootStrapProperties.readApplication(environment)); + consulInstance.setServiceName(BootStrapProperties.readServiceName(environment)); + consulInstance.setAlias(BootStrapProperties.readServiceAlias(environment)); + consulInstance.setDescription(BootStrapProperties.readServiceDescription(environment)); + if (StringUtils.isNotEmpty(dataCenterProperties.getName())) { + DataCenterInfo dataCenterInfo = new DataCenterInfo(); + dataCenterInfo.setName(dataCenterProperties.getName()); + dataCenterInfo.setRegion(dataCenterProperties.getRegion()); + dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone()); + consulInstance.setDataCenterInfo(dataCenterInfo); + } + consulInstance.setProperties(BootStrapProperties.readServiceProperties(environment)); + consulInstance.setVersion(BootStrapProperties.readServiceVersion(environment)); + consulInstance.setStatus( + MicroserviceInstanceStatus.valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); + } + + @Override + public void run() { + LOGGER.info("ConsulRegistration run"); + registrationBuilder = ImmutableRegistration.builder() + .name(consulInstance.getServiceName()); + List endpoints = consulInstance.getEndpoints(); + for (String endpoint : endpoints) { + Endpoint temp = EndpointUtils.parse(endpoint); + if (temp.getAddress() instanceof URIEndpointObject) { + String hostOrIp = ((URIEndpointObject) temp.getAddress()).getHostOrIp(); + int port = ((URIEndpointObject) temp.getAddress()).getPort(); + String serviceId = hostOrIp + ":" + port; + consulDiscoveryProperties.setServiceId(serviceId); + consulInstance.setServiceId(serviceId); + registrationBuilder.id(serviceId); + registrationBuilder.address(hostOrIp); + registrationBuilder.port(port); + break; + } + } + List tags = consulDiscoveryProperties.getTags(); + if (CollectionUtils.isEmpty(tags)) { + tags.add(consulInstance.getServiceName()); + } + registrationBuilder.tags(tags); + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + Map meta = new HashMap<>(); + meta.put("meta", gson.toJson(consulInstance)); + registrationBuilder.meta(meta); + ImmutableRegistration newService = registrationBuilder.build(); + AgentClient agentClient = consulClient.agentClient(); + agentClient.register(newService); + LOGGER.info("ConsulRegistration newService"); + } + + @Override + public void destroy() { + LOGGER.info("ConsulRegistration destroy"); + } +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistrationInstance.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistrationInstance.java new file mode 100644 index 00000000000..dc0b83eaefc --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistrationInstance.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.registry.consul; + + +import org.apache.servicecomb.registry.api.RegistrationInstance; + +public class ConsulRegistrationInstance extends ConsulInstance implements RegistrationInstance { + + public ConsulRegistrationInstance(ConsulInstance instance) { + super(instance); + } +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulDiscoveryProperties.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulDiscoveryProperties.java new file mode 100644 index 00000000000..a6a23192eb4 --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulDiscoveryProperties.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.consul.config; + +import org.springframework.core.style.ToStringCreator; + +import java.util.ArrayList; +import java.util.List; + + +public class ConsulDiscoveryProperties { + + private String aclToken; + + private List tags = new ArrayList<>(); + + private Boolean enabled = true; + + private Integer watchSeconds = 8; + + private Boolean enableSwaggerRegistration = false; + + private String serviceId; + + public String getAclToken() { + return this.aclToken; + } + + public void setAclToken(String aclToken) { + this.aclToken = aclToken; + } + + public List getTags() { + return this.tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + public Boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public Boolean isEnableSwaggerRegistration() { + return enableSwaggerRegistration; + } + + public void setEnableSwaggerRegistration(Boolean enableSwaggerRegistration) { + this.enableSwaggerRegistration = enableSwaggerRegistration; + } + + public Integer getWatchSeconds() { + return watchSeconds; + } + + public void setWatchSeconds(Integer watchSeconds) { + this.watchSeconds = watchSeconds; + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + @Override + public String toString() { + return new ToStringCreator(this).append("aclToken", this.aclToken) + .append("enabled", this.enabled) + .append("serviceId", this.serviceId) + .append("tags", this.tags) + .append("watchSeconds", this.watchSeconds) + .toString(); + } +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulProperties.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulProperties.java new file mode 100644 index 00000000000..bb04a758330 --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulProperties.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.registry.consul.config; + +import jakarta.validation.constraints.NotNull; + +import org.springframework.validation.annotation.Validated; + + +@Validated +public class ConsulProperties { + + @NotNull + private String host = "localhost"; + + private String scheme = "http"; + + @NotNull + private Integer port = 8500; + + private Boolean enabled = true; + + public String getHost() { + return this.host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPort() { + return this.port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public Boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public String getScheme() { + return this.scheme; + } + + public void setScheme(String scheme) { + this.scheme = scheme; + } + + @Override + public String toString() { + return "ConsulProperties{" + "host='" + this.host + '\'' + ", port=" + this.port + ", scheme=" + this.scheme + + ", enabled=" + this.enabled + "}"; + } +} diff --git a/service-registry/registry-consul/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/service-registry/registry-consul/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..9a0422d67c2 --- /dev/null +++ b/service-registry/registry-consul/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.consul.ConsulConfiguration diff --git a/service-registry/registry-etcd/pom.xml b/service-registry/registry-etcd/pom.xml new file mode 100644 index 00000000000..10c037a03aa --- /dev/null +++ b/service-registry/registry-etcd/pom.xml @@ -0,0 +1,52 @@ + + + + + + org.apache.servicecomb + service-registry-parent + 3.4.0-SNAPSHOT + + 4.0.0 + + registry-etcd + Java Chassis::Service Registry::Etcd + + + + + io.etcd + jetcd-core + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + java-chassis-core + + + + diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConfiguration.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConfiguration.java new file mode 100644 index 00000000000..a926c7bbc11 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConfiguration.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.registry.etcd; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class EtcdConfiguration { + @Bean + @ConfigurationProperties(prefix = EtcdConst.ETCD_REGISTRY_PREFIX) + public EtcdRegistryProperties etcdRegistryProperties() { + return new EtcdRegistryProperties(); + } + + @Bean + public EtcdDiscovery etcdDiscovery() { + return new EtcdDiscovery(); + } + + @Bean + public EtcdRegistration etcdRegistration() { + return new EtcdRegistration(); + } +} diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConst.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConst.java new file mode 100644 index 00000000000..03b28f0d2ae --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConst.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.registry.etcd; + +public class EtcdConst { + + public static final String ETCD_REGISTRY_NAME = "etcd-registry"; + + public static final String ETCD_DISCOVERY_ROOT = "/servicecomb/registry/%s"; + + public static final String ETCD_REGISTRY_PREFIX = "servicecomb.registry.etcd"; + + public static final String ETCD_DISCOVERY_ENABLED = ETCD_REGISTRY_PREFIX + ".%s.%s.enabled"; + + public static final String ETCD_DEFAULT_ENVIRONMENT = "production"; +} diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscovery.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscovery.java new file mode 100644 index 00000000000..b43b9bdabd9 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscovery.java @@ -0,0 +1,221 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.etcd; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.utils.ConditionWaiter; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.apache.servicecomb.foundation.common.utils.MuteExceptionUtil; +import org.apache.servicecomb.registry.api.Discovery; +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.collect.Lists; + +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 EtcdDiscovery implements Discovery { + + private Environment environment; + + private String basePath; + + private EtcdRegistryProperties etcdRegistryProperties; + + private Client client; + + private InstanceChangedListener instanceChangedListener; + + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdDiscovery.class); + + private Map watchMap = new ConcurrentHashMapEx<>(); + + @Autowired + @SuppressWarnings("unused") + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + @SuppressWarnings("unused") + public void setEtcdRegistryProperties(EtcdRegistryProperties etcdRegistryProperties) { + this.etcdRegistryProperties = etcdRegistryProperties; + } + + @Override + public String name() { + return EtcdConst.ETCD_REGISTRY_NAME; + } + + @Override + public boolean enabled(String application, String serviceName) { + return environment.getProperty(String.format(EtcdConst.ETCD_DISCOVERY_ENABLED, application, serviceName), + boolean.class, true); + } + + @Override + public List findServiceInstances(String application, String serviceName) { + + String prefixPath = basePath + "/" + application + "/" + serviceName; + watchMap.computeIfAbsent(prefixPath, listenServiceName -> { + Watch watchClient = client.getWatchClient(); + try { + ByteSequence prefixByteSeq = ByteSequence.from(prefixPath, Charset.defaultCharset()); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> watchNode(application, serviceName, prefixPath)); + } catch (Exception e) { + LOGGER.error("Failed to add watch", e); + } + return watchClient; + }); + +// async get all instances,because sync is bad way in etcd. + ConditionWaiter> waiter = new ConditionWaiter<>(new ArrayList<>(), 50, + TimeUnit.MILLISECONDS); + waiter.executeTaskAsync(() -> { + CompletableFuture getFuture = client.getKVClient() + .get(ByteSequence.from(prefixPath, StandardCharsets.UTF_8), + GetOption.builder().withPrefix(ByteSequence.from(prefixPath, StandardCharsets.UTF_8)).build()); + GetResponse getResponse = getFuture.get(); + return convertServiceInstanceList(getResponse.getKvs()); + }); + return waiter.waitForCompletion(); + } + + private void watchNode(String application, String serviceName, String prefixPath) { + + CompletableFuture getFuture = client.getKVClient() + .get(ByteSequence.from(prefixPath, StandardCharsets.UTF_8), + GetOption.builder().withPrefix(ByteSequence.from(prefixPath, StandardCharsets.UTF_8)).build()); + getFuture.thenAcceptAsync(response -> { + List discoveryInstanceList = convertServiceInstanceList(response.getKvs()); + instanceChangedListener.onInstanceChanged(name(), application, serviceName, discoveryInstanceList); + }).exceptionally(e -> { + LOGGER.error("watchNode error", e); + return null; + }); + } + + 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 List convertServiceInstanceList(List keyValueList) { + + List list = Lists.newArrayListWithExpectedSize(keyValueList.size()); + for (KeyValue keyValue : keyValueList) { + EtcdDiscoveryInstance etcdDiscoveryInstance = getEtcdDiscoveryInstance(keyValue); + list.add(etcdDiscoveryInstance); + } + return list; + } + + private static EtcdDiscoveryInstance getEtcdDiscoveryInstance(KeyValue keyValue) { + String valueJson = new String(keyValue.getValue().getBytes(), Charset.defaultCharset()); + + EtcdInstance etcdInstance = MuteExceptionUtil.builder() + .withLog("convert json value to obj from etcd failure, {}", valueJson) + .executeFunctionWithDoubleParam(JsonUtils::readValue, valueJson.getBytes(StandardCharsets.UTF_8), + EtcdInstance.class); + return new EtcdDiscoveryInstance(etcdInstance); + } + + @Override + public List findServices(String application) { + + ConditionWaiter> waiter = new ConditionWaiter<>(new ArrayList<>(), 50, TimeUnit.MILLISECONDS); + waiter.executeTaskAsync(() -> { + String prefixPath = basePath + "/" + application; + List endpointKv = getValuesByPrefix(prefixPath); + return endpointKv.stream() + .map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)) + .map(key -> { + String[] parts = StringUtils.split(key, "/"); + return parts.length > 5 ? parts[4] : null; + }) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + }); + return waiter.waitForCompletion(); + } + + @Override + public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { + this.instanceChangedListener = instanceChangedListener; + } + + @Override + public boolean enabled() { + return etcdRegistryProperties.isEnabled(); + } + + @Override + public void init() { + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = EtcdConst.ETCD_DEFAULT_ENVIRONMENT; + } + basePath = String.format(EtcdConst.ETCD_DISCOVERY_ROOT, env); + } + + @Override + public void run() { + if (StringUtils.isEmpty(etcdRegistryProperties.getAuthenticationInfo())) { + this.client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()).build(); + } else { + String[] authInfo = etcdRegistryProperties.getAuthenticationInfo().split(":"); + this.client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()) + .user(ByteSequence.from(authInfo[0], Charset.defaultCharset())) + .password(ByteSequence.from(authInfo[1], Charset.defaultCharset())).build(); + } + } + + @Override + public void destroy() { + if (client != null) { + client.close(); + watchMap = null; + } + } +} diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscoveryInstance.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscoveryInstance.java new file mode 100644 index 00000000000..d75a3b04cf4 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscoveryInstance.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.registry.etcd; + +import org.apache.servicecomb.registry.api.DiscoveryInstance; +public class EtcdDiscoveryInstance extends EtcdInstance implements DiscoveryInstance { + + public EtcdDiscoveryInstance(EtcdInstance other) { + super(other); + } + + @Override + public String getRegistryName() { + return EtcdConst.ETCD_REGISTRY_NAME; + } +} diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdInstance.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdInstance.java new file mode 100644 index 00000000000..071e84bf3bc --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdInstance.java @@ -0,0 +1,221 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.etcd; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstance; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; + +public class EtcdInstance implements MicroserviceInstance { + private String serviceId; + + private String instanceId; + + private String environment; + + private String application; + + private String serviceName; + + private String alias; + + private String version; + + private String description; + + private DataCenterInfo dataCenterInfo; + + private List endpoints = new ArrayList<>(); + + private Map schemas = new HashMap<>(); + + private Map properties = new HashMap<>(); + + private MicroserviceInstanceStatus status; + + public EtcdInstance() { + + } + + public EtcdInstance(EtcdInstance other) { + this.serviceId = other.serviceId; + this.instanceId = other.instanceId; + this.environment = other.environment; + this.application = other.application; + this.serviceName = other.serviceName; + this.alias = other.alias; + this.version = other.version; + this.description = other.description; + this.dataCenterInfo = other.dataCenterInfo; + this.endpoints = other.endpoints; + this.schemas = other.schemas; + this.properties = other.properties; + this.status = other.status; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public void setApplication(String application) { + this.application = application; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setDataCenterInfo(DataCenterInfo dataCenterInfo) { + this.dataCenterInfo = dataCenterInfo; + } + + public void setEndpoints(List endpoints) { + this.endpoints = endpoints; + } + + public void setSchemas(Map schemas) { + this.schemas = schemas; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public void setStatus(MicroserviceInstanceStatus status) { + this.status = status; + } + + @Override + public String getEnvironment() { + return this.environment; + } + + @Override + public String getApplication() { + return this.application; + } + + @Override + public String getServiceName() { + return this.serviceName; + } + + @Override + public String getAlias() { + return alias; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public DataCenterInfo getDataCenterInfo() { + return dataCenterInfo == null ? new DataCenterInfo() : dataCenterInfo; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public Map getProperties() { + return properties; + } + + @Override + public Map getSchemas() { + return schemas; + } + + @Override + public List getEndpoints() { + return endpoints; + } + + public void addSchema(String schemaId, String content) { + this.schemas.put(schemaId, content); + } + + public void addEndpoint(String endpoint) { + this.endpoints.add(endpoint); + } + + public void addProperty(String key, String value) { + this.properties.put(key, value); + } + + @Override + public String getInstanceId() { + return instanceId; + } + + @Override + public String getServiceId() { + return serviceId; + } + + @Override + public MicroserviceInstanceStatus getStatus() { + return this.status; + } + + @Override + public String toString() { + return "EtcdInstance{" + + "serviceId='" + serviceId + '\'' + + ", instanceId='" + instanceId + '\'' + + ", environment='" + environment + '\'' + + ", application='" + application + '\'' + + ", serviceName='" + serviceName + '\'' + + ", alias='" + alias + '\'' + + ", version='" + version + '\'' + + ", description='" + description + '\'' + + ", dataCenterInfo=" + dataCenterInfo + + ", endpoints=" + endpoints + + ", schemas=" + schemas + + ", properties=" + properties + + '}'; + } +} diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistration.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistration.java new file mode 100644 index 00000000000..3fabfac4e23 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistration.java @@ -0,0 +1,207 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.etcd; + +import java.nio.charset.Charset; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.apache.servicecomb.foundation.common.utils.MuteExceptionUtil; +import org.apache.servicecomb.registry.RegistrationId; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.Registration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +import io.etcd.jetcd.ByteSequence; +import io.etcd.jetcd.Client; +import io.etcd.jetcd.KV; +import io.etcd.jetcd.Lease; +import io.etcd.jetcd.kv.PutResponse; +import io.etcd.jetcd.options.PutOption; + +public class EtcdRegistration implements Registration { + + private EtcdInstance etcdInstance; + + private Environment environment; + + private String basePath; + + private RegistrationId registrationId; + + private DataCenterProperties dataCenterProperties; + + private EtcdRegistryProperties etcdRegistryProperties; + + private Client client; + + private ScheduledExecutorService executorService; + + private String keyPath; + + private Long leaseId; + + @Autowired + @SuppressWarnings("unused") + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + @SuppressWarnings("unused") + public void setEtcdRegistryProperties(EtcdRegistryProperties etcdRegistryProperties) { + this.etcdRegistryProperties = etcdRegistryProperties; + } + + @Autowired + public void setRegistrationId(RegistrationId registrationId) { + this.registrationId = registrationId; + } + + @Autowired + @SuppressWarnings("unused") + public void setDataCenterProperties(DataCenterProperties dataCenterProperties) { + this.dataCenterProperties = dataCenterProperties; + } + + @Override + public String name() { + return EtcdConst.ETCD_REGISTRY_NAME; + } + + @Override + public EtcdRegistrationInstance getMicroserviceInstance() { + return new EtcdRegistrationInstance(etcdInstance); + } + + @Override + public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { + this.etcdInstance.setStatus(status); + String valueJson = MuteExceptionUtil.builder().withLog("to json, key:{}, value:{}", keyPath, etcdInstance) + .executeFunction(JsonUtils::writeValueAsString, etcdInstance); + register(ByteSequence.from(keyPath, Charset.defaultCharset()), + ByteSequence.from(valueJson, Charset.defaultCharset())); + return true; + } + + @Override + public void addSchema(String schemaId, String content) { + if (etcdRegistryProperties.isEnableSwaggerRegistration()) { + etcdInstance.addSchema(schemaId, content); + } + } + + @Override + public void addEndpoint(String endpoint) { + etcdInstance.addEndpoint(endpoint); + } + + @Override + public void addProperty(String key, String value) { + etcdInstance.addProperty(key, value); + } + + @Override + public boolean enabled() { + return etcdRegistryProperties.isEnabled(); + } + + @Override + public void init() { + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = EtcdConst.ETCD_DEFAULT_ENVIRONMENT; + } + basePath = String.format(EtcdConst.ETCD_DISCOVERY_ROOT, env); + etcdInstance = new EtcdInstance(); + etcdInstance.setInstanceId(registrationId.getInstanceId()); + etcdInstance.setEnvironment(env); + etcdInstance.setApplication(BootStrapProperties.readApplication(environment)); + etcdInstance.setServiceName(BootStrapProperties.readServiceName(environment)); + etcdInstance.setAlias(BootStrapProperties.readServiceAlias(environment)); + etcdInstance.setDescription(BootStrapProperties.readServiceDescription(environment)); + if (StringUtils.isNotEmpty(dataCenterProperties.getName())) { + DataCenterInfo dataCenterInfo = new DataCenterInfo(); + dataCenterInfo.setName(dataCenterProperties.getName()); + dataCenterInfo.setRegion(dataCenterProperties.getRegion()); + dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone()); + etcdInstance.setDataCenterInfo(dataCenterInfo); + } + etcdInstance.setProperties(BootStrapProperties.readServiceProperties(environment)); + etcdInstance.setVersion(BootStrapProperties.readServiceVersion(environment)); + etcdInstance.setStatus( + MicroserviceInstanceStatus.valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); + } + + @Override + public void run() { + client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()) + .build(); + keyPath = basePath + "/" + + BootStrapProperties.readApplication(environment) + "/" + + BootStrapProperties.readServiceName(environment) + "/" + + registrationId.getInstanceId(); + + String valueJson = MuteExceptionUtil.builder().withLog("to json, key:{}, value:{}", keyPath, etcdInstance) + .executeFunction(JsonUtils::writeValueAsString, etcdInstance); + register(ByteSequence.from(keyPath, Charset.defaultCharset()), + ByteSequence.from(valueJson, Charset.defaultCharset())); + } + + public void register(ByteSequence key, ByteSequence value) { + + Lease leaseClient = client.getLeaseClient(); + leaseId = MuteExceptionUtil.builder().withLog("get lease id, key:{}, value:{}", keyPath, etcdInstance) + .executeCompletableFuture(leaseClient.grant(60)).getID(); + KV kvClient = client.getKVClient(); + + PutOption putOption = PutOption.builder().withLeaseId(leaseId).build(); + CompletableFuture putResponse = kvClient.put(key, value, putOption); + putResponse.thenRun(() -> { + executorService = Executors.newSingleThreadScheduledExecutor(); + executorService.scheduleAtFixedRate( + () -> MuteExceptionUtil.builder().withLog("reRegister, {}, {}", keyPath, etcdInstance) + .executeFunction(leaseClient::keepAliveOnce, leaseId), 0, 5, TimeUnit.SECONDS); + }); + } + + private void unregister() { + // close job + executorService.shutdownNow(); + + // close etcd node + Lease leaseClient = client.getLeaseClient(); + leaseClient.revoke(leaseId); + client.getKVClient().delete(ByteSequence.from(keyPath, Charset.defaultCharset())); + } + + @Override + public void destroy() { + if (client != null) { + unregister(); + client.close(); + } + } +} diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistrationInstance.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistrationInstance.java new file mode 100644 index 00000000000..3dfa78c338e --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistrationInstance.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.etcd; + +import org.apache.servicecomb.registry.api.RegistrationInstance; + +public class EtcdRegistrationInstance extends EtcdInstance implements RegistrationInstance { + public EtcdRegistrationInstance(EtcdInstance instance) { + super(instance); + } +} diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistryProperties.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistryProperties.java new file mode 100644 index 00000000000..c4d61094bfc --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistryProperties.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.registry.etcd; + +public class EtcdRegistryProperties { + private boolean enabled = true; + + private boolean ephemeral = true; + + private String connectString = "http://127.0.0.1:2379"; + + private String authenticationSchema; + + private String authenticationInfo; + + private int connectionTimeoutMillis = 1000; + + private int sessionTimeoutMillis = 60000; + + private boolean enableSwaggerRegistration = false; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isEphemeral() { + return ephemeral; + } + + public void setEphemeral(boolean ephemeral) { + this.ephemeral = ephemeral; + } + + public String getConnectString() { + return connectString; + } + + public void setConnectString(String connectString) { + this.connectString = connectString; + } + + public int getConnectionTimeoutMillis() { + return connectionTimeoutMillis; + } + + public void setConnectionTimeoutMillis(int connectionTimeoutMillis) { + this.connectionTimeoutMillis = connectionTimeoutMillis; + } + + public int getSessionTimeoutMillis() { + return sessionTimeoutMillis; + } + + public void setSessionTimeoutMillis(int sessionTimeoutMillis) { + this.sessionTimeoutMillis = sessionTimeoutMillis; + } + + public boolean isEnableSwaggerRegistration() { + return enableSwaggerRegistration; + } + + public void setEnableSwaggerRegistration(boolean enableSwaggerRegistration) { + this.enableSwaggerRegistration = enableSwaggerRegistration; + } + + public String getAuthenticationSchema() { + return authenticationSchema; + } + + public void setAuthenticationSchema(String authenticationSchema) { + this.authenticationSchema = authenticationSchema; + } + + public String getAuthenticationInfo() { + return authenticationInfo; + } + + public void setAuthenticationInfo(String authenticationInfo) { + this.authenticationInfo = authenticationInfo; + } +} diff --git a/service-registry/registry-etcd/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/service-registry/registry-etcd/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..999ffb6b04b --- /dev/null +++ b/service-registry/registry-etcd/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.etcd.EtcdConfiguration diff --git a/service-registry/registry-lightweight/pom.xml b/service-registry/registry-lightweight/pom.xml new file mode 100644 index 00000000000..43a27c5cfe6 --- /dev/null +++ b/service-registry/registry-lightweight/pom.xml @@ -0,0 +1,52 @@ + + + + + + service-registry-parent + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + registry-lightweight + Java Chassis::Service Registry::lightweight + + + + org.apache.servicecomb + registry-local + + + org.apache.servicecomb + provider-jaxrs + + + org.apache.servicecomb + provider-pojo + + + + org.apache.servicecomb + foundation-test-scaffolding + test + + + diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightDiscovery.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightDiscovery.java new file mode 100644 index 00000000000..f52e8345534 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightDiscovery.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.registry.lightweight; + +import org.apache.servicecomb.registry.api.Discovery; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.registry.lightweight.store.Store; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +import com.google.common.eventbus.EventBus; + +public abstract class AbstractLightweightDiscovery implements Discovery { + public static final String ZERO_DISCOVERY_ENABLED = "servicecomb.registry.zero-config.%s.%s.enabled"; + + protected EventBus eventBus; + + protected Store store; + + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + public AbstractLightweightDiscovery setEventBus(EventBus eventBus) { + this.eventBus = eventBus; + return this; + } + + @Autowired + public AbstractLightweightDiscovery setStore(Store store) { + this.store = store; + return this; + } + + @Override + public void init() { + eventBus.register(this); + } + + @Override + public void destroy() { + } + + @Override + public boolean enabled(String application, String serviceName) { + return environment.getProperty(String.format(ZERO_DISCOVERY_ENABLED, application, serviceName), + boolean.class, true); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightRegistration.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightRegistration.java new file mode 100644 index 00000000000..06aa7c9b73f --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightRegistration.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.registry.lightweight; + +import java.io.IOException; +import java.time.Duration; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.Registration; +import org.apache.servicecomb.registry.api.RegistrationInstance; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import com.google.common.eventbus.EventBus; + +@SuppressWarnings("UnstableApiUsage") +public abstract class AbstractLightweightRegistration implements Registration { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLightweightRegistration.class); + + protected EventBus eventBus; + + protected StoreService storeService; + + protected Self self; + + @Autowired + public AbstractLightweightRegistration setEventBus(EventBus eventBus) { + this.eventBus = eventBus; + return this; + } + + @Autowired + public AbstractLightweightRegistration setStoreService(StoreService storeService) { + this.storeService = storeService; + return this; + } + + @Autowired + public AbstractLightweightRegistration setSelf(Self self) { + this.self = self; + return this; + } + + @Override + public void init() { + + } + + protected void startRegister(Duration interval) { + Executors + .newSingleThreadScheduledExecutor(runnable -> new Thread(runnable, name())) + .scheduleAtFixedRate(this::sendRegister, 0, interval.getSeconds(), TimeUnit.SECONDS); + } + + + protected void sendRegister() { + try { + doSendRegister(); + } catch (Exception e) { + LOGGER.error("register failed.", e); + } + } + + protected abstract void doSendRegister() throws IOException; + + @Override + public void addSchema(String schemaId, String content) { + self.addSchema(schemaId, content); + } + + @Override + public void addEndpoint(String endpoint) { + self.getInstance().getEndpoints().add(endpoint); + } + + @Override + public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { + return true; + } + + @Override + public void addProperty(String key, String value) { + + } + + @Override + public void run() { + storeService.registerSelf(self); + } + + @Override + public void destroy() { + try { + doSendUnregister(); + } catch (Exception e) { + LOGGER.error("unregister failed.", e); + } + } + + protected abstract void doSendUnregister() throws IOException; +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/DiscoveryClient.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/DiscoveryClient.java new file mode 100644 index 00000000000..3d6a769d821 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/DiscoveryClient.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lightweight; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.foundation.common.utils.AsyncUtils; +import org.apache.servicecomb.registry.lightweight.model.Microservice; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; + +import io.swagger.v3.oas.annotations.Operation; +import jakarta.ws.rs.GET; +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; + +@Path("/v1/discovery") +public interface DiscoveryClient { + @Path("/info") + @GET + @Operation(summary = "", operationId = "getInfo") + CompletableFuture getInfoAsync(Endpoint endpoint, @QueryParam("service-id") String serviceId); + + + default MicroserviceInfo getInfo(Endpoint endpoint, String serviceId) { + return AsyncUtils.toSync(getInfoAsync(endpoint, serviceId)); + } + + @Path("/microservice") + @GET + @Operation(summary = "", operationId = "getMicroservice") + CompletableFuture getMicroserviceAsync(Endpoint endpoint, @QueryParam("service-id") String serviceId); + + default Microservice getMicroservice(Endpoint endpoint, String serviceId) { + return AsyncUtils.toSync(getMicroserviceAsync(endpoint, serviceId)); + } + + @Path("/instance") + @GET + @Operation(summary = "", operationId = "getInstance") + CompletableFuture getInstanceAsync(Endpoint endpoint, + @QueryParam("service-id") String serviceId); + + default MicroserviceInstance getInstance(Endpoint endpoint, String serviceId) { + return AsyncUtils.toSync(getInstanceAsync(endpoint, serviceId)); + } + + @Path("/schemas/{schema-id}") + @GET + @Produces(MediaType.TEXT_PLAIN) + @Operation(summary = "", operationId = "getSchema") + CompletableFuture getSchemaAsync(Endpoint endpoint, @QueryParam("service-id") String serviceId, + @PathParam("schema-id") String schemaId); + + default String getSchema(Endpoint endpoint, String serviceId, String schemaId) { + return AsyncUtils.toSync(getSchemaAsync(endpoint, serviceId, schemaId)); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/DiscoveryEndpoint.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/DiscoveryEndpoint.java new file mode 100644 index 00000000000..a1e2d0ccb50 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/DiscoveryEndpoint.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lightweight; + +import static org.apache.servicecomb.registry.lightweight.DiscoveryEndpoint.SCHEMA_ID; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.registry.lightweight.model.Microservice; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; + +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.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@RestSchema(schemaId = SCHEMA_ID) +@Path("/v1/discovery") +public class DiscoveryEndpoint { + public static final String SCHEMA_ID = "scb-discovery"; + + private final Self self; + + public DiscoveryEndpoint(Self self) { + this.self = self; + } + + @Parameters( + { + @Parameter(name = "service-id", in = ParameterIn.QUERY, schema = @Schema(implementation = String.class), + description = "just make it possible to mock many instances by one real instance for performance test") + } + ) + @Path("/info") + @GET + public CompletableFuture getInfo() { + return CompletableFuture.completedFuture(self.getMicroserviceInfo()); + } + + @Parameters( + { + @Parameter(name = "service-id", in = ParameterIn.QUERY, schema = @Schema(implementation = String.class), + description = "just make it possible to mock many instances by one real instance for performance test") + } + ) + @Path("/microservice") + @GET + public CompletableFuture getMicroservice() { + return CompletableFuture.completedFuture(self.getMicroservice()); + } + + @Parameters( + { + @Parameter(name = "service-id", in = ParameterIn.QUERY, schema = @Schema(implementation = String.class), + description = "just make it possible to mock many instances by one real instance for performance test") + } + ) + @Path("/instance") + @GET + public CompletableFuture getInstance() { + return CompletableFuture.completedFuture(self.getInstance()); + } + + @Parameters( + { + @Parameter(name = "service-id", in = ParameterIn.QUERY, schema = @Schema(implementation = String.class), + description = "just make it possible to mock many instances by one real instance for performance test") + } + ) + @Path("/schemas/{schema-id}") + @GET + @Produces(MediaType.TEXT_PLAIN) + public CompletableFuture getSchema(@PathParam("schema-id") String schemaId) { + return CompletableFuture.completedFuture(self.getMicroservice().getSchemaMap().get(schemaId)); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/LightWeightRegistryConfiguration.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/LightWeightRegistryConfiguration.java new file mode 100644 index 00000000000..862356d0dba --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/LightWeightRegistryConfiguration.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.registry.lightweight; + +import java.util.Collections; +import java.util.List; + +import org.apache.servicecomb.config.BootStrapProperties; +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.apache.servicecomb.registry.lightweight.store.Store; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import com.google.common.eventbus.EventBus; + +@Configuration +@SuppressWarnings("unused") +public class LightWeightRegistryConfiguration { + + public static final String ZERO_CONFIG_DISCOVERY_CLIENT = "zero-config-discovery-client"; + + @Bean + public Store zeroConfigStore() { + return new Store(); + } + + @Bean + public MessageExecutor zeroConfigMessageExecutor(Self self, StoreService storeService) { + return new MessageExecutor(self, storeService); + } + + @Bean + public Self zeroConfigSelf() { + return new Self(); + } + + @Bean + public StoreService zeroConfigStoreService(EventBus eventBus, Store store, DiscoveryClient discoveryClient) { + return new StoreService(eventBus, store, discoveryClient); + } + + @Bean + public DiscoveryEndpoint zeroConfigDiscoveryEndpoint(Self self) { + return new DiscoveryEndpoint(self); + } + + @Bean + public RegistryBean zeroConfigDiscoveryServer(Environment environment) { + return new RegistryBean().setAppId(BootStrapProperties.readApplication(environment)) + .setServiceName(ZERO_CONFIG_DISCOVERY_CLIENT) + .addSchemaInterface(ZERO_CONFIG_DISCOVERY_CLIENT, DiscoveryClient.class) + // add an empty instance endpoint so that can invoke by endpoint + .setInstances(new Instances().setInstances(List.of(new Instance().setEndpoints(Collections.emptyList())))); + } + + @Bean + public DiscoveryClient zeroConfigDiscoveryClient() { + return Invoker.createProxy(ZERO_CONFIG_DISCOVERY_CLIENT, + ZERO_CONFIG_DISCOVERY_CLIENT, DiscoveryClient.class); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Message.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Message.java new file mode 100644 index 00000000000..eda811f2d48 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Message.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.lightweight; + +import java.io.IOException; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + +import io.vertx.core.json.jackson.DatabindCodec; + +public class Message { + public static Message of(MessageType type, T body) { + return new Message() + .setType(type) + .setBody(body); + } + + private MessageType type; + + @JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY) + @JsonSubTypes( + { + @Type(name = "REGISTER", value = RegisterRequest.class), + @Type(name = "UNREGISTER", value = UnregisterRequest.class) + } + ) + private T body; + + public MessageType getType() { + return type; + } + + public Message setType(MessageType type) { + this.type = type; + return this; + } + + public T getBody() { + return body; + } + + public Message setBody(T body) { + this.body = body; + return this; + } + + public byte[] encode() throws IOException { + return DatabindCodec.mapper().writeValueAsBytes(this); + } + + public static Message decode(byte[] bytes, int length) throws IOException { + return DatabindCodec.mapper() + .readValue(bytes, 0, length, Message.class); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/MessageExecutor.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/MessageExecutor.java new file mode 100644 index 00000000000..d2d550bf79a --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/MessageExecutor.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.registry.lightweight; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MessageExecutor { + private static final Logger LOGGER = LoggerFactory.getLogger(MessageExecutor.class); + + private final Self self; + + private final StoreService storeService; + + private final Map> messageProcessors = new HashMap<>(); + + private final ScheduledExecutorService taskExecutor = Executors + .newSingleThreadScheduledExecutor(runnable -> new Thread(runnable, "lightweight-message-executor")); + + public MessageExecutor(Self self, StoreService storeService) { + this.self = self; + this.storeService = storeService; + + addMessageProcessor(MessageType.REGISTER, this::register); + addMessageProcessor(MessageType.UNREGISTER, storeService::unregister); + } + + public void startCheckDeadInstances(Duration interval) { + taskExecutor.scheduleAtFixedRate( + () -> storeService.deleteDeadInstances(interval), + 0, interval.getSeconds(), TimeUnit.SECONDS); + } + + private void register(RegisterRequest request) { + if (request.isCrossApp() || Objects.equals(request.getAppId(), self.getAppId())) { + storeService.register(request); + } + } + + private void addMessageProcessor(MessageType messageType, Consumer messageProcessor) { + messageProcessors.put(messageType, messageProcessor); + } + + @SuppressWarnings("unchecked") + public void processMessage(Message message) { + Consumer consumer = (Consumer) messageProcessors.get(message.getType()); + + taskExecutor.execute(() -> { + try { + consumer.accept(message.getBody()); + } catch (Throwable e) { + LOGGER.error("process message error. ", e); + } + }); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/MessageType.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/MessageType.java new file mode 100644 index 00000000000..d3287ef00e8 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/MessageType.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.lightweight; + +public enum MessageType { + REGISTER, + UNREGISTER +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/MicroserviceInfo.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/MicroserviceInfo.java new file mode 100644 index 00000000000..92e605bae2e --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/MicroserviceInfo.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.lightweight; + +import java.util.Map; + +import org.apache.servicecomb.registry.lightweight.model.Microservice; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; + +public class MicroserviceInfo { + private Microservice microservice; + + private MicroserviceInstance instance; + + private Map schemasById; + + public Microservice getMicroservice() { + return microservice; + } + + public MicroserviceInfo setMicroservice(Microservice microservice) { + this.microservice = microservice; + return this; + } + + public Map getSchemasById() { + return schemasById; + } + + public MicroserviceInfo setSchemasById(Map schemasById) { + this.schemasById = schemasById; + return this; + } + + public MicroserviceInstance getInstance() { + return instance; + } + + public MicroserviceInfo setInstance(MicroserviceInstance instance) { + this.instance = instance; + return this; + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/RegisterException.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/RegisterException.java new file mode 100644 index 00000000000..68ed59393e0 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/RegisterException.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.registry.lightweight; + +public class RegisterException extends RuntimeException { + private static final long serialVersionUID = 4130899909889771251L; + + public RegisterException(String message) { + super(message); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/RegisterInstanceEvent.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/RegisterInstanceEvent.java new file mode 100644 index 00000000000..c8186037bd1 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/RegisterInstanceEvent.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.registry.lightweight; + +import org.apache.servicecomb.registry.lightweight.model.Microservice; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; + +/** + * post when a new instance registered
+ * to notify registration send a new register message
+ * currently, only "zero config" need this + */ +public class RegisterInstanceEvent { + private final Microservice microservice; + + private final MicroserviceInstance instance; + + public RegisterInstanceEvent(Microservice microservice, MicroserviceInstance instance) { + this.microservice = microservice; + this.instance = instance; + } + + public Microservice getMicroservice() { + return microservice; + } + + public MicroserviceInstance getInstance() { + return instance; + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/RegisterRequest.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/RegisterRequest.java new file mode 100644 index 00000000000..2fffe6da802 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/RegisterRequest.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lightweight; + +import java.util.List; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.invocation.endpoint.EndpointCacheUtils; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; + +public class RegisterRequest { + private String appId; + + private String serviceId; + + private boolean crossApp; + + private String schemasSummary; + + private String instanceId; + + private MicroserviceInstanceStatus status; + + private List endpoints; + + public String getAppId() { + return appId; + } + + public RegisterRequest setAppId(String appId) { + this.appId = appId; + return this; + } + + public String getServiceId() { + return serviceId; + } + + public RegisterRequest setServiceId(String serviceId) { + this.serviceId = serviceId; + return this; + } + + public boolean isCrossApp() { + return crossApp; + } + + public RegisterRequest setCrossApp(boolean crossApp) { + this.crossApp = crossApp; + return this; + } + + public String getSchemasSummary() { + return schemasSummary; + } + + public RegisterRequest setSchemasSummary(String schemasSummary) { + this.schemasSummary = schemasSummary; + return this; + } + + public String getInstanceId() { + return instanceId; + } + + public RegisterRequest setInstanceId(String instanceId) { + this.instanceId = instanceId; + return this; + } + + public MicroserviceInstanceStatus getStatus() { + return status; + } + + public RegisterRequest setStatus(MicroserviceInstanceStatus status) { + this.status = status; + return this; + } + + public List getEndpoints() { + return endpoints; + } + + public RegisterRequest setEndpoints(List endpoints) { + this.endpoints = endpoints; + return this; + } + + public Endpoint selectFirstEndpoint() { + return endpoints.stream() + .findFirst() + .map(EndpointCacheUtils::getOrCreate) + .orElse(null); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/SchemaChangedEvent.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/SchemaChangedEvent.java new file mode 100644 index 00000000000..9836448d889 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/SchemaChangedEvent.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.lightweight; + +import org.apache.servicecomb.registry.lightweight.model.Microservice; + +public class SchemaChangedEvent { + private final Microservice microservice; + + public SchemaChangedEvent(Microservice microservice) { + this.microservice = microservice; + } + + public Microservice getMicroservice() { + return microservice; + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Self.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Self.java new file mode 100644 index 00000000000..d6acccbf737 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Self.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lightweight; + +import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.registry.RegistrationId; +import org.apache.servicecomb.registry.lightweight.model.Microservice; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceFactory; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +import com.google.common.annotations.VisibleForTesting; + +public class Self implements InitializingBean, BootListener { + private Microservice microservice; + + // Whether to allow cross-app calls to me + private boolean crossApp; + + private MicroserviceInstance instance; + + private final MicroserviceInfo microserviceInfo = new MicroserviceInfo(); + + private Environment environment; + + private RegistrationId registrationId; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + public void setRegistrationId(RegistrationId registrationId) { + this.registrationId = registrationId; + } + + @Override + public void afterPropertiesSet() { + init(environment); + } + + @VisibleForTesting + public Self init(Environment environment) { + microservice = new MicroserviceFactory().create(environment); + microservice.serviceId(String.format("%s/%s/%s/%s", + microservice.getEnvironment(), + microservice.getAppId(), + microservice.getServiceName(), + microservice.getVersion())); + + instance = microservice.getInstance() + .instanceId(registrationId.getInstanceId()) + .serviceId(microservice.getServiceId()); + + microserviceInfo + .setMicroservice(microservice) + .setSchemasById(microservice.getSchemaMap()) + .setInstance(instance); + + return this; + } + + @Override + public int getOrder() { + return Integer.MAX_VALUE; + } + + @Override + public void onBeforeRegistry(BootEvent event) { + crossApp = microservice.allowCrossApp(); + } + + public Microservice getMicroservice() { + return microservice; + } + + public String getAppId() { + return microservice.getAppId(); + } + + public Microservice setServiceName(String serviceName) { + return microservice.serviceName(serviceName); + } + + public String getVersion() { + return microservice.getVersion(); + } + + public String getInstanceId() { + return instance.getInstanceId(); + } + + public String getServiceId() { + return instance.getServiceId(); + } + + public MicroserviceInstance getInstance() { + return instance; + } + + public MicroserviceInfo getMicroserviceInfo() { + return microserviceInfo; + } + + public Self addSchema(String schemaId, String content) { + microservice.addSchema(schemaId, content); + return this; + } + + public Self addEndpoint(String endpoint) { + instance.getEndpoints().add(endpoint); + return this; + } + + public RegisterRequest buildRegisterRequest() { + return createRegisterRequest() + .setAppId(microservice.getAppId()) + .setServiceId(microservice.getServiceId()) + .setCrossApp(crossApp) + .setInstanceId(instance.getInstanceId()) + .setStatus(instance.getStatus()) + .setEndpoints(instance.getEndpoints()); + } + + protected RegisterRequest createRegisterRequest() { + return new RegisterRequest(); + } + + public UnregisterRequest buildUnregisterRequest() { + return createUnregisterRequest() + .setServiceId(microservice.getServiceId()) + .setInstanceId(instance.getInstanceId()); + } + + protected UnregisterRequest createUnregisterRequest() { + return new UnregisterRequest(); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/StoreService.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/StoreService.java new file mode 100644 index 00000000000..851386844e0 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/StoreService.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lightweight; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.foundation.common.utils.AsyncUtils; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; +import org.apache.servicecomb.registry.lightweight.store.InstanceStore; +import org.apache.servicecomb.registry.lightweight.store.MicroserviceStore; +import org.apache.servicecomb.registry.lightweight.store.Store; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.EventBus; + +public class StoreService { + private static final Logger LOGGER = LoggerFactory.getLogger(StoreService.class); + + private final EventBus eventBus; + + private final Store store; + + private final DiscoveryClient discoveryClient; + + public StoreService(EventBus eventBus, Store store, DiscoveryClient discoveryClient) { + this.store = store; + this.discoveryClient = discoveryClient; + this.eventBus = eventBus; + } + + public void registerSelf(Self self) { + MicroserviceStore microserviceStore = store.addMicroservice(self.getMicroservice()); + addInstance(microserviceStore, self.getInstance()); + } + + public InstanceStore register(RegisterRequest request) { + return AsyncUtils.toSync(registerAsync(request)); + } + + public CompletableFuture registerAsync(RegisterRequest request) { + return doRegisterAsync(request) + .whenComplete((r, e) -> logFailedRegister(request, e)); + } + + private void logFailedRegister(RegisterRequest request, Throwable throwable) { + if (throwable == null) { + return; + } + + LOGGER.error("register instance failed, serviceId={}, instanceId={}, endpoints={}.", + request.getServiceId(), + request.getInstanceId(), + request.getEndpoints(), + throwable); + } + + private CompletableFuture doRegisterAsync(RegisterRequest request) { + InstanceStore instanceStore = store.findInstanceStore(request.getInstanceId()); + if (instanceStore == null) { + return addInstance(request); + } + + if (instanceStore.isStatusChanged(request.getStatus())) { + updateInstanceStatus(request, instanceStore); + } + return heartbeat(instanceStore); + } + + private CompletableFuture addInstance(RegisterRequest request) { + Endpoint endpoint = request.selectFirstEndpoint(); + if (endpoint == null) { + return AsyncUtils.completeExceptionally(new RegisterException("can not select endpoint")); + } + + MicroserviceStore microserviceStore = this.store.findMicroserviceStore(request.getServiceId()); + if (microserviceStore == null) { + return addMicroserviceAndInstance(endpoint, request); + } + + return CompletableFuture.completedFuture(null).thenCompose(v -> + discoveryClient.getInstanceAsync(endpoint, request.getServiceId())) + .thenApply(instance -> addInstance(microserviceStore, instance)); + } + + private CompletableFuture addMicroserviceAndInstance(Endpoint endpoint, RegisterRequest request) { + return discoveryClient.getInfoAsync(endpoint, request.getServiceId()) + .thenApply(info -> { + info.getMicroservice().getSchemaMap().putAll(info.getSchemasById()); + MicroserviceStore microserviceStore = store + .addMicroservice(info.getMicroservice()); + + LOGGER.info("add microservice and instance, serviceId={}, instanceId={}, endpoints={}", + request.getServiceId(), + request.getInstanceId(), + request.getEndpoints()); + return doAddInstance(microserviceStore, info.getInstance()); + }); + } + + private InstanceStore addInstance(MicroserviceStore microserviceStore, MicroserviceInstance instance) { + LOGGER.info("add instance, serviceId={}, instanceId={}, endpoints={}", + instance.getServiceId(), + instance.getInstanceId(), + instance.getEndpoints()); + + return doAddInstance(microserviceStore, instance); + } + + private InstanceStore doAddInstance(MicroserviceStore microserviceStore, MicroserviceInstance instance) { + InstanceStore instanceStore = store.addInstance(microserviceStore, instance); + eventBus.post(new RegisterInstanceEvent(microserviceStore.getMicroservice(), instance)); + return instanceStore; + } + + private void updateInstanceStatus(RegisterRequest request, InstanceStore instanceStore) { + LOGGER.info("update instance status, old status={}, new status={}, serviceId={}, instanceId={}, endpoints={}", + instanceStore.getStatus(), + request.getStatus(), + instanceStore.getServiceId(), + instanceStore.getInstanceId(), + instanceStore.getEndpoints()); + + store.findMicroserviceStore(instanceStore.getServiceId()) + .updateInstanceStatus(instanceStore, request.getStatus()); + } + + private CompletableFuture heartbeat(InstanceStore instanceStore) { + instanceStore.updateLastHeartBeat(); + + LOGGER.debug("instance heartbeat, serviceId={}, instanceId={}, endpoints={}", + instanceStore.getServiceId(), + instanceStore.getInstanceId(), + instanceStore.getEndpoints()); + + return CompletableFuture.completedFuture(instanceStore); + } + + public Void unregister(UnregisterRequest request) { + return AsyncUtils.toSync(unregisterAsync(request)); + } + + public CompletableFuture unregisterAsync(UnregisterRequest request) { + deleteInstance("unregister", request.getServiceId(), request.getInstanceId()); + return CompletableFuture.completedFuture(null); + } + + public void deleteInstance(String action, String serviceId, String instanceId) { + InstanceStore instanceStore = store.deleteInstance(serviceId, instanceId); + if (instanceStore == null) { + return; + } + + LOGGER.info("{} instance, serviceId={}, instanceId={}, endpoints={}", + action, + instanceStore.getServiceId(), + instanceStore.getInstanceId(), + instanceStore.getEndpoints()); + } + + public void deleteDeadInstances(Duration timeout) { + store.findDeadInstances(timeout) + .forEach(instance -> deleteInstance("delete dead", instance.getServiceId(), instance.getInstanceId())); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/UnregisterRequest.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/UnregisterRequest.java new file mode 100644 index 00000000000..33c9e561a50 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/UnregisterRequest.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.registry.lightweight; + +public class UnregisterRequest { + private String serviceId; + + private String instanceId; + + public String getServiceId() { + return serviceId; + } + + public UnregisterRequest setServiceId(String serviceId) { + this.serviceId = serviceId; + return this; + } + + public String getInstanceId() { + return instanceId; + } + + public UnregisterRequest setInstanceId(String instanceId) { + this.instanceId = instanceId; + return this; + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/AbstractPropertiesLoader.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/AbstractPropertiesLoader.java new file mode 100644 index 00000000000..5153e09a634 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/AbstractPropertiesLoader.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.registry.lightweight.model; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.registry.api.PropertyExtended; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +/** + * Loading microservice properties + */ +public abstract class AbstractPropertiesLoader { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractPropertiesLoader.class); + + public Map loadProperties(Environment environment) { + Map propertiesMap = new HashMap<>(); + loadPropertiesFromConfigMap(environment, propertiesMap); + loadPropertiesFromExtendedClass(environment, propertiesMap); + + return propertiesMap; + } + + protected abstract Map readProperties(Environment environment); + + protected abstract String readPropertiesExtendedClass(Environment environment); + + private void loadPropertiesFromConfigMap(Environment environment, Map propertiesMap) { + propertiesMap.putAll(readProperties(environment)); + } + + private void loadPropertiesFromExtendedClass(Environment environment, Map propertiesMap) { + String extendedPropertyClass = readPropertiesExtendedClass(environment); + + 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/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/FindInstancesResponse.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/FindInstancesResponse.java new file mode 100644 index 00000000000..61bc4fb9df3 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/FindInstancesResponse.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.lightweight.model; + +import java.util.List; + +public class FindInstancesResponse { + private List instances; + + public List getInstances() { + return instances; + } + + public FindInstancesResponse setInstances(List instances) { + this.instances = instances; + return this; + } + + public void mergeInstances(List instances) { + if (this.instances == null) { + this.instances = instances; + } else { + this.instances.addAll(instances); + } + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/HealthCheck.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/HealthCheck.java new file mode 100644 index 00000000000..b3f3d1ee47d --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/HealthCheck.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.registry.lightweight.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * Created by on 2017/1/13. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class HealthCheck { + private HealthCheckMode mode; + + private int port; + + private int interval; + + private int times; + + public int getInterval() { + return interval; + } + + public void setInterval(int interval) { + this.interval = interval; + } + + public int getTimes() { + return times; + } + + public void setTimes(int times) { + this.times = times; + } + + public HealthCheckMode getMode() { + return mode; + } + + public void setMode(HealthCheckMode mode) { + this.mode = mode; + } + + public int getTTL() { + if (this.mode != HealthCheckMode.HEARTBEAT) { + return 0; + } + return getInterval() * (getTimes() + 1); + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/HealthCheckMode.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/HealthCheckMode.java new file mode 100644 index 00000000000..d13d1e815b9 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/HealthCheckMode.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.registry.lightweight.model; + +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Created by on 2017/1/25. + */ +public enum HealthCheckMode { + UNKNOWN("unknown"), + HEARTBEAT("push"), + PLATFORM("pull"); + + private final String name; + + HealthCheckMode(String name) { + this.name = name; + } + + @JsonValue + public String getName() { + return name; + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/InstancePropertiesLoader.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/InstancePropertiesLoader.java new file mode 100644 index 00000000000..a4dd1d30d83 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/InstancePropertiesLoader.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.registry.lightweight.model; + +import java.util.Map; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.springframework.core.env.Environment; + +public final class InstancePropertiesLoader extends AbstractPropertiesLoader { + + public static final InstancePropertiesLoader INSTANCE = new InstancePropertiesLoader(); + + private InstancePropertiesLoader() { + } + + + @Override + protected Map readProperties(Environment environment) { + return BootStrapProperties.readServiceInstanceProperties(environment); + } + + @Override + protected String readPropertiesExtendedClass(Environment environment) { + return BootStrapProperties.readServiceInstanceExtendedClass(environment); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/Microservice.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/Microservice.java new file mode 100644 index 00000000000..3033ff60d49 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/Microservice.java @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lightweight.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.registry.definition.DefinitionConst; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * Created by on 2016/12/5. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Microservice { + // service center rule: max length: 64 + // two way to generate service id + // 1. microservice instance generate by some rule, ex: env/app/name/version + // and then register to service center with the id + // 2. register to service center with the id to be null, and then service center generate by UUID + private String serviceId; + + private String registerBy; + + private String environment; + + // service center rule: max length: 160 + private String appId; + + // service center rule: max length: 128 + private String serviceName; + + /** + * for governance + * when invoke cross app, if not use alias name, then {microservice}.{schema}.{operation} will conflict + */ + private String alias; + + private String version; + + private String description; + + private String level; + + private List schemas = new ArrayList<>(); + + @JsonIgnore + private final Map schemaMap = new HashMap<>(); + + + private Map properties = new HashMap<>(); + + @JsonIgnore + private MicroserviceInstance instance; + + /** + * Currently this field only exists in ServiceComb-Java-Chassis, + * and ServiceComb-Service-Center does not hold this field. + * Once the 3rd party services are supported to be registered into ServiceComb-Service-Center, + * the corresponding field should be added into Service-Center. + */ + private boolean thirdPartyService; + + public Microservice() { + } + + public MicroserviceInstance getInstance() { + return instance; + } + + public void setInstance(MicroserviceInstance instance) { + this.instance = instance; + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public Microservice serviceId(String serviceId) { + this.serviceId = serviceId; + return this; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public Microservice appId(String appId) { + this.appId = appId; + return this; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public Microservice serviceName(String serviceName) { + this.serviceName = serviceName; + return this; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public Microservice version(String version) { + this.version = version; + return this; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getLevel() { + return level; + } + + public void setLevel(String level) { + this.level = level; + } + + public List getSchemas() { + return schemas; + } + + public void setSchemas(List schemas) { + this.schemas = schemas; + } + + public void addSchema(String schemaId, String content) { + this.schemaMap.put(schemaId, content); + schemas.add(schemaId); + } + + public Map getSchemaMap() { + return schemaMap; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public static String generateAbsoluteMicroserviceName(String appId, String microserviceName) { + StringBuilder sb = new StringBuilder(appId.length() + microserviceName.length() + 1); + sb.append(appId).append(DefinitionConst.APP_SERVICE_SEPARATOR).append(microserviceName); + return sb.toString(); + } + + + public String getRegisterBy() { + return registerBy; + } + + public void setRegisterBy(String registerBy) { + this.registerBy = registerBy; + } + + public String getEnvironment() { + return environment; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public boolean isThirdPartyService() { + return thirdPartyService; + } + + public void setThirdPartyService(boolean thirdPartyService) { + this.thirdPartyService = thirdPartyService; + } + + // Whether to allow cross-app calls to me + public boolean allowCrossApp() { + return Boolean.parseBoolean(properties.get(DefinitionConst.CONFIG_ALLOW_CROSS_APP_KEY)); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroserviceFactory.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroserviceFactory.java new file mode 100644 index 00000000000..fed3b9a282a --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroserviceFactory.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.registry.lightweight.model; + +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.SERVICE_MAPPING; +import static org.apache.servicecomb.foundation.common.base.ServiceCombConstants.VERSION_MAPPING; + +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.foundation.common.Version; +import org.springframework.core.env.Environment; + +public class MicroserviceFactory { + public Microservice create(Environment environment) { + Microservice microservice = createMicroserviceFromConfiguration(environment); + microservice.setInstance(MicroserviceInstance.createFromDefinition(environment)); + return microservice; + } + + private Microservice createMicroserviceFromConfiguration(Environment environment) { + Microservice microservice = new Microservice(); + + if (!StringUtils.isEmpty(environment.getProperty(APP_MAPPING)) && + !StringUtils.isEmpty(environment.getProperty(environment.getProperty(APP_MAPPING)))) { + microservice.setAppId(environment.getProperty(environment.getProperty(APP_MAPPING))); + } else { + microservice.setAppId(BootStrapProperties.readApplication(environment)); + } + if (!StringUtils.isEmpty(environment.getProperty(SERVICE_MAPPING)) && + !StringUtils.isEmpty(environment.getProperty(environment.getProperty(SERVICE_MAPPING)))) { + microservice.setServiceName(environment.getProperty(environment.getProperty(SERVICE_MAPPING))); + } else { + microservice.setServiceName(BootStrapProperties.readServiceName(environment)); + } + String version; + if (!StringUtils.isEmpty(environment.getProperty(VERSION_MAPPING)) && + !StringUtils.isEmpty(environment.getProperty(environment.getProperty(VERSION_MAPPING)))) { + version = environment.getProperty(environment.getProperty(VERSION_MAPPING)); + } else { + version = BootStrapProperties.readServiceVersion(environment); + } + // just check version format + new Version(version); + microservice.setVersion(version); + + microservice.setDescription(BootStrapProperties.readServiceDescription(environment)); + microservice.setLevel(BootStrapProperties.readServiceRole(environment)); + Map propertiesMap = MicroservicePropertiesLoader.INSTANCE.loadProperties(environment); + microservice.setProperties(propertiesMap); + microservice.setEnvironment(BootStrapProperties.readServiceEnvironment(environment)); + + // set alias name when allow cross app + if (microservice.allowCrossApp()) { + microservice.setAlias(Microservice.generateAbsoluteMicroserviceName(microservice.getAppId(), + microservice.getServiceName())); + } + + microservice.setRegisterBy(CONFIG_DEFAULT_REGISTER_BY); + + return microservice; + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroserviceInstance.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroserviceInstance.java new file mode 100644 index 00000000000..2d919281737 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroserviceInstance.java @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lightweight.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.springframework.core.env.Environment; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * 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(Environment environment) { + MicroserviceInstance microserviceInstance = new MicroserviceInstance(); + // default hard coded values + microserviceInstance.setStage(DefinitionConst.DEFAULT_STAGE); + microserviceInstance.setStatus(MicroserviceInstanceStatus + .valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); + HealthCheck healthCheck = new HealthCheck(); + healthCheck.setMode(HealthCheckMode.HEARTBEAT); + microserviceInstance.setHealthCheck(healthCheck); + + // load properties + Map propertiesMap = InstancePropertiesLoader.INSTANCE.loadProperties(environment); + microserviceInstance.setProperties(propertiesMap); + + // load data center information + loadDataCenterInfo(environment, microserviceInstance); + return microserviceInstance; + } + + private static void loadDataCenterInfo(Environment environment, MicroserviceInstance microserviceInstance) { + String dataCenterName = environment.getProperty("servicecomb.datacenter.name"); + if (StringUtils.isEmpty(dataCenterName)) { + return; + } + String region = environment.getProperty("servicecomb.datacenter.region"); + String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); + DataCenterInfo dataCenterInfo = new DataCenterInfo(); + dataCenterInfo.setName(dataCenterName); + dataCenterInfo.setRegion(region); + dataCenterInfo.setAvailableZone(availableZone); + microserviceInstance.setDataCenterInfo(dataCenterInfo); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroserviceInstances.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroserviceInstances.java new file mode 100644 index 00000000000..24fd45b5ec2 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroserviceInstances.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.lightweight.model; + +import java.nio.charset.StandardCharsets; + +import javax.crypto.Mac; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.digest.HmacAlgorithms; +import org.apache.commons.codec.digest.HmacUtils; +import org.apache.commons.lang3.StringUtils; + +public class MicroserviceInstances { + private boolean microserviceNotExist; + + private boolean needRefresh = true; + + private String revision; + + private FindInstancesResponse instancesResponse; + + public boolean isMicroserviceNotExist() { + return microserviceNotExist; + } + + public MicroserviceInstances setMicroserviceNotExist(boolean microserviceNotExist) { + this.microserviceNotExist = microserviceNotExist; + return this; + } + + public boolean isNeedRefresh() { + return needRefresh; + } + + public MicroserviceInstances setNeedRefresh(boolean needRefresh) { + this.needRefresh = needRefresh; + return this; + } + + public String getRevision() { + return revision; + } + + public MicroserviceInstances setRevision(String revision) { + this.revision = revision; + return this; + } + + public FindInstancesResponse getInstancesResponse() { + return instancesResponse; + } + + public MicroserviceInstances setInstancesResponse(FindInstancesResponse instancesResponse) { + this.instancesResponse = instancesResponse; + return this; + } + + public void mergeMicroserviceInstances(MicroserviceInstances other) { + mergeNeedRefresh(other.needRefresh); + mergeMicroserviceNotExist(other.microserviceNotExist); + mergeRevision(other); + mergeInstanceResponse(other.getInstancesResponse()); + } + + private void mergeRevision(MicroserviceInstances other) { + if (!other.isMicroserviceNotExist() && other.needRefresh) { + Mac mac = HmacUtils.getInitializedMac(HmacAlgorithms.HMAC_SHA_1, stringToBytes(this.revision)); + this.revision = Base64.encodeBase64String(mac.doFinal(stringToBytes(other.revision))); + } + } + + private byte[] stringToBytes(String input) { + if (StringUtils.isEmpty(input)) { + input = "@"; + } + return input.getBytes(StandardCharsets.UTF_8); + } + + private void mergeMicroserviceNotExist(boolean microserviceNotExist) { + // only is all not exists, set to not exits. + if (this.microserviceNotExist) { + this.microserviceNotExist = microserviceNotExist; + } + } + + private void mergeNeedRefresh(boolean needRefresh) { + // if one of discovery need refresh, all need refresh + if (!this.needRefresh) { + this.needRefresh = needRefresh; + } + } + + private void mergeInstanceResponse(FindInstancesResponse instancesResponse) { + if (instancesResponse == null) { + return; + } + + if (this.instancesResponse == null) { + this.instancesResponse = instancesResponse; + return; + } + + this.instancesResponse.mergeInstances(instancesResponse.getInstances()); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroservicePropertiesLoader.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroservicePropertiesLoader.java new file mode 100644 index 00000000000..ed6d181fb29 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/model/MicroservicePropertiesLoader.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.lightweight.model; + +import java.util.Map; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.springframework.core.env.Environment; + +public final class MicroservicePropertiesLoader extends AbstractPropertiesLoader { + + public static final MicroservicePropertiesLoader INSTANCE = new MicroservicePropertiesLoader(); + + private MicroservicePropertiesLoader() { + } + + @Override + protected Map readProperties(Environment environment) { + return BootStrapProperties.readServiceProperties(environment); + } + + @Override + protected String readPropertiesExtendedClass(Environment environment) { + return BootStrapProperties.readServiceExtendedClass(environment); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/AppStore.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/AppStore.java new file mode 100644 index 00000000000..a068c10cb34 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/AppStore.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.lightweight.store; + +import java.util.Map; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstances; + +public class AppStore { + private final Map microserviceByName = new ConcurrentHashMapEx<>(); + + public void addMicroservice(MicroserviceStore microserviceStore) { + microserviceByName.put(microserviceStore.getServiceName(), microserviceStore); + } + + public MicroserviceInstances findServiceInstances(String serviceName, String revision) { + MicroserviceStore microserviceStore = microserviceByName.get(serviceName); + if (microserviceStore == null) { + return new MicroserviceInstances() + .setMicroserviceNotExist(true); + } + + return microserviceStore.findServiceInstances(revision); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/InstanceStore.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/InstanceStore.java new file mode 100644 index 00000000000..6c2c96f5366 --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/InstanceStore.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.registry.lightweight.store; + +import java.util.List; +import java.util.Objects; + +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; + +import com.google.common.base.Ticker; + +public class InstanceStore { + private final Ticker ticker; + + private MicroserviceInstance instance; + + private long lastHeartBeat; + + public InstanceStore(Ticker ticker, MicroserviceInstance instance) { + this.ticker = ticker; + this.instance = instance; + + this.updateLastHeartBeat(); + } + + public boolean isStatusChanged(MicroserviceInstanceStatus status) { + return !Objects.equals(instance.getStatus(), status); + } + + public MicroserviceInstance getInstance() { + return instance; + } + + public InstanceStore setInstance(MicroserviceInstance instance) { + this.instance = instance; + return this; + } + + public String getInstanceId() { + return getInstance().getInstanceId(); + } + + public String getServiceId() { + return getInstance().getServiceId(); + } + + public List getEndpoints() { + return getInstance().getEndpoints(); + } + + public MicroserviceInstanceStatus getStatus() { + return instance.getStatus(); + } + + public void setStatus(MicroserviceInstanceStatus status) { + instance.setStatus(status); + } + + public void updateLastHeartBeat() { + this.lastHeartBeat = ticker.read(); + } + + public long getLastHeartBeat() { + return lastHeartBeat; + } + + public boolean isHeartBeatTimeout(long nanoNow, long nanoTimeout) { + return (nanoNow - lastHeartBeat) > nanoTimeout; + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/MicroserviceStore.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/MicroserviceStore.java new file mode 100644 index 00000000000..aa58371ec8c --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/MicroserviceStore.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lightweight.store; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.lightweight.model.FindInstancesResponse; +import org.apache.servicecomb.registry.lightweight.model.Microservice; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstances; + +import com.google.common.base.Ticker; + +public class MicroserviceStore { + private final Ticker ticker; + + private final Microservice microservice; + + private final Map instancesById = new ConcurrentHashMapEx<>(); + + private String instancesRevision; + + public MicroserviceStore(Ticker ticker, Microservice microservice) { + this.ticker = ticker; + this.microservice = microservice; + + updateInstancesRevision(); + } + + public void updateInstancesRevision() { + this.instancesRevision = String.valueOf(ticker.read()); + } + + public String getServiceName() { + return microservice.getServiceName(); + } + + public String getServiceId() { + return microservice.getServiceId(); + } + + public Microservice getMicroservice() { + return microservice; + } + + public InstanceStore addInstance(MicroserviceInstance instance) { + InstanceStore instanceStore = new InstanceStore(ticker, instance); + + instancesById.put(instance.getInstanceId(), instanceStore); + + updateInstancesRevision(); + return instanceStore; + } + + public InstanceStore deleteInstance(String instanceId) { + InstanceStore instanceStore = instancesById.remove(instanceId); + if (instanceStore != null) { + updateInstancesRevision(); + } + return instanceStore; + } + + public void updateInstanceStatus(InstanceStore instanceStore, MicroserviceInstanceStatus status) { + instanceStore.setStatus(status); + updateInstancesRevision(); + } + + public MicroserviceInstances findServiceInstances(String revision) { + if (instancesRevision.equals(revision)) { + return new MicroserviceInstances() + .setRevision(instancesRevision) + .setNeedRefresh(false); + } + + List instances = instancesById.values().stream() + .map(InstanceStore::getInstance) + .collect(Collectors.toList()); + FindInstancesResponse response = new FindInstancesResponse() + .setInstances(instances); + return new MicroserviceInstances() + .setRevision(instancesRevision) + .setInstancesResponse(response); + } + + + public boolean hasInstance() { + return !instancesById.isEmpty(); + } + + public int getInstanceCount() { + return instancesById.size(); + } +} diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/Store.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/Store.java new file mode 100644 index 00000000000..c2d31a579cb --- /dev/null +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/store/Store.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lightweight.store; + +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.registry.lightweight.model.Microservice; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstances; + +import com.google.common.base.Ticker; + +public class Store { + private Ticker ticker = Ticker.systemTicker(); + + private final Map appsById = new ConcurrentHashMapEx<>(); + + private final Map microservicesByName = new ConcurrentHashMapEx<>(); + + private final Map microservicesById = new ConcurrentHashMapEx<>(); + + private final Map instancesById = new ConcurrentHashMapEx<>(); + + public Store setTicker(Ticker ticker) { + this.ticker = ticker; + return this; + } + + public AppStore getOrCreateAppStore(String appId) { + return appsById.computeIfAbsent(appId, key -> new AppStore()); + } + + public MicroserviceStore findMicroserviceStore(String serviceId) { + return microservicesById.get(serviceId); + } + + public InstanceStore findInstanceStore(String instanceId) { + return instancesById.get(instanceId); + } + + public MicroserviceStore addMicroservice(Microservice microservice) { + MicroserviceStore microserviceStore = new MicroserviceStore(ticker, microservice); + + getOrCreateAppStore(microservice.getAppId()) + .addMicroservice(microserviceStore); + microservicesById.put(microservice.getServiceId(), microserviceStore); + microservicesByName.put(microservice.getServiceName(), microserviceStore); + + return microserviceStore; + } + + public InstanceStore addInstance(MicroserviceStore microserviceStore, MicroserviceInstance instance) { + InstanceStore instanceStore = microserviceStore.addInstance(instance); + instancesById.put(instance.getInstanceId(), instanceStore); + return instanceStore; + } + + public InstanceStore deleteInstance(String serviceId, String instanceId) { + MicroserviceStore microserviceStore = findMicroserviceStore(serviceId); + if (microserviceStore == null) { + return null; + } + + InstanceStore instanceStore = microserviceStore.deleteInstance(instanceId); + if (instanceStore != null) { + instancesById.remove(instanceId); + } + return instanceStore; + } + + public Optional getMicroservice(String microserviceId) { + return Optional.ofNullable(microservicesById.get(microserviceId)) + .map(MicroserviceStore::getMicroservice); + } + + public Optional getMicroserviceInstance(String instanceId) { + return Optional.ofNullable(instancesById.get(instanceId)) + .map(InstanceStore::getInstance); + } + + public MicroserviceInstances findServiceInstances(String appId, String serviceName, String revision) { + return Optional.ofNullable(appsById.get(appId)) + .map(appStore -> appStore.findServiceInstances(serviceName, revision)) + .orElse(new MicroserviceInstances() + .setMicroserviceNotExist(true)); + } + + public List getAllMicroservices() { + return microservicesByName.values().stream() + .map(MicroserviceStore::getMicroservice) + .collect(Collectors.toList()); + } + + public int getMicroserviceCount() { + return microservicesByName.size(); + } + + public int getInstanceCount() { + return instancesById.size(); + } + + public Stream findDeadInstances(Duration timeout) { + long nanoNow = ticker.read(); + long nanoTimeout = timeout.toNanos(); + return instancesById.values().stream() + .filter(instanceStore -> instanceStore.isHeartBeatTimeout(nanoNow, nanoTimeout)) + .map(InstanceStore::getInstance); + } +} diff --git a/service-registry/registry-lightweight/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/service-registry/registry-lightweight/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..793c96dc8e5 --- /dev/null +++ b/service-registry/registry-lightweight/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.lightweight.LightWeightRegistryConfiguration diff --git a/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/config/TestAbstractPropertiesLoader.java b/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/config/TestAbstractPropertiesLoader.java new file mode 100644 index 00000000000..4e3ceafdc99 --- /dev/null +++ b/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/config/TestAbstractPropertiesLoader.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.registry.config; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.registry.lightweight.model.AbstractPropertiesLoader; +import org.apache.servicecomb.registry.lightweight.model.MicroservicePropertiesLoader; +import org.junit.jupiter.api.Assertions; +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 TestAbstractPropertiesLoader { + @Test + public void testExtendedClassCompatible() { + ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + EnumerablePropertySource propertySource = Mockito.mock(EnumerablePropertySource.class); + mutablePropertySources.addLast(propertySource); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); + Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] {}); + + Mockito.when(environment.getProperty(BootStrapProperties.CONFIG_SERVICE_EXTENDED_CLASS)) + .thenReturn("invalidClass"); + AbstractPropertiesLoader loader = MicroservicePropertiesLoader.INSTANCE; + try { + loader.loadProperties(environment); + Assertions.fail("Must throw exception"); + } catch (Error e) { + Assertions.assertEquals(ClassNotFoundException.class, e.getCause().getClass()); + Assertions.assertEquals("invalidClass", e.getCause().getMessage()); + } + } +} diff --git a/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/MessageTest.java b/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/MessageTest.java new file mode 100644 index 00000000000..0e79f3296c3 --- /dev/null +++ b/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/MessageTest.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.lightweight; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +import io.vertx.core.json.Json; +import org.skyscreamer.jsonassert.JSONAssert; + +class MessageTest { + private String toLinuxPrettyJson(Object value) { + return Json.encodePrettily(value) + .replaceAll("\r\n", "\n"); + } + + @Test + void should_encode_register_type() { + Message msg = Message.of(MessageType.REGISTER, new RegisterRequest()); + + try { + JSONAssert.assertEquals(toLinuxPrettyJson(msg), "" + + "{\n" + + " \"type\" : \"REGISTER\",\n" + + " \"body\" : {\n" + + " \"appId\" : null,\n" + + " \"serviceId\" : null,\n" + + " \"crossApp\" : false,\n" + + " \"schemasSummary\" : null,\n" + + " \"instanceId\" : null,\n" + + " \"status\" : null,\n" + + " \"endpoints\" : null\n" + + " }\n" + + "}", false); + } catch (Exception e) { + fail("Failed to compare JSONs: " + e.getMessage(), e); + } + + } + + @Test + void should_decode_register_type() { + String json = Json.encode(Message.of(MessageType.REGISTER, new RegisterRequest())); + Message message = Json.decodeValue(json, Message.class); + + assertThat(message.getBody()).isInstanceOf(RegisterRequest.class); + } + + @Test + void should_encode_unregister_type() { + Message msg = Message.of(MessageType.UNREGISTER, new UnregisterRequest()); + + try { + JSONAssert.assertEquals(toLinuxPrettyJson(msg), "" + + "{\n" + + " \"type\" : \"UNREGISTER\",\n" + + " \"body\" : {\n" + + " \"serviceId\" : null,\n" + + " \"instanceId\" : null\n" + + " }\n" + + "}", false); + } catch (Exception e) { + fail("Failed to compare JSONs: " + e.getMessage(), e); + } + } + + @Test + void should_decode_unregister_type() { + String json = Json.encode(Message.of(MessageType.UNREGISTER, new UnregisterRequest())); + Message message = Json.decodeValue(json, Message.class); + + assertThat(message.getBody()).isInstanceOf(UnregisterRequest.class); + } +} diff --git a/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/StoreServiceTest.java b/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/StoreServiceTest.java new file mode 100644 index 00000000000..efaf9665dc5 --- /dev/null +++ b/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/StoreServiceTest.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.lightweight; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.foundation.test.scaffolding.time.MockTicker; +import org.apache.servicecomb.registry.RegistrationId; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.lightweight.model.Microservice; +import org.apache.servicecomb.registry.lightweight.store.InstanceStore; +import org.apache.servicecomb.registry.lightweight.store.Store; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; +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.eventbus.EventBus; + +import io.vertx.core.json.Json; + +public class StoreServiceTest { + static Endpoint endpoint = Mockito.mock(Endpoint.class); + + static class MockRegisterRequest extends RegisterRequest { + @Override + public Endpoint selectFirstEndpoint() { + return endpoint; + } + } + + static ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); + + Self self; + + EventBus eventBus = new EventBus(); + + MockTicker ticker = new MockTicker(); + + Store store = new Store().setTicker(ticker); + + DiscoveryClient discoveryClient = Mockito.mock(DiscoveryClient.class); + + StoreService service = new StoreService(eventBus, store, discoveryClient); + + @BeforeEach + void setUp() { + MutablePropertySources mutablePropertySources = new MutablePropertySources(); + EnumerablePropertySource propertySource = Mockito.mock(EnumerablePropertySource.class); + mutablePropertySources.addLast(propertySource); + Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); + Mockito.when(propertySource.getPropertyNames()).thenReturn(new String[] {}); + Mockito.when(environment.getProperty("servicecomb.service.application")).thenReturn("app"); + Mockito.when(environment.getProperty("servicecomb.service.name")).thenReturn("svc"); + Mockito.when(environment.getProperty("servicecomb.service.version")).thenReturn("1.0.0.0"); + Mockito.when(environment.getProperty("servicecomb.instance.initialStatus")).thenReturn("UP"); + + self = new Self() { + @Override + protected RegisterRequest createRegisterRequest() { + return new MockRegisterRequest(); + } + }; + self.setRegistrationId(new RegistrationId()); + self.init(environment) + .addSchema("schema-1", "s1") + .addEndpoint("rest://1.1.1.1:80"); + + Mockito.when(discoveryClient.getInfoAsync(ArgumentMatchers.any(), ArgumentMatchers.any())) + .thenReturn(CompletableFuture.completedFuture(self.getMicroserviceInfo())); + Mockito.when(discoveryClient.getInstanceAsync(ArgumentMatchers.any(), ArgumentMatchers.any())) + .thenReturn(CompletableFuture.completedFuture(self.getInstance())); + } + + @Test + void should_register_microservice_and_instance_when_both_not_exist() { + RegisterRequest request = self.buildRegisterRequest(); + InstanceStore instanceStore = service.register(request); + + assertThat(store.findMicroserviceStore(self.getServiceId()).getMicroservice()) + .isSameAs(self.getMicroservice()); + assertThat(instanceStore.getInstance()).isSameAs(self.getInstance()); + assertThat(self.getInstance().getStatus()).isEqualTo(MicroserviceInstanceStatus.UP); + } + + @Test + void should_register_instance_when_microservice_exist() { + Microservice microservice = Json.decodeValue(Json.encode(self.getMicroservice()), Microservice.class); + store.addMicroservice(microservice); + + RegisterRequest request = self.buildRegisterRequest(); + InstanceStore instanceStore = service.register(request); + + assertThat(microservice).isNotSameAs(self.getMicroservice()); + assertThat(store.findMicroserviceStore(self.getServiceId()).getMicroservice()) + .isSameAs(microservice); + assertThat(instanceStore.getInstance()).isSameAs(self.getInstance()); + } + + + @Test + void should_allow_update_instance_status() { + should_register_microservice_and_instance_when_both_not_exist(); + + RegisterRequest request = self.buildRegisterRequest() + .setStatus(MicroserviceInstanceStatus.TESTING); + ticker.setValues(1L); + InstanceStore instanceStore = service.register(request); + + assertThat(self.getInstance().getStatus()).isEqualTo(MicroserviceInstanceStatus.TESTING); + assertThat(instanceStore.getLastHeartBeat()).isEqualTo(1); + } + + @Test + void should_process_as_heartbeat_when_nothing_changed() { + should_register_microservice_and_instance_when_both_not_exist(); + InstanceStore instanceStore = store.findInstanceStore(self.getInstanceId()); + assertThat(instanceStore.getLastHeartBeat()).isEqualTo(0); + + ticker.setValues(1L); + should_register_microservice_and_instance_when_both_not_exist(); + assertThat(instanceStore.getLastHeartBeat()).isEqualTo(1); + } +} diff --git a/service-registry/registry-local/pom.xml b/service-registry/registry-local/pom.xml new file mode 100644 index 00000000000..e3ddcc5510b --- /dev/null +++ b/service-registry/registry-local/pom.xml @@ -0,0 +1,85 @@ + + + + + + org.apache.servicecomb + service-registry-parent + 3.4.0-SNAPSHOT + + 4.0.0 + + registry-local + Java Chassis::Java Chassis::Service Registry::Local + + + + org.apache.servicecomb + foundation-vertx + + + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + swagger-generator-core + + + org.apache.servicecomb + java-chassis-core + + + + org.apache.servicecomb + foundation-registry + + + + org.apache.commons + commons-lang3 + + + + 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 + test + + + diff --git a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscovery.java b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscovery.java new file mode 100644 index 00000000000..4d3a0a6e822 --- /dev/null +++ b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscovery.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.localregistry; + + +import java.util.List; + +import org.apache.servicecomb.registry.api.Discovery; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +public class LocalDiscovery implements Discovery { + public static final String LOCAL_DISCOVERY_ENABLED = "servicecomb.registry.local.%s.%s.enabled"; + + private LocalRegistryStore localRegistryStore; + + private Environment environment; + + @Autowired + public void setLocalRegistryStore(LocalRegistryStore localRegistryStore) { + this.localRegistryStore = localRegistryStore; + } + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public int getOrder() { + return -10000; + } + + @Override + public String name() { + return LocalRegistryConst.LOCAL_REGISTRY_NAME; + } + + @Override + public boolean enabled(String application, String serviceName) { + return environment.getProperty(String.format(LOCAL_DISCOVERY_ENABLED, application, serviceName), + boolean.class, true); + } + + @Override + public List findServiceInstances(String application, String serviceName) { + return this.localRegistryStore.findServiceInstances(application, serviceName); + } + + @Override + public List findServices(String application) { + return this.localRegistryStore.findServices(application); + } + + @Override + public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { + + } + + @Override + public void init() { + this.localRegistryStore.init(); + } + + @Override + public void run() { + this.localRegistryStore.run(); + } + + @Override + public void destroy() { + } + + @Override + public boolean enabled() { + return this.environment.getProperty(LocalRegistryConst.LOCAL_REGISTRY_ENABLED, boolean.class, true); + } +} diff --git a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscoveryInstance.java b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscoveryInstance.java new file mode 100644 index 00000000000..ff8e10794f1 --- /dev/null +++ b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscoveryInstance.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.localregistry; + +import java.lang.management.ManagementFactory; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; +import org.apache.servicecomb.registry.api.AbstractDiscoveryInstance; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import io.swagger.v3.oas.models.OpenAPI; + +public class LocalDiscoveryInstance extends AbstractDiscoveryInstance { + private static final AtomicLong INSTANCE_ID = new AtomicLong(); + + private final RegistryBean registryBean; + + private final LocalRegistrationInstance localRegistrationInstance; + + private final List endpoints; + + private final String instanceId; + + public LocalDiscoveryInstance(LocalOpenAPIRegistry localOpenAPIRegistry, + RegistryBean registryBean, List endpoints, + LocalRegistrationInstance localRegistrationInstance) { + this.registryBean = registryBean; + this.localRegistrationInstance = localRegistrationInstance; + this.endpoints = endpoints; + this.instanceId = System.currentTimeMillis() + "-" + + ManagementFactory.getRuntimeMXBean().getPid() + "-" + INSTANCE_ID.getAndIncrement(); + + registryBean.getSchemaInterfaces().forEach((k, v) -> { + SwaggerGenerator generator = SwaggerGenerator.create(v); + OpenAPI openAPI = generator.generate(); + if (openAPI == null) { + throw new IllegalStateException(String.format("Generate schema for %s/%s/%s failed.", + registryBean.getAppId(), registryBean.getServiceName(), k)); + } + String schemaContent = SwaggerUtils.swaggerToString(openAPI); + if (StringUtils.isEmpty(schemaContent)) { + throw new IllegalStateException(String.format("Generate schema for %s/%s/%s failed.", + registryBean.getAppId(), registryBean.getServiceName(), k)); + } + localOpenAPIRegistry.registerOpenAPI(registryBean.getAppId(), registryBean.getServiceName(), + k, openAPI); + }); + } + + public LocalDiscoveryInstance(LocalRegistrationInstance registrationInstance) { + this.registryBean = new RegistryBean(); + this.registryBean.setAppId(registrationInstance.getApplication()); + this.registryBean.setServiceName(registrationInstance.getServiceName()); + this.registryBean.setVersion(registrationInstance.getVersion()); + this.localRegistrationInstance = registrationInstance; + this.endpoints = registrationInstance.getEndpoints(); + this.instanceId = registrationInstance.getInstanceId(); + } + + @Override + public MicroserviceInstanceStatus getStatus() { + return MicroserviceInstanceStatus.UP; + } + + @Override + public String getRegistryName() { + return LocalRegistryConst.LOCAL_REGISTRY_NAME; + } + + @Override + public String getEnvironment() { + return localRegistrationInstance.getEnvironment(); + } + + @Override + public String getApplication() { + return registryBean.getAppId(); + } + + @Override + public String getServiceName() { + return registryBean.getServiceName(); + } + + @Override + public String getAlias() { + return null; + } + + @Override + public String getVersion() { + return registryBean.getVersion(); + } + + @Override + public DataCenterInfo getDataCenterInfo() { + return null; + } + + @Override + public String getDescription() { + return null; + } + + @Override + public Map getProperties() { + return null; + } + + @Override + public Map getSchemas() { + // not implement + return Collections.emptyMap(); + } + + @Override + public List getEndpoints() { + return endpoints; + } + + @Override + public String getInstanceId() { + return instanceId; + } +} diff --git a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistration.java b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistration.java new file mode 100644 index 00000000000..0269d7b8945 --- /dev/null +++ b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistration.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.localregistry; + +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.Registration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +public class LocalRegistration implements Registration { + private final LocalRegistrationInstance localRegistrationInstance; + + private Environment environment; + + public LocalRegistration(LocalRegistrationInstance localRegistrationInstance) { + this.localRegistrationInstance = localRegistrationInstance; + } + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public void init() { + + } + + @Override + public void run() { + + } + + @Override + public void destroy() { + + } + + @Override + public String name() { + return LocalRegistryConst.LOCAL_REGISTRY_NAME; + } + + @Override + public LocalRegistrationInstance getMicroserviceInstance() { + return localRegistrationInstance; + } + + @Override + public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { + return true; + } + + @Override + public void addSchema(String schemaId, String content) { + // not implement + } + + @Override + public void addEndpoint(String endpoint) { + localRegistrationInstance.addEndpoint(endpoint); + } + + @Override + public void addProperty(String key, String value) { + localRegistrationInstance.addProperty(key, value); + } + + @Override + public boolean enabled() { + return this.environment.getProperty(LocalRegistryConst.LOCAL_REGISTRY_ENABLED, Boolean.class, true); + } +} diff --git a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistrationInstance.java b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistrationInstance.java new file mode 100644 index 00000000000..e07527636a9 --- /dev/null +++ b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistrationInstance.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.localregistry; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; +import org.apache.servicecomb.registry.RegistrationId; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.RegistrationInstance; +import org.springframework.core.env.Environment; + +public class LocalRegistrationInstance implements RegistrationInstance { + private final Environment environment; + + private final DataCenterInfo dataCenterInfo; + + private final LocalOpenAPIRegistry localOpenAPIRegistry; + + private final String instanceId; + + private final Map schemas = new HashMap<>(); + + private final List endpoints = new ArrayList<>(); + + private final Map properties = new HashMap<>(); + + public LocalRegistrationInstance( + Environment environment, + DataCenterProperties dataCenterProperties, + LocalOpenAPIRegistry localOpenAPIRegistry, + RegistrationId registrationId) { + this.environment = environment; + this.localOpenAPIRegistry = localOpenAPIRegistry; + + this.dataCenterInfo = new DataCenterInfo(); + this.dataCenterInfo.setName(dataCenterProperties.getName()); + this.dataCenterInfo.setRegion(dataCenterProperties.getRegion()); + this.dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone()); + + this.properties.putAll(BootStrapProperties.readServiceProperties(environment)); + + this.instanceId = registrationId.getInstanceId(); + } + + @Override + public String getEnvironment() { + return BootStrapProperties.readServiceEnvironment(environment); + } + + @Override + public String getApplication() { + return BootStrapProperties.readApplication(environment); + } + + @Override + public String getServiceName() { + return BootStrapProperties.readServiceName(environment); + } + + @Override + public String getAlias() { + return BootStrapProperties.readServiceAlias(environment); + } + + @Override + public String getVersion() { + return BootStrapProperties.readServiceVersion(environment); + } + + @Override + public DataCenterInfo getDataCenterInfo() { + return dataCenterInfo; + } + + @Override + public String getDescription() { + return BootStrapProperties.readServiceDescription(environment); + } + + @Override + public Map getProperties() { + return this.properties; + } + + @Override + public Map getSchemas() { + return this.schemas; + } + + @Override + public List getEndpoints() { + return this.endpoints; + } + + @Override + public String getInstanceId() { + return this.instanceId; + } + + @Override + public MicroserviceInstanceStatus getStatus() { + return MicroserviceInstanceStatus.UP; + } + + public void addEndpoint(String endpoint) { + this.endpoints.add(endpoint); + } + + public void addProperty(String key, String value) { + this.properties.put(key, value); + } +} diff --git a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryConfiguration.java b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryConfiguration.java new file mode 100644 index 00000000000..9ebdcca368b --- /dev/null +++ b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryConfiguration.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.localregistry; + +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; +import org.apache.servicecomb.registry.RegistrationId; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +@Configuration +public class LocalRegistryConfiguration { + @Bean + public LocalRegistrationInstance localRegistrationInstance( + Environment environment, + DataCenterProperties dataCenterProperties, + LocalOpenAPIRegistry localOpenAPIRegistry, + RegistrationId registrationId) { + return new LocalRegistrationInstance(environment, dataCenterProperties, localOpenAPIRegistry, registrationId); + } + + @Bean + public LocalRegistration localRegistration(LocalRegistrationInstance localRegistrationInstance) { + return new LocalRegistration(localRegistrationInstance); + } + + @Bean + public LocalDiscovery localDiscovery() { + return new LocalDiscovery(); + } + + @Bean + public LocalRegistryStore localRegistryStore() { + return new LocalRegistryStore(); + } +} diff --git a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryConst.java b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryConst.java new file mode 100644 index 00000000000..6e54b29e7cd --- /dev/null +++ b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryConst.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.localregistry; + +public class LocalRegistryConst { + public static final String LOCAL_REGISTRY_ENABLED = "servicecomb.registry.local.enabled"; + + public static final String LOCAL_REGISTRY_NAME = "local-registry"; +} diff --git a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryStore.java b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryStore.java new file mode 100644 index 00000000000..3af782e5900 --- /dev/null +++ b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryStore.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.localregistry; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.servicecomb.config.YAMLUtil; +import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.apache.servicecomb.foundation.common.utils.JvmUtils; +import org.apache.servicecomb.localregistry.RegistryBean.Instance; +import org.springframework.beans.factory.annotation.Autowired; + +public class LocalRegistryStore { + private static final String REGISTRY_FILE_NAME = "registry.yaml"; + + private LocalRegistrationInstance selfMicroserviceInstance; + + private LocalOpenAPIRegistry localOpenAPIRegistry; + + // application:serviceName + private final Map>> + microserviceInstanceMap = new ConcurrentHashMap<>(); + + public LocalRegistryStore() { + + } + + @Autowired + public void setLocalRegistrationInstance(LocalRegistrationInstance selfMicroserviceInstance) { + this.selfMicroserviceInstance = selfMicroserviceInstance; + } + + @Autowired + public void setLocalOpenAPIRegistry(LocalOpenAPIRegistry localOpenAPIRegistry) { + this.localOpenAPIRegistry = localOpenAPIRegistry; + } + + public void init() { + microserviceInstanceMap.clear(); + } + + public void run() { + List beans = loadYamlBeans(); + BeanUtils.getBeansOfType(RegistryBean.class).forEach((key, value) -> beans.add(value)); + + initRegistryFromBeans(beans); + + addSelf(); + } + + private void addSelf() { + microserviceInstanceMap.computeIfAbsent(selfMicroserviceInstance.getApplication(), key -> + new ConcurrentHashMapEx<>()).computeIfAbsent(selfMicroserviceInstance.getServiceName(), key -> + new ArrayList<>()).add(new LocalDiscoveryInstance(selfMicroserviceInstance)); + } + + private List loadYamlBeans() { + List beans = new ArrayList<>(); + + try { + ClassLoader loader = JvmUtils.findClassLoader(); + Enumeration urls = loader.getResources(REGISTRY_FILE_NAME); + + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + + try (InputStream is = url.openStream()) { + if (is != null) { + beans.addAll(initFromData(is)); + } + } + } + } catch (IOException e) { + throw new IllegalStateException(e); + } + + return beans; + } + + private List initFromData(InputStream is) { + Map data = YAMLUtil.yaml2Properties(is); + return initFromData(data); + } + + @SuppressWarnings("unchecked") + private List initFromData(Map data) { + List beans = new ArrayList<>(); + + for (Entry entry : data.entrySet()) { + String name = entry.getKey(); + List> serviceConfigs = (List>) entry.getValue(); + for (Map serviceConfig : serviceConfigs) { + beans.add(RegistryBean.buildFromYamlModel(name, serviceConfig)); + } + } + return beans; + } + + private void initRegistryFromBeans(List beans) { + beans.forEach((bean -> { + List instances = microserviceInstanceMap.computeIfAbsent(bean.getAppId(), key -> + new ConcurrentHashMapEx<>()).computeIfAbsent(bean.getServiceName(), key -> + new ArrayList<>()); + if (bean.getInstances() == null) { + return; + } + for (Instance instance : bean.getInstances().getInstances()) { + instances.add(new LocalDiscoveryInstance(localOpenAPIRegistry, + bean, instance.getEndpoints(), selfMicroserviceInstance)); + } + })); + } + + public List findServiceInstances(String application, String serviceName) { + Map> app = microserviceInstanceMap.get(application); + if (app == null) { + return Collections.emptyList(); + } + List instances = app.get(serviceName); + if (instances == null) { + return Collections.emptyList(); + } + return instances; + } + + public List findServices(String application) { + if (microserviceInstanceMap.get(application) == null) { + return Collections.emptyList(); + } + return microserviceInstanceMap.get(application).keySet().stream().toList(); + } +} diff --git a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/RegistryBean.java b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/RegistryBean.java new file mode 100644 index 00000000000..3a3ac85d457 --- /dev/null +++ b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/RegistryBean.java @@ -0,0 +1,193 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.localregistry; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; + +/** + * + * Configuration bean for local services. Bean configuration is token + * same as `registry.yaml` file configuration. + * + */ +public class RegistryBean { + public static class Instances { + private List instances; + + public List getInstances() { + return instances; + } + + public Instances setInstances(List instances) { + this.instances = instances; + return this; + } + } + + public static class Instance { + private List endpoints; + + public List getEndpoints() { + return endpoints; + } + + public Instance setEndpoints(List endpoints) { + this.endpoints = endpoints; + return this; + } + } + + private String id; + + private String serviceName; + + private String version; + + private String appId; + + /** + * Schema ids configured from RegistryBean. Generate schema content from Class. + */ + private final Map> schemaInterfaces = new HashMap<>(); + + /** + * Schema ids configured in yaml. Will load contents from local file. + */ + private List schemaIds = new ArrayList<>(); + + private Instances instances; + + @SuppressWarnings("unchecked") + public static RegistryBean buildFromYamlModel + (String serviceName, Map serviceConfig) { + return new RegistryBean() + .setId(validId((String) serviceConfig.get("id"))) + .setServiceName(serviceName) + .setVersion((String) serviceConfig.get("version")) + .setAppId(validAppId((String) serviceConfig.get("appid"))) + .setSchemaIds(validListsValue((List) serviceConfig.get("schemaIds"))) + .setInstances( + new Instances() + .setInstances(validInstances((List>) serviceConfig.get("instances")))); + } + + @SuppressWarnings("unchecked") + private static List validInstances(List> instancesConfig) { + if (instancesConfig == null) { + return Collections.emptyList(); + } + + List instances = new ArrayList<>(); + for (Map instanceConfig : instancesConfig) { + instances.add(new Instance().setEndpoints( + validListsValue((List) instanceConfig.get("endpoints")))); + } + return instances; + } + + private static List validListsValue(List listsValue) { + return listsValue == null ? Collections.emptyList() : listsValue; + } + + private static String validId(String serviceId) { + return StringUtils.isEmpty(serviceId) ? UUID.randomUUID().toString() : serviceId; + } + + private static String validAppId(String configAppId) { + if (!StringUtils.isEmpty(configAppId)) { + return configAppId; + } + return BootStrapProperties.DEFAULT_APPLICATION; + } + + public String getId() { + return id; + } + + public RegistryBean setId(String id) { + this.id = id; + return this; + } + + public String getServiceName() { + return serviceName; + } + + public RegistryBean setServiceName(String serviceName) { + this.serviceName = serviceName; + return this; + } + + public String getVersion() { + return version; + } + + public RegistryBean setVersion(String version) { + this.version = version; + return this; + } + + public String getAppId() { + return appId; + } + + public RegistryBean setAppId(String appId) { + this.appId = appId; + return this; + } + + public RegistryBean addSchemaInterface(String schemaId, Class schemaInterface) { + this.schemaInterfaces.put(schemaId, schemaInterface); + return this; + } + + public RegistryBean addSchemaId(String schemaId) { + this.schemaIds.add(schemaId); + return this; + } + + public RegistryBean setSchemaIds(List schemaIds) { + this.schemaIds = schemaIds; + return this; + } + + public List getSchemaIds() { + return this.schemaIds; + } + + public Map> getSchemaInterfaces() { + return this.schemaInterfaces; + } + + public Instances getInstances() { + return instances; + } + + public RegistryBean setInstances(Instances instances) { + this.instances = instances; + return this; + } +} diff --git a/service-registry/registry-local/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/service-registry/registry-local/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..39d17511373 --- /dev/null +++ b/service-registry/registry-local/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.localregistry.LocalRegistryConfiguration diff --git a/service-registry/registry-nacos/pom.xml b/service-registry/registry-nacos/pom.xml new file mode 100644 index 00000000000..11e58275235 --- /dev/null +++ b/service-registry/registry-nacos/pom.xml @@ -0,0 +1,51 @@ + + + + + + org.apache.servicecomb + service-registry-parent + 3.4.0-SNAPSHOT + + 4.0.0 + + registry-nacos + Java Chassis::Service Registry::Nacos + + + + com.alibaba.nacos + nacos-client + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + java-chassis-core + + + + diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConfiguration.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConfiguration.java new file mode 100644 index 00000000000..24eaf50a33d --- /dev/null +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConfiguration.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.registry.nacos; + +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.registry.RegistrationId; +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 NacosConfiguration { + @Bean + @ConfigurationProperties(prefix = NacosConst.NACOS_REGISTRY_PREFIX) + public NacosDiscoveryProperties nacosDiscoveryProperties() { + return new NacosDiscoveryProperties(); + } + + @Bean + public NacosRegistration nacosRegistration( + DataCenterProperties dataCenterProperties, + @Qualifier("nacosDiscoveryProperties") NacosDiscoveryProperties nacosDiscoveryProperties, + Environment environment, RegistrationId registrationId) { + return new NacosRegistration(dataCenterProperties, nacosDiscoveryProperties, environment, registrationId); + } + + @Bean + public NacosDiscovery nacosDiscovery( + @Qualifier("nacosDiscoveryProperties") NacosDiscoveryProperties nacosDiscoveryProperties) { + return new NacosDiscovery(nacosDiscoveryProperties); + } +} diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConst.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConst.java new file mode 100644 index 00000000000..563d1726095 --- /dev/null +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConst.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.registry.nacos; + +public class NacosConst { + public static final String PROPERTY_VERSION = "scb-version"; + + public static final String PROPERTY_ALIAS = "scb-alias"; + + public static final String PROPERTY_DESCRIPTION = "scb-description"; + + public static final String PROPERTY_ENDPOINT = "scb-endpoint"; + + public static final String PROPERTY_REGION = "scb-region"; + + public static final String PROPERTY_ZONE = "scb-zone"; + + public static final String PROPERTY_DATACENTER = "scb-datacenter"; + + public static final String PROPERTY_SCHEMA_PREFIX = "scb-schema-"; + + public static final String ENDPOINT_PROPERTY_SEPARATOR = ","; + + public static final String NACOS_REGISTRY_PREFIX = "servicecomb.registry.nacos"; + + public static final String SERVER_ADDR = "serverAddr"; + + public static final String USERNAME = "username"; + + public static final String PASSWORD = "password"; + + public static final String NAMESPACE = "namespace"; + + public static final String NACOS_NAMING_LOG_NAME = "com.alibaba.nacos.naming.log.filename"; + + public static final String ACCESS_KEY = "accessKey"; + + public static final String SECRET_KEY = "secretKey"; + + public static final String CLUSTER_NAME = "clusterName"; + + public static final String NAMING_LOAD_CACHE_AT_START = "namingLoadCacheAtStart"; + + public static final String NACOS_REGISTRY_NAME = "nacos-registry"; + + public static final String NACOS_STATUS = "nacos-status"; +} diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscovery.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscovery.java new file mode 100644 index 00000000000..dfe2a5739eb --- /dev/null +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscovery.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.nacos; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.alibaba.nacos.api.exception.NacosException; + +import org.apache.servicecomb.registry.api.Discovery; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; + +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.Instance; + +public class NacosDiscovery implements Discovery { + public static final String NACOS_DISCOVERY_ENABLED = "servicecomb.registry.nacos.%s.%s.enabled"; + + private static final Map> SUBSCRIBES = new HashMap<>(); + + private final Object lock = new Object(); + + private final NacosDiscoveryProperties nacosDiscoveryProperties; + + private Environment environment; + + private NamingService namingService; + + private InstanceChangedListener instanceChangedListener; + + @Autowired + public NacosDiscovery(NacosDiscoveryProperties nacosDiscoveryProperties) { + this.nacosDiscoveryProperties = nacosDiscoveryProperties; + } + + @Autowired + @SuppressWarnings("unused") + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public String name() { + return NacosConst.NACOS_REGISTRY_NAME; + } + + @Override + public boolean enabled(String application, String serviceName) { + return environment.getProperty(String.format(NACOS_DISCOVERY_ENABLED, application, serviceName), + boolean.class, true); + } + + @Override + public List findServiceInstances(String application, String serviceName) { + try { + // Get the instance first, and the Listener will not be executed for the first subscribe. + List instances = namingService.getAllInstances(serviceName, application, true); + + AtomicBoolean result = SUBSCRIBES.computeIfAbsent(application, + k -> new HashMap<>()).computeIfAbsent(serviceName, k -> new AtomicBoolean(true)); + if (result.get()) { + synchronized (lock) { + if (result.get()) { + namingService.subscribe(serviceName, application, (event) -> { + if (event instanceof NamingEvent) { + this.instanceChangedListener.onInstanceChanged(name(), application, serviceName, + convertServiceInstanceList(((NamingEvent) event).getInstances(), application, serviceName)); + } + }); + result.set(false); + } + } + } + return convertServiceInstanceList(instances, application, serviceName); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + @Override + public List findServices(String application) { + try { + return namingService.getServicesOfServer(0, Integer.MAX_VALUE, application) + .getData(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + private List convertServiceInstanceList( + List instances, String application, String serviceName) { + if (CollectionUtils.isEmpty(instances)) { + return Collections.emptyList(); + } + List result = new ArrayList<>(); + for (Instance instance : instances) { + result.add(new NacosDiscoveryInstance(instance, application, serviceName, environment)); + } + return result; + } + + @Override + public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { + this.instanceChangedListener = instanceChangedListener; + } + + @Override + public void init() { + namingService = NamingServiceManager.buildNamingService(environment, nacosDiscoveryProperties); + } + + @Override + public void run() { + + } + + @Override + public void destroy() { + if (namingService != null) { + try { + namingService.shutDown(); + } catch (NacosException e) { + throw new IllegalStateException("destroy process is interrupted."); + } + } + } + + @Override + public boolean enabled() { + return nacosDiscoveryProperties.isEnabled(); + } +} diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscoveryInstance.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscoveryInstance.java new file mode 100644 index 00000000000..6360d012012 --- /dev/null +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscoveryInstance.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.nacos; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.registry.api.AbstractDiscoveryInstance; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.springframework.core.env.Environment; + +import com.alibaba.nacos.api.naming.pojo.Instance; + +public class NacosDiscoveryInstance extends AbstractDiscoveryInstance { + private final Instance instance; + + private final String application; + + private final String serviceName; + + private final Environment environment; + + private final Map schemas; + + private final List endpoints; + + public NacosDiscoveryInstance(Instance instance, String application, String serviceName, + Environment environment) { + this.instance = instance; + this.environment = environment; + this.application = application; + this.serviceName = serviceName; + this.endpoints = readEndpoints(instance); + this.schemas = readSchemas(instance); + } + + @Override + public MicroserviceInstanceStatus getStatus() { + if (instance.isEnabled()) { + String instanceStatus = instance.getMetadata().get(NacosConst.NACOS_STATUS); + if (StringUtils.isBlank(instanceStatus)) { + return MicroserviceInstanceStatus.UP; + } + return MicroserviceInstanceStatus.valueOf(instanceStatus); + } else { + return MicroserviceInstanceStatus.DOWN; + } + } + + @Override + public String getRegistryName() { + return NacosConst.NACOS_REGISTRY_NAME; + } + + @Override + public String getEnvironment() { + return BootStrapProperties.readServiceEnvironment(environment); + } + + @Override + public String getApplication() { + return this.application; + } + + @Override + public String getServiceName() { + // nacos instance service name may contain group and `@` annotations + return this.serviceName; + } + + @Override + public String getAlias() { + return instance.getMetadata().get(NacosConst.PROPERTY_ALIAS); + } + + @Override + public String getVersion() { + return instance.getMetadata().get(NacosConst.PROPERTY_VERSION); + } + + @Override + public DataCenterInfo getDataCenterInfo() { + DataCenterInfo dataCenterInfo = new DataCenterInfo(); + dataCenterInfo.setRegion(instance.getMetadata().get(NacosConst.PROPERTY_REGION)); + dataCenterInfo.setAvailableZone(instance.getMetadata().get(NacosConst.PROPERTY_ZONE)); + dataCenterInfo.setName(instance.getMetadata().get(NacosConst.PROPERTY_DATACENTER)); + return dataCenterInfo; + } + + @Override + public String getDescription() { + return instance.getMetadata().get(NacosConst.PROPERTY_DESCRIPTION); + } + + @Override + public Map getProperties() { + return instance.getMetadata(); + } + + @Override + public Map getSchemas() { + return schemas; + } + + private static Map readSchemas(Instance instance) { + Map metaData = instance.getMetadata(); + Map instanceSchemas = new HashMap<>(); + for (Map.Entry entry : metaData.entrySet()) { + if (entry.getKey().startsWith(NacosConst.PROPERTY_SCHEMA_PREFIX)) { + instanceSchemas.put(entry.getKey().substring(NacosConst.PROPERTY_SCHEMA_PREFIX.length()), + entry.getValue()); + } + } + return instanceSchemas; + } + + @Override + public List getEndpoints() { + return endpoints; + } + + private static List readEndpoints(Instance instance) { + if (StringUtils.isEmpty(instance.getMetadata().get(NacosConst.PROPERTY_ENDPOINT))) { + // interoperate with spring cloud using nacos + if (StringUtils.isNotEmpty(instance.getIp()) && instance.getPort() > 0) { + return List.of("rest://" + instance.getIp() + ":" + instance.getPort()); + } + return Collections.emptyList(); + } + return Arrays.asList(instance.getMetadata().get(NacosConst.PROPERTY_ENDPOINT) + .split(NacosConst.ENDPOINT_PROPERTY_SEPARATOR)); + } + + @Override + public String getInstanceId() { + return instance.getInstanceId(); + } +} diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscoveryProperties.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscoveryProperties.java new file mode 100644 index 00000000000..70ff0cee500 --- /dev/null +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscoveryProperties.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.nacos; + +import java.util.HashMap; +import java.util.Map; + +public class NacosDiscoveryProperties { + private boolean enabled = true; + + private String serverAddr = "http://127.0.0.1:8848"; + + private Map metadata = new HashMap<>(); + + private boolean ephemeral = true; + + private String username; + + private String password; + + private String accessKey; + + private String secretKey; + + private String namingLoadCacheAtStart = "false"; + + private String clusterName = "DEFAULT"; + + private float weight = 1; + + private boolean instanceEnabled = true; + + private String logName; + + private boolean enableSwaggerRegistration = false; + + public String getServerAddr() { + return serverAddr; + } + + public void setServerAddr(String serverAddr) { + this.serverAddr = serverAddr; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + + public boolean isEphemeral() { + return ephemeral; + } + + public void setEphemeral(boolean ephemeral) { + this.ephemeral = ephemeral; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getAccessKey() { + return accessKey; + } + + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public String getNamingLoadCacheAtStart() { + return namingLoadCacheAtStart; + } + + public void setNamingLoadCacheAtStart(String namingLoadCacheAtStart) { + this.namingLoadCacheAtStart = namingLoadCacheAtStart; + } + + public String getClusterName() { + return clusterName; + } + + public void setClusterName(String clusterName) { + this.clusterName = clusterName; + } + + public float getWeight() { + return weight; + } + + public void setWeight(float weight) { + this.weight = weight; + } + + public boolean isInstanceEnabled() { + return instanceEnabled; + } + + public void setInstanceEnabled(boolean instanceEnabled) { + this.instanceEnabled = instanceEnabled; + } + + public String getLogName() { + return logName; + } + + public void setLogName(String logName) { + this.logName = logName; + } + + public boolean isEnableSwaggerRegistration() { + return enableSwaggerRegistration; + } + + public void setEnableSwaggerRegistration(boolean enableSwaggerRegistration) { + this.enableSwaggerRegistration = enableSwaggerRegistration; + } +} diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosMicroserviceHandler.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosMicroserviceHandler.java new file mode 100644 index 00000000000..29aed07bffb --- /dev/null +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosMicroserviceHandler.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.registry.nacos; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.config.DataCenterProperties; +import org.springframework.core.env.Environment; + +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.utils.NamingUtils; + +public class NacosMicroserviceHandler { + private static final String VERSION_MAPPING = "VERSION_MAPPING"; + + private static final String CAS_APPLICATION_ID = "CAS_APPLICATION_ID"; + + private static final String CAS_COMPONENT_NAME = "CAS_COMPONENT_NAME"; + + private static final String CAS_INSTANCE_VERSION = "CAS_INSTANCE_VERSION"; + + private static final String CAS_INSTANCE_ID = "CAS_INSTANCE_ID"; + + private static final String CAS_ENVIRONMENT_ID = "CAS_ENVIRONMENT_ID"; + + private static final String INSTANCE_PROPS = "SERVICECOMB_INSTANCE_PROPS"; + + public static Instance createMicroserviceInstance( + DataCenterProperties dataCenterProperties, NacosDiscoveryProperties properties, Environment environment) { + Instance instance = new Instance(); + instance.setServiceName(NamingUtils.getGroupedName( + BootStrapProperties.readServiceName(environment), BootStrapProperties.readApplication(environment))); + instance.setWeight(properties.getWeight()); + instance.setEnabled(properties.isInstanceEnabled()); + instance.setClusterName(properties.getClusterName()); + instance.setEphemeral(properties.isEphemeral()); + + Map metadata = properties.getMetadata(); + metadata.put(NacosConst.PROPERTY_VERSION, BootStrapProperties.readServiceVersion(environment)); + metadata.put(NacosConst.PROPERTY_ALIAS, BootStrapProperties.readServiceAlias(environment)); + metadata.put(NacosConst.PROPERTY_DESCRIPTION, BootStrapProperties.readServiceDescription(environment)); + metadata.put(NacosConst.PROPERTY_DATACENTER, dataCenterProperties.getName()); + metadata.put(NacosConst.PROPERTY_REGION, dataCenterProperties.getRegion()); + metadata.put(NacosConst.PROPERTY_ZONE, dataCenterProperties.getAvailableZone()); + if (!StringUtils.isEmpty(environment.getProperty(VERSION_MAPPING)) && + !StringUtils.isEmpty(environment.getProperty(environment.getProperty(VERSION_MAPPING)))) { + metadata.put("version", environment.getProperty(environment.getProperty(VERSION_MAPPING))); + } + metadata.putAll(genCasProperties(environment)); + instance.setMetadata(metadata); + return instance; + } + + private static Map genCasProperties(Environment environment) { + Map properties = new HashMap<>(); + if (!StringUtils.isEmpty(environment.getProperty(CAS_APPLICATION_ID))) { + properties.put(CAS_APPLICATION_ID, environment.getProperty(CAS_APPLICATION_ID)); + } + if (!StringUtils.isEmpty(environment.getProperty(CAS_COMPONENT_NAME))) { + properties.put(CAS_COMPONENT_NAME, environment.getProperty(CAS_COMPONENT_NAME)); + } + if (!StringUtils.isEmpty(environment.getProperty(CAS_INSTANCE_VERSION))) { + properties.put(CAS_INSTANCE_VERSION, environment.getProperty(CAS_INSTANCE_VERSION)); + } + if (!StringUtils.isEmpty(environment.getProperty(CAS_INSTANCE_ID))) { + properties.put(CAS_INSTANCE_ID, environment.getProperty(CAS_INSTANCE_ID)); + } + if (!StringUtils.isEmpty(environment.getProperty(CAS_ENVIRONMENT_ID))) { + properties.put(CAS_ENVIRONMENT_ID, environment.getProperty(CAS_ENVIRONMENT_ID)); + } + + String[] instancePropArray = ConfigUtil.parseArrayValue(environment.getProperty(INSTANCE_PROPS)) + .toArray(new String[0]); + if (instancePropArray.length != 0) { + properties.putAll(parseProps(instancePropArray)); + } + + return properties; + } + + private static Map parseProps(String... value) { + return Arrays.stream(value).map(v -> v.split(":")) + .filter(v -> v.length == 2) + .collect(Collectors.toMap(v -> v[0], v -> v[1])); + } +} diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosRegistration.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosRegistration.java new file mode 100644 index 00000000000..96f41d0670e --- /dev/null +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosRegistration.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.nacos; + +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.invocation.endpoint.EndpointUtils; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.registry.RegistrationId; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.Registration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.pojo.Instance; + +public class NacosRegistration implements Registration { + private final NacosDiscoveryProperties nacosDiscoveryProperties; + + private final Environment environment; + + private final String instanceId; + + private final DataCenterProperties dataCenterProperties; + + private NacosRegistrationInstance nacosRegistrationInstance; + + private Instance instance; + + private NamingService namingService; + + @Autowired + public NacosRegistration(DataCenterProperties dataCenterProperties, NacosDiscoveryProperties nacosDiscoveryProperties, + Environment environment, RegistrationId registrationId) { + this.instanceId = registrationId.getInstanceId(); + this.dataCenterProperties = dataCenterProperties; + this.nacosDiscoveryProperties = nacosDiscoveryProperties; + this.environment = environment; + } + + @Override + public void init() { + instance = NacosMicroserviceHandler.createMicroserviceInstance(dataCenterProperties, nacosDiscoveryProperties, + environment); + instance.setInstanceId(instanceId); + nacosRegistrationInstance = new NacosRegistrationInstance(instance, environment); + instance.getMetadata() + .put(NacosConst.NACOS_STATUS, BootStrapProperties.readServiceInstanceInitialStatus(environment)); + namingService = NamingServiceManager.buildNamingService(environment, nacosDiscoveryProperties); + } + + @Override + public void run() { + try { + if (nacosDiscoveryProperties.isEnableSwaggerRegistration()) { + addSchemas(nacosRegistrationInstance.getSchemas(), instance); + } + addEndpoints(nacosRegistrationInstance.getEndpoints(), instance); + namingService.registerInstance(nacosRegistrationInstance.getServiceName(), + nacosRegistrationInstance.getApplication(), instance); + } catch (NacosException e) { + throw new IllegalStateException(e); + } + } + + private static void addSchemas(Map schemas, Instance instance) { + if (CollectionUtils.isEmpty(schemas)) { + return; + } + for (Map.Entry entry : schemas.entrySet()) { + instance.addMetadata(NacosConst.PROPERTY_SCHEMA_PREFIX + entry.getKey(), entry.getValue()); + } + } + + private static void addEndpoints(List endpoints, Instance instance) { + if (endpoints.isEmpty()) { + return; + } + + for (String endpoint : endpoints) { + Endpoint temp = EndpointUtils.parse(endpoint); + if (temp.getAddress() instanceof URIEndpointObject) { + instance.setIp(((URIEndpointObject) temp.getAddress()).getHostOrIp()); + instance.setPort(((URIEndpointObject) temp.getAddress()).getPort()); + break; + } + } + + instance.addMetadata(NacosConst.PROPERTY_ENDPOINT, String.join(NacosConst.ENDPOINT_PROPERTY_SEPARATOR, endpoints)); + } + + @Override + public void destroy() { + try { + namingService.deregisterInstance(nacosRegistrationInstance.getServiceName(), + nacosRegistrationInstance.getApplication(), instance); + namingService.shutDown(); + } catch (NacosException e) { + throw new IllegalStateException("destroy process is interrupted."); + } + } + + @Override + public String name() { + return NacosConst.NACOS_REGISTRY_NAME; + } + + @Override + public NacosRegistrationInstance getMicroserviceInstance() { + return this.nacosRegistrationInstance; + } + + @Override + public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { + try { + instance.getMetadata().put(NacosConst.NACOS_STATUS, status.name()); + namingService.registerInstance(nacosRegistrationInstance.getServiceName(), + nacosRegistrationInstance.getApplication(), instance); + return true; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void addSchema(String schemaId, String content) { + if (nacosDiscoveryProperties.isEnableSwaggerRegistration()) { + nacosRegistrationInstance.addSchema(schemaId, content); + } + } + + @Override + public void addEndpoint(String endpoint) { + nacosRegistrationInstance.addEndpoint(endpoint); + } + + @Override + public void addProperty(String key, String value) { + instance.addMetadata(key, value); + } + + @Override + public boolean enabled() { + return nacosDiscoveryProperties.isEnabled(); + } +} diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosRegistrationInstance.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosRegistrationInstance.java new file mode 100644 index 00000000000..7a99f1b2dba --- /dev/null +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosRegistrationInstance.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.nacos; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.RegistrationInstance; +import org.springframework.core.env.Environment; + +import com.alibaba.nacos.api.naming.pojo.Instance; + +public class NacosRegistrationInstance implements RegistrationInstance { + private final Instance instance; + + private final Map schemas = new HashMap<>(); + + private final List endpoints = new ArrayList<>(); + + private final Environment environment; + + public NacosRegistrationInstance(Instance instance, + Environment environment) { + this.instance = instance; + this.environment = environment; + } + + @Override + public String getEnvironment() { + return BootStrapProperties.readServiceEnvironment(environment); + } + + @Override + public String getApplication() { + return BootStrapProperties.readApplication(environment); + } + + @Override + public String getServiceName() { + return BootStrapProperties.readServiceName(environment); + } + + @Override + public String getAlias() { + return BootStrapProperties.readServiceAlias(environment); + } + + @Override + public String getVersion() { + return instance.getMetadata().get(NacosConst.PROPERTY_VERSION); + } + + @Override + public DataCenterInfo getDataCenterInfo() { + DataCenterInfo dataCenterInfo = new DataCenterInfo(); + dataCenterInfo.setRegion(instance.getMetadata().get(NacosConst.PROPERTY_REGION)); + dataCenterInfo.setAvailableZone(instance.getMetadata().get(NacosConst.PROPERTY_ZONE)); + return dataCenterInfo; + } + + @Override + public String getDescription() { + return BootStrapProperties.readServiceDescription(environment); + } + + @Override + public Map getProperties() { + return instance.getMetadata(); + } + + @Override + public Map getSchemas() { + return schemas; + } + + @Override + public List getEndpoints() { + return endpoints; + } + + @Override + public String getInstanceId() { + return instance.getInstanceId(); + } + + @Override + public MicroserviceInstanceStatus getStatus() { + return MicroserviceInstanceStatus.valueOf(instance.getMetadata().get(NacosConst.NACOS_STATUS)); + } + + public void addSchema(String schemaId, String content) { + this.schemas.put(schemaId, content); + } + + public void addEndpoint(String endpoint) { + this.endpoints.add(endpoint); + } +} diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NamingServiceManager.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NamingServiceManager.java new file mode 100644 index 00000000000..635a7c48565 --- /dev/null +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NamingServiceManager.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.registry.nacos; + +import java.util.Objects; +import java.util.Properties; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.springframework.core.env.Environment; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.client.naming.NacosNamingService; + +public class NamingServiceManager { + private static volatile NamingService namingService; + + public static NamingService buildNamingService(Environment environment, NacosDiscoveryProperties properties) { + if (Objects.isNull(namingService)) { + synchronized (NamingServiceManager.class) { + if (Objects.isNull(namingService)) { + try { + namingService = new NacosNamingService(getProperties(environment, properties)); + } catch (NacosException e) { + throw new IllegalStateException("build namingService failed."); + } + } + } + } + return namingService; + } + + private static Properties getProperties(Environment environment, + NacosDiscoveryProperties nacosDiscoveryProperties) { + Properties properties = new Properties(); + properties.put(NacosConst.NAMESPACE, BootStrapProperties.readServiceEnvironment(environment)); + properties.put(NacosConst.SERVER_ADDR, nacosDiscoveryProperties.getServerAddr()); + if (nacosDiscoveryProperties.getUsername() != null) { + properties.put(NacosConst.USERNAME, nacosDiscoveryProperties.getUsername()); + } + if (nacosDiscoveryProperties.getPassword() != null) { + properties.put(NacosConst.PASSWORD, nacosDiscoveryProperties.getPassword()); + } + if (nacosDiscoveryProperties.getAccessKey() != null) { + properties.put(NacosConst.ACCESS_KEY, nacosDiscoveryProperties.getAccessKey()); + } + if (nacosDiscoveryProperties.getSecretKey() != null) { + properties.put(NacosConst.SECRET_KEY, nacosDiscoveryProperties.getSecretKey()); + } + if (nacosDiscoveryProperties.getLogName() != null) { + properties.put(NacosConst.NACOS_NAMING_LOG_NAME, nacosDiscoveryProperties.getLogName()); + } + + properties.put(NacosConst.CLUSTER_NAME, nacosDiscoveryProperties.getClusterName()); + properties.put(NacosConst.NAMING_LOAD_CACHE_AT_START, nacosDiscoveryProperties.getNamingLoadCacheAtStart()); + return properties; + } +} diff --git a/service-registry/registry-nacos/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/service-registry/registry-nacos/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..a53926ddf0c --- /dev/null +++ b/service-registry/registry-nacos/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.nacos.NacosConfiguration diff --git a/service-registry/registry-service-center/pom.xml b/service-registry/registry-service-center/pom.xml new file mode 100644 index 00000000000..97910c29054 --- /dev/null +++ b/service-registry/registry-service-center/pom.xml @@ -0,0 +1,86 @@ + + + + + + org.apache.servicecomb + service-registry-parent + 3.4.0-SNAPSHOT + + 4.0.0 + + registry-service-center + Java Chassis::Service Registry::Service Center + + + + org.apache.servicecomb + foundation-vertx + + + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + swagger-generator-core + + + + org.apache.servicecomb + java-chassis-core + + + + org.apache.servicecomb + service-center-client + + + + org.apache.commons + commons-lang3 + + + + 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 + test + + + diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/MicroserviceHandler.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/MicroserviceHandler.java new file mode 100644 index 00000000000..7ffa409a089 --- /dev/null +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/MicroserviceHandler.java @@ -0,0 +1,182 @@ +/* + + * Copyright (C) 2020-2022 Huawei Technologies Co., Ltd. All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.sc; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.foundation.common.net.NetUtils; +import org.apache.servicecomb.service.center.client.model.DataCenterInfo; +import org.apache.servicecomb.service.center.client.model.Framework; +import org.apache.servicecomb.service.center.client.model.HealthCheck; +import org.apache.servicecomb.service.center.client.model.HealthCheckMode; +import org.apache.servicecomb.service.center.client.model.Microservice; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstanceStatus; +import org.apache.servicecomb.service.center.client.model.MicroserviceStatus; +import org.springframework.core.env.Environment; + +public class MicroserviceHandler { + private static final String SERVICE_MAPPING = "SERVICE_MAPPING"; + + private static final String VERSION_MAPPING = "VERSION_MAPPING"; + + private static final String APP_MAPPING = "APP_MAPPING"; + + private static final String CAS_APPLICATION_ID = "CAS_APPLICATION_ID"; + + private static final String CAS_COMPONENT_NAME = "CAS_COMPONENT_NAME"; + + private static final String CAS_INSTANCE_VERSION = "CAS_INSTANCE_VERSION"; + + private static final String CAS_INSTANCE_ID = "CAS_INSTANCE_ID"; + + private static final String CAS_ENVIRONMENT_ID = "CAS_ENVIRONMENT_ID"; + + private static final String SERVICE_PROPS = "SERVICECOMB_SERVICE_PROPS"; + + private static final String INSTANCE_PROPS = "SERVICECOMB_INSTANCE_PROPS"; + + public static Microservice createMicroservice( + Environment environment) { + Microservice microservice = new Microservice(); + microservice.setProperties(BootStrapProperties.readServiceProperties(environment)); + if (!StringUtils.isEmpty(environment.getProperty(APP_MAPPING)) && + !StringUtils.isEmpty(environment.getProperty(environment.getProperty(APP_MAPPING)))) { + microservice.setAppId(environment.getProperty(environment.getProperty(APP_MAPPING))); + } else { + microservice.setAppId(BootStrapProperties.readApplication(environment)); + } + if (!StringUtils.isEmpty(environment.getProperty(SERVICE_MAPPING)) && + !StringUtils.isEmpty(environment.getProperty(environment.getProperty(SERVICE_MAPPING)))) { + microservice.setServiceName(environment.getProperty(environment.getProperty(SERVICE_MAPPING))); + } else { + microservice.setServiceName(BootStrapProperties.readServiceName(environment)); + } + if (!StringUtils.isEmpty(environment.getProperty(VERSION_MAPPING)) && + !StringUtils.isEmpty(environment.getProperty(environment.getProperty(VERSION_MAPPING)))) { + microservice.setVersion(environment.getProperty(environment.getProperty(VERSION_MAPPING))); + } else { + microservice.setVersion(BootStrapProperties.readServiceVersion(environment)); + } + microservice.setEnvironment(BootStrapProperties.readServiceEnvironment(environment)); + + Framework framework = createFramework(); + microservice.setFramework(framework); + String[] servicePropArray = ConfigUtil.parseArrayValue(environment.getProperty(SERVICE_PROPS)) + .toArray(new String[0]); + if (servicePropArray.length != 0) { + microservice.getProperties().putAll(parseProps(servicePropArray)); + } + + microservice.setStatus(MicroserviceStatus.UP); + return microservice; + } + + private static Framework createFramework() { + Framework framework = new Framework(); + framework.setName("ServiceComb"); + framework.setVersion(MicroserviceHandler.class.getPackage().getImplementationVersion()); + return framework; + } + + public static MicroserviceInstance createMicroserviceInstance( + Environment environment, + SCConfigurationProperties scConfigurationProperties, + DataCenterProperties dataCenterProperties) { + MicroserviceInstance microserviceInstance = new MicroserviceInstance(); + String hostName = StringUtils.isEmpty(scConfigurationProperties.getHostname()) ? NetUtils.getHostName() + : scConfigurationProperties.getHostname(); + if (hostName.length() > 64) { + hostName = hostName.substring(0, 64); + } + microserviceInstance.setHostName(hostName); + + if (StringUtils.isNotEmpty(dataCenterProperties.getName())) { + DataCenterInfo dataCenterInfo = new DataCenterInfo(); + dataCenterInfo.setName(dataCenterProperties.getName()); + dataCenterInfo.setRegion(dataCenterProperties.getRegion()); + dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone()); + microserviceInstance.setDataCenterInfo(dataCenterInfo); + } + + HealthCheck healthCheck = new HealthCheck(); + healthCheck.setMode(HealthCheckMode.push); + healthCheck.setInterval(scConfigurationProperties.getHealthCheckIntervalInSeconds()); + healthCheck.setTimes(scConfigurationProperties.getHealthCheckTimes()); + microserviceInstance.setHealthCheck(healthCheck); + String currTime = String.valueOf(System.currentTimeMillis()); + microserviceInstance.setTimestamp(currTime); + microserviceInstance.setModTimestamp(currTime); + + // what's MicroserviceInstance doing? same sa Microservice? + if (!StringUtils.isEmpty(environment.getProperty(VERSION_MAPPING)) && + !StringUtils.isEmpty(environment.getProperty(environment.getProperty(VERSION_MAPPING)))) { + microserviceInstance.setVersion(environment.getProperty(environment.getProperty(VERSION_MAPPING))); + } else { + microserviceInstance.setVersion(BootStrapProperties.readServiceVersion(environment)); + } + + Map properties = new HashMap<>(); + properties.putAll(BootStrapProperties.readServiceProperties(environment)); + properties.putAll(genCasProperties(environment)); + microserviceInstance.setProperties(properties); + microserviceInstance.setStatus( + MicroserviceInstanceStatus.valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); + return microserviceInstance; + } + + private static Map genCasProperties(Environment environment) { + Map properties = new HashMap<>(); + if (!StringUtils.isEmpty(environment.getProperty(CAS_APPLICATION_ID))) { + properties.put(CAS_APPLICATION_ID, environment.getProperty(CAS_APPLICATION_ID)); + } + if (!StringUtils.isEmpty(environment.getProperty(CAS_COMPONENT_NAME))) { + properties.put(CAS_COMPONENT_NAME, environment.getProperty(CAS_COMPONENT_NAME)); + } + if (!StringUtils.isEmpty(environment.getProperty(CAS_INSTANCE_VERSION))) { + properties.put(CAS_INSTANCE_VERSION, environment.getProperty(CAS_INSTANCE_VERSION)); + } + if (!StringUtils.isEmpty(environment.getProperty(CAS_INSTANCE_ID))) { + properties.put(CAS_INSTANCE_ID, environment.getProperty(CAS_INSTANCE_ID)); + } + if (!StringUtils.isEmpty(environment.getProperty(CAS_ENVIRONMENT_ID))) { + properties.put(CAS_ENVIRONMENT_ID, environment.getProperty(CAS_ENVIRONMENT_ID)); + } + + String[] instancePropArray = ConfigUtil.parseArrayValue(environment.getProperty(INSTANCE_PROPS)) + .toArray(new String[0]); + if (instancePropArray.length != 0) { + properties.putAll(parseProps(instancePropArray)); + } + + return properties; + } + + private static Map parseProps(String... value) { + return Arrays.stream(value).map(v -> v.split(":")) + .filter(v -> v.length == 2) + .collect(Collectors.toMap(v -> v[0], v -> v[1])); + } +} diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCAddressManager.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCAddressManager.java new file mode 100644 index 00000000000..1c8ec27c8a8 --- /dev/null +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCAddressManager.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.sc; + +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.foundation.common.event.EventManager; +import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; +import org.apache.servicecomb.service.center.client.RegistrationEvents.HeartBeatEvent; +import org.apache.servicecomb.service.center.client.ServiceCenterClient; +import org.apache.servicecomb.service.center.client.exception.OperationException; +import org.apache.servicecomb.service.center.client.model.DataCenterInfo; +import org.apache.servicecomb.service.center.client.model.FindMicroserviceInstancesResponse; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.eventbus.Subscribe; + +/** + * Support auto discovery of service center addresses. + */ +public class SCAddressManager { + public enum Type { + SERVICECENTER, + KIE, + CseConfigCenter, + } + + private static final Logger LOGGER = LoggerFactory.getLogger(SCAddressManager.class); + + private final ServiceCenterClient serviceCenterClient; + + private final SCRegistration scRegistration; + + private final SCConfigurationProperties configurationProperties; + + private final Map> lastEngineEndpointsCache = new HashMap<>(); + + public SCAddressManager(SCConfigurationProperties configurationProperties, + ServiceCenterClient serviceCenterClient, + SCRegistration scRegistration) { + this.configurationProperties = configurationProperties; + this.serviceCenterClient = serviceCenterClient; + this.scRegistration = scRegistration; + scRegistration.getEventBus().register(this); + } + + @Subscribe + public void onHeartBeatEvent(HeartBeatEvent event) { + if (event.isSuccess() && configurationProperties.isAutoDiscovery()) { + for (Type type : Type.values()) { + initEndPort(type.name()); + } + } + } + + private void initEndPort(String key) { + List instances = findServiceInstance("default", key, "0+"); + HashSet currentEngineEndpoints = new HashSet<>(); + Map> zoneAndRegion = generateZoneAndRegionAddress(instances, currentEngineEndpoints); + if (zoneAndRegion == null) { + return; + } + if (isEngineEndpointsChanged(lastEngineEndpointsCache.get(key), currentEngineEndpoints)) { + LOGGER.info("auto discovery service [{}] addresses: [{}]", key, zoneAndRegion); + lastEngineEndpointsCache.put(key, currentEngineEndpoints); + EventManager.post(new RefreshEndpointEvent(zoneAndRegion, key)); + } + } + + private boolean isEngineEndpointsChanged(Set lastEngineEndpoints, Set currentEngineEndpoints) { + if (lastEngineEndpoints == null || lastEngineEndpoints.isEmpty()) { + return true; + } + HashSet compareTemp = new HashSet<>(lastEngineEndpoints); + compareTemp.removeAll(currentEngineEndpoints); + return !compareTemp.isEmpty() || lastEngineEndpoints.size() != currentEngineEndpoints.size(); + } + + private Map> generateZoneAndRegionAddress(List instances, + HashSet currentEngineEndpoints) { + if (instances.isEmpty()) { + return null; + } + + Map> zoneAndRegion = new HashMap<>(); + DataCenterInfo dataCenterInfo = findRegion(instances); + + Set sameZone = new HashSet<>(); + Set sameRegion = new HashSet<>(); + for (MicroserviceInstance microserviceInstance : instances) { + if (regionAndAZMatch(dataCenterInfo, microserviceInstance)) { + sameZone.addAll(microserviceInstance.getEndpoints()); + } else { + sameRegion.addAll(microserviceInstance.getEndpoints()); + } + currentEngineEndpoints.addAll(microserviceInstance.getEndpoints()); + } + zoneAndRegion.put("sameZone", new ArrayList<>(sameZone)); + zoneAndRegion.put("sameRegion", new ArrayList<>(sameRegion)); + return zoneAndRegion; + } + + private DataCenterInfo findRegion(List microserviceInstances) { + for (MicroserviceInstance microserviceInstance : microserviceInstances) { + boolean isMatch = microserviceInstance.getEndpoints().get(0) + .contains(scRegistration.getBackendMicroserviceInstance() + .getEndpoints().get(0)); + if (isMatch && microserviceInstance.getDataCenterInfo() != null) { + return microserviceInstance.getDataCenterInfo(); + } + } + + if (scRegistration.getBackendMicroserviceInstance().getDataCenterInfo() == null) { + return null; + } + return scRegistration.getBackendMicroserviceInstance().getDataCenterInfo(); + } + + private List findServiceInstance(String appId, String serviceName, String versionRule) { + try { + FindMicroserviceInstancesResponse instancesResponse = serviceCenterClient + .findMicroserviceInstance(scRegistration.getBackendMicroserviceInstance().getServiceId(), + appId, serviceName, versionRule, null); + return instancesResponse.getMicroserviceInstancesResponse().getInstances(); + } catch (OperationException operationException) { + LOGGER.warn("not find the Microservice instance of {}", serviceName); + return new ArrayList<>(); + } + } + + private boolean regionAndAZMatch(DataCenterInfo myself, MicroserviceInstance target) { + if (myself == null) { + return true; + } + if (target.getDataCenterInfo() != null) { + return myself.getRegion().equals(target.getDataCenterInfo().getRegion()) && + myself.getAvailableZone().equals(target.getDataCenterInfo().getAvailableZone()); + } + return false; + } +} diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCClientUtils.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCClientUtils.java new file mode 100644 index 00000000000..f9371926a1d --- /dev/null +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCClientUtils.java @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.sc; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +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.config.ConfigUtil; +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.foundation.ssl.SSLCustom; +import org.apache.servicecomb.foundation.ssl.SSLOption; +import org.apache.servicecomb.foundation.ssl.SSLOptionFactory; +import org.apache.servicecomb.foundation.vertx.VertxConst; +import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; +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.service.center.client.ServiceCenterAddressManager; +import org.apache.servicecomb.service.center.client.ServiceCenterClient; +import org.apache.servicecomb.service.center.client.ServiceCenterRawClient; +import org.apache.servicecomb.service.center.client.ServiceCenterWatch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +public class SCClientUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(SCClientUtils.class); + + // Compatible chassis multi-registration center + private static final Map serviceAddressManagers = new ConcurrentHashMap<>(); + + public static ServiceCenterAddressManager createAddressManager(SCConfigurationProperties discoveryProperties, + Environment environment) { + List addresses = ConfigUtil.parseArrayValue(discoveryProperties.getAddress()); + return createAddressManager("default", addresses, environment); + } + + /** + * Ensure that the ServiceCenterAddressManager in the client created for RBAC authentication and registry discovery + * is the same. This ensures that when an error is reported due to the registry center address being unavailable, + * the authentication and registry discovery remain consistent. + * + * @param projectName projectName + * @param addresses engine address + * @param environment environment + * @return Service Center Address Manager + */ + public static ServiceCenterAddressManager createAddressManager(String projectName, List addresses, + Environment environment) { + if (getServiceCenterAddressManager(addresses) == null) { + synchronized (SCClientUtils.class) { + if (getServiceCenterAddressManager(addresses) == null) { + String key = String.join(",", addresses); + LOGGER.info("initialize discovery server={}", addresses); + String region = environment.getProperty("servicecomb.datacenter.region"); + String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); + ServiceCenterAddressManager addressManager = new ServiceCenterAddressManager(projectName, addresses, + EventManager.getEventBus(), region, availableZone); + serviceAddressManagers.put(key, addressManager); + return addressManager; + } + } + } + return getServiceCenterAddressManager(addresses); + } + + private static ServiceCenterAddressManager getServiceCenterAddressManager(List addresses) { + String forwardKey = String.join(",", addresses); + List tempAddr = new ArrayList<>(addresses); + Collections.reverse(tempAddr); + String reverseKey = String.join(",", tempAddr); + if (serviceAddressManagers.get(forwardKey) != null) { + return serviceAddressManagers.get(forwardKey); + } + if (serviceAddressManagers.get(reverseKey) != null) { + return serviceAddressManagers.get(reverseKey); + } + return null; + } + + // add other headers needed for registration by new ServiceCenterClient(...) + public static ServiceCenterClient serviceCenterClient(SCConfigurationProperties discoveryProperties, + Environment environment) { + ServiceCenterAddressManager addressManager = createAddressManager(discoveryProperties, environment); + + SSLProperties sslProperties = buildSslProperties(addressManager, environment); + + return new ServiceCenterClient(new ServiceCenterRawClient.Builder() + .setTenantName("default") + .setAddressManager(addressManager) + .setHttpTransport(createHttpTransport(environment, sslProperties)).build(), addressManager); + } + + private static HttpTransport createHttpTransport(Environment environment, SSLProperties sslProperties) { + List authHeaderProviders = SPIServiceUtils.getOrLoadSortedService(AuthHeaderProvider.class); + + 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, + getRequestAuthHeaderProvider(authHeaderProviders), httpClientBuilder); + } + + return HttpTransportFactory + .createHttpTransport(sslProperties, + getRequestAuthHeaderProvider(authHeaderProviders), HttpTransportFactory.defaultRequestConfig().build()); + } + + 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); + } + + private static SSLProperties buildSslProperties(ServiceCenterAddressManager addressManager, + Environment environment) { + SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory(SCConst.SC_SSL_TAG, + environment); + SSLOption sslOption; + if (factory == null) { + sslOption = SSLOption.build(SCConst.SC_SSL_TAG, environment); + } else { + sslOption = factory.createSSLOption(); + } + SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); + + SSLProperties sslProperties = new SSLProperties(); + sslProperties.setSslCustom(sslCustom); + sslProperties.setSslOption(sslOption); + sslProperties.setEnabled(addressManager.sslEnabled()); + return sslProperties; + } + + public static ServiceCenterWatch serviceCenterWatch(SCConfigurationProperties discoveryProperties, + List authHeaderProviders, Environment environment) { + ServiceCenterAddressManager addressManager = createAddressManager(discoveryProperties, environment); + SSLProperties sslProperties = buildSslProperties(addressManager, environment); + return new ServiceCenterWatch(addressManager, sslProperties, getRequestAuthHeaderProvider(authHeaderProviders), + "default", new HashMap<>(), EventManager.getEventBus()); + } + + 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; + }; + } +} diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfiguration.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfiguration.java new file mode 100644 index 00000000000..6cd8965c8a2 --- /dev/null +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfiguration.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.sc; + +import java.util.List; + +import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; +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 +@SuppressWarnings("unused") +public class SCConfiguration { + @Bean + @ConfigurationProperties(prefix = SCConst.SC_REGISTRY_PREFIX) + public SCConfigurationProperties scConfigurationProperties() { + return new SCConfigurationProperties(); + } + + @Bean + public ServiceCenterClient serviceCenterClient( + @Qualifier("scConfigurationProperties") SCConfigurationProperties scConfigurationProperties, + Environment environment) { + return SCClientUtils.serviceCenterClient(scConfigurationProperties, environment); + } + + @Bean + public ServiceCenterWatch serviceCenterWatch( + @Qualifier("scConfigurationProperties") SCConfigurationProperties scConfigurationProperties, + List authHeaderProviders, Environment environment) { + return SCClientUtils.serviceCenterWatch(scConfigurationProperties, authHeaderProviders, environment); + } + + @Bean + public SCRegistration scRegistration( + @Qualifier("scConfigurationProperties") SCConfigurationProperties scConfigurationProperties, + @Qualifier("serviceCenterClient") ServiceCenterClient serviceCenterClient, + @Qualifier("serviceCenterWatch") ServiceCenterWatch serviceCenterWatch) { + return new SCRegistration(scConfigurationProperties, serviceCenterClient, serviceCenterWatch); + } + + @Bean + public SCDiscovery scDiscovery( + @Qualifier("scConfigurationProperties") SCConfigurationProperties scConfigurationProperties, + @Qualifier("serviceCenterClient") ServiceCenterClient serviceCenterClient) { + return new SCDiscovery(scConfigurationProperties, serviceCenterClient); + } + + @Bean + public SCAddressManager scAddressManager( + @Qualifier("scConfigurationProperties") SCConfigurationProperties scConfigurationProperties, + SCRegistration scRegistration, + @Qualifier("serviceCenterClient") ServiceCenterClient serviceCenterClient) { + return new SCAddressManager(scConfigurationProperties, + serviceCenterClient, scRegistration); + } +} diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfigurationProperties.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfigurationProperties.java new file mode 100644 index 00000000000..5ac431406a3 --- /dev/null +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfigurationProperties.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.sc; + +import org.apache.commons.lang3.StringUtils; + +/** + * Configurations for Service Center registration and discovery. + */ +public class SCConfigurationProperties { + private boolean enabled = true; + + private String address = null; + + private boolean enableSwaggerRegistration = false; + + /** + * for registration service + * when swagger is different between local with remote serviceCenter. if ignoreSwaggerDifferent is true. + * it will ignore the different and continue the program. otherwise, the program will stop. + */ + private boolean ignoreSwaggerDifferent = true; + + private boolean canOverwriteSwagger = true; + + private String hostname; + + private int healthCheckIntervalInSeconds = 15; + + private int healthCheckTimes = 3; + + private int healthCheckRequestTimeoutInMillis = 5000; + + private int pollIntervalInMillis = 15000; + + private boolean autoDiscovery = false; + + private boolean watch = false; + + private long registrationWaitTimeInMillis = 30000; + + public String getAddress() { + if (StringUtils.isEmpty(address)) { + throw new IllegalStateException( + "Address is required in configuration. NOTICE: since 3.0.0, only support " + + SCConst.SC_REGISTRY_PREFIX + ".address to configure service center address."); + } + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isIgnoreSwaggerDifferent() { + return ignoreSwaggerDifferent; + } + + public void setIgnoreSwaggerDifferent(boolean ignoreSwaggerDifferent) { + this.ignoreSwaggerDifferent = ignoreSwaggerDifferent; + } + + public boolean isCanOverwriteSwagger() { + return canOverwriteSwagger; + } + + public void setCanOverwriteSwagger(boolean canOverwriteSwagger) { + this.canOverwriteSwagger = canOverwriteSwagger; + } + + public String getHostname() { + return hostname; + } + + public void setHostname(String hostname) { + this.hostname = hostname; + } + + public int getHealthCheckIntervalInSeconds() { + return healthCheckIntervalInSeconds; + } + + public void setHealthCheckIntervalInSeconds(int healthCheckIntervalInSeconds) { + this.healthCheckIntervalInSeconds = healthCheckIntervalInSeconds; + } + + public int getHealthCheckRequestTimeoutInMillis() { + return healthCheckRequestTimeoutInMillis; + } + + public void setHealthCheckRequestTimeoutInMillis(int healthCheckRequestTimeoutInMillis) { + this.healthCheckRequestTimeoutInMillis = healthCheckRequestTimeoutInMillis; + } + + public int getPollIntervalInMillis() { + return pollIntervalInMillis; + } + + public void setPollIntervalInMillis(int pollIntervalInMillis) { + this.pollIntervalInMillis = pollIntervalInMillis; + } + + public boolean isAutoDiscovery() { + return autoDiscovery; + } + + public void setAutoDiscovery(boolean autoDiscovery) { + this.autoDiscovery = autoDiscovery; + } + + public int getHealthCheckTimes() { + return healthCheckTimes; + } + + public void setHealthCheckTimes(int healthCheckTimes) { + this.healthCheckTimes = healthCheckTimes; + } + + public boolean isWatch() { + return watch; + } + + public void setWatch(boolean watch) { + this.watch = watch; + } + + public long getRegistrationWaitTimeInMillis() { + return registrationWaitTimeInMillis; + } + + public void setRegistrationWaitTimeInMillis(long registrationWaitTimeInMillis) { + this.registrationWaitTimeInMillis = registrationWaitTimeInMillis; + } + + public boolean isEnableSwaggerRegistration() { + return enableSwaggerRegistration; + } + + public void setEnableSwaggerRegistration(boolean enableSwaggerRegistration) { + this.enableSwaggerRegistration = enableSwaggerRegistration; + } +} diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConst.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConst.java new file mode 100644 index 00000000000..612f52c5589 --- /dev/null +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConst.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.registry.sc; + +public class SCConst { + public static final String SC_REGISTRY_NAME = "sc-registry"; + + public static final String SC_REGISTRY_PREFIX = "servicecomb.registry.sc"; + + public static final String SC_SSL_TAG = "sc.consumer"; + + public static final String SC_ALLOW_CROSS_APP = "allowCrossApp"; + + public static final String SC_DEFAULT_PROJECT = "default"; +} diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscovery.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscovery.java new file mode 100644 index 00000000000..8cffa504705 --- /dev/null +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscovery.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.sc; + +import java.util.ArrayList; +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.foundation.common.event.SimpleEventBus; +import org.apache.servicecomb.registry.api.Discovery; +import org.apache.servicecomb.service.center.client.DiscoveryEvents.InstanceChangedEvent; +import org.apache.servicecomb.service.center.client.ServiceCenterClient; +import org.apache.servicecomb.service.center.client.ServiceCenterDiscovery; +import org.apache.servicecomb.service.center.client.ServiceCenterDiscovery.SubscriptionKey; +import org.apache.servicecomb.service.center.client.model.Microservice; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; +import org.apache.servicecomb.service.center.client.model.SchemaInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; + +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; + +public class SCDiscovery implements Discovery { + public static final String SC_DISCOVERY_ENABLED = "servicecomb.registry.sc.%s.%s.enabled"; + + private final EventBus eventBus = new SimpleEventBus(); + + private final SCConfigurationProperties configurationProperties; + + private final ServiceCenterClient serviceCenterClient; + + private SCRegistration scRegistration; + + private ServiceCenterDiscovery serviceCenterDiscovery; + + private InstanceChangedListener instanceChangedListener; + + private Environment environment; + + @Autowired + public SCDiscovery(SCConfigurationProperties configurationProperties, + ServiceCenterClient serviceCenterClient) { + this.configurationProperties = configurationProperties; + this.serviceCenterClient = serviceCenterClient; + } + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + public void setScRegistration(SCRegistration scRegistration) { + this.scRegistration = scRegistration; + } + + @Override + public String name() { + return SCConst.SC_REGISTRY_NAME; + } + + @Override + public int getOrder() { + return -9000; + } + + @Override + public boolean enabled(String application, String serviceName) { + return environment.getProperty(String.format(SC_DISCOVERY_ENABLED, application, serviceName), + boolean.class, true); + } + + @Override + public List findServiceInstances(String application, String serviceName) { + SubscriptionKey subscriptionKey = new SubscriptionKey(application, serviceName); + serviceCenterDiscovery.registerIfNotPresent(subscriptionKey); + List instances = serviceCenterDiscovery.getInstanceCache(subscriptionKey); + + return toDiscoveryInstances(instances); + } + + @Override + public List findServices(String application) { + return serviceCenterClient.getMicroserviceList().getServices().stream() + .filter(e -> e.getAppId().equals(application)).map(Microservice::getServiceName) + .collect(Collectors.toList()); + } + + private List toDiscoveryInstances(List instances) { + if (CollectionUtils.isEmpty(instances)) { + return Collections.emptyList(); + } + + Map schemaResult; + if (configurationProperties.isEnableSwaggerRegistration()) { + List schemas = serviceCenterClient.getServiceSchemasList(instances.get(0).getServiceId(), true); + if (schemas == null) { + schemaResult = Collections.emptyMap(); + } else { + schemaResult = new HashMap<>(schemas.size()); + schemas.forEach(info -> schemaResult.put(info.getSchemaId(), info.getSchema())); + } + } else { + schemaResult = new HashMap<>(); + } + List result = new ArrayList<>(instances.size()); + instances.forEach(instance -> result.add(new SCDiscoveryInstance(instance, schemaResult))); + return result; + } + + @Override + public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { + this.instanceChangedListener = instanceChangedListener; + } + + @Subscribe + @SuppressWarnings("unused") + public void onInstanceChangedEvent(InstanceChangedEvent event) { + this.instanceChangedListener.onInstanceChanged(name(), event.getAppName(), event.getServiceName(), + toDiscoveryInstances(event.getInstances())); + } + + @Override + public void init() { + serviceCenterDiscovery = new ServiceCenterDiscovery(serviceCenterClient, eventBus); + serviceCenterDiscovery.setPollInterval(configurationProperties.getPollIntervalInMillis()); + + eventBus.register(this); + } + + @Override + public void run() { + // SCDiscovery.run is called after SCRegistration.run + serviceCenterDiscovery.updateMyselfServiceId(scRegistration.getBackendMicroservice().getServiceId()); + // startDiscovery will check if already started, can call several times + serviceCenterDiscovery.startDiscovery(); + } + + @Override + public void destroy() { + if (serviceCenterDiscovery != null) { + serviceCenterDiscovery.stop(); + } + } + + @Override + public boolean enabled() { + return this.configurationProperties.isEnabled(); + } +} diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscoveryInstance.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscoveryInstance.java new file mode 100644 index 00000000000..f3b7ffe4403 --- /dev/null +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscoveryInstance.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.registry.sc; + +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.MicroserviceInstanceStatus; +import org.apache.servicecomb.service.center.client.model.Microservice; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; + +public class SCDiscoveryInstance extends AbstractDiscoveryInstance { + private final Microservice microservice; + + private final MicroserviceInstance microserviceInstance; + + private final Map schemas; + + public SCDiscoveryInstance(MicroserviceInstance microserviceInstance, + Map schemas) { + this.microservice = microserviceInstance.getMicroservice(); + this.microserviceInstance = microserviceInstance; + this.schemas = schemas; + } + + @Override + public MicroserviceInstanceStatus getStatus() { + return MicroserviceInstanceStatus.valueOf(microserviceInstance.getStatus().name()); + } + + @Override + public String getRegistryName() { + return SCConst.SC_REGISTRY_NAME; + } + + @Override + public String getEnvironment() { + return microservice.getEnvironment(); + } + + @Override + public String getApplication() { + return microservice.getAppId(); + } + + @Override + public String getServiceName() { + return microservice.getServiceName(); + } + + @Override + public String getAlias() { + return microservice.getAlias(); + } + + @Override + public String getVersion() { + return microservice.getVersion(); + } + + @Override + public DataCenterInfo getDataCenterInfo() { + if (microserviceInstance.getDataCenterInfo() != null) { + return new DataCenterInfo(microserviceInstance.getDataCenterInfo().getName(), + microserviceInstance.getDataCenterInfo().getRegion(), + microserviceInstance.getDataCenterInfo().getAvailableZone()); + } + return new DataCenterInfo(); + } + + @Override + public String getDescription() { + return microservice.getDescription(); + } + + @Override + public Map getProperties() { + return microserviceInstance.getProperties(); + } + + @Override + public Map getSchemas() { + return schemas; + } + + @Override + public List getEndpoints() { + return microserviceInstance.getEndpoints(); + } + + @Override + public String getInstanceId() { + return microserviceInstance.getInstanceId(); + } + + @Override + public String getServiceId() { + return microservice.getServiceId(); + } +} diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistration.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistration.java new file mode 100644 index 00000000000..6692f61c6c7 --- /dev/null +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistration.java @@ -0,0 +1,205 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.sc; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.foundation.common.event.SimpleEventBus; +import org.apache.servicecomb.registry.RegistrationId; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.Registration; +import org.apache.servicecomb.service.center.client.RegistrationEvents.MicroserviceInstanceRegistrationEvent; +import org.apache.servicecomb.service.center.client.ServiceCenterClient; +import org.apache.servicecomb.service.center.client.ServiceCenterRegistration; +import org.apache.servicecomb.service.center.client.ServiceCenterWatch; +import org.apache.servicecomb.service.center.client.model.Microservice; +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 com.google.common.base.Charsets; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import com.google.common.hash.Hashing; + +public class SCRegistration implements Registration { + private final EventBus eventBus = new SimpleEventBus(); + + private Microservice microservice; + + private MicroserviceInstance microserviceInstance; + + private ServiceCenterRegistration serviceCenterRegistration; + + private final ServiceCenterClient serviceCenterClient; + + private final ServiceCenterWatch serviceCenterWatch; + + private final SCConfigurationProperties configurationProperties; + + private SCRegistrationInstance registrationInstance; + + private DataCenterProperties dataCenterProperties; + + private Environment environment; + + private RegistrationId registrationId; + + private CountDownLatch readyWaiter = new CountDownLatch(1); + + @Autowired + public SCRegistration(SCConfigurationProperties configurationProperties, + ServiceCenterClient serviceCenterClient, ServiceCenterWatch serviceCenterWatch) { + this.configurationProperties = configurationProperties; + this.serviceCenterClient = serviceCenterClient; + this.serviceCenterWatch = serviceCenterWatch; + } + + @Autowired + public void setDataCenterProperties(DataCenterProperties dataCenterProperties) { + this.dataCenterProperties = dataCenterProperties; + } + + @Autowired + public void setRegistrationId(RegistrationId registrationId) { + this.registrationId = registrationId; + } + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public void init() { + microservice = MicroserviceHandler.createMicroservice( + environment); + microserviceInstance = MicroserviceHandler.createMicroserviceInstance( + environment, + configurationProperties, + dataCenterProperties); + microserviceInstance.setInstanceId(registrationId.getInstanceId()); + serviceCenterRegistration = new ServiceCenterRegistration(serviceCenterClient, + new ServiceCenterConfiguration().setCanOverwriteSwagger( + this.configurationProperties.isCanOverwriteSwagger()) + .setCanOverwriteSwagger(this.configurationProperties.isCanOverwriteSwagger()), + eventBus); + serviceCenterRegistration.setMicroservice(microservice); + serviceCenterRegistration.setMicroserviceInstance(microserviceInstance); + serviceCenterRegistration.setHeartBeatInterval( + TimeUnit.SECONDS.toMillis(configurationProperties.getHealthCheckIntervalInSeconds())); + serviceCenterRegistration.setHeartBeatRequestTimeout( + configurationProperties.getHealthCheckRequestTimeoutInMillis()); + registrationInstance = new SCRegistrationInstance(microservice, microserviceInstance, serviceCenterRegistration); + eventBus.register(this); + } + + @Override + public void run() { + try { + serviceCenterRegistration.startRegistration(); + if (!readyWaiter.await(configurationProperties.getRegistrationWaitTimeInMillis(), TimeUnit.MILLISECONDS)) { + throw new IllegalStateException( + String.format("registration timeout after %s milli seconds.", + configurationProperties.getRegistrationWaitTimeInMillis())); + } + } catch (InterruptedException e) { + throw new IllegalStateException("registration process is interrupted."); + } + } + + @Subscribe + public void onMicroserviceInstanceRegistrationEvent(MicroserviceInstanceRegistrationEvent event) { + if (!event.isSuccess()) { + return; + } + readyWaiter.countDown(); + if (configurationProperties.isWatch()) { + serviceCenterWatch.startWatch(SCConst.SC_DEFAULT_PROJECT, microservice.getServiceId()); + } + } + + @Override + public void destroy() { + if (serviceCenterRegistration != null) { + serviceCenterRegistration.stop(); + } + } + + @Override + public String name() { + return SCConst.SC_REGISTRY_NAME; + } + + @Override + public SCRegistrationInstance getMicroserviceInstance() { + return registrationInstance; + } + + @Override + public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { + return serviceCenterClient.updateMicroserviceInstanceStatus(microservice.getServiceId(), + microserviceInstance.getInstanceId(), + org.apache.servicecomb.service.center.client.model.MicroserviceInstanceStatus.valueOf(status.name())); + } + + @Override + public void addSchema(String schemaId, String content) { + if (configurationProperties.isEnableSwaggerRegistration()) { + this.microservice.addSchema(schemaId); + + this.serviceCenterRegistration.addSchemaInfo( + new SchemaInfo(schemaId, content, calcSchemaSummary(content))); + } + } + + @SuppressWarnings("UnstableApiUsage") + public static String calcSchemaSummary(String schemaContent) { + return Hashing.sha256().newHasher().putString(schemaContent, Charsets.UTF_8).hash().toString(); + } + + @Override + public void addEndpoint(String endpoint) { + this.microserviceInstance.addEndpoint(endpoint); + } + + @Override + public void addProperty(String key, String value) { + this.microserviceInstance.addProperty(key, value); + } + + @Override + public boolean enabled() { + return this.configurationProperties.isEnabled(); + } + + public Microservice getBackendMicroservice() { + return microservice; + } + + public MicroserviceInstance getBackendMicroserviceInstance() { + return microserviceInstance; + } + + EventBus getEventBus() { + return eventBus; + } +} diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistrationInstance.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistrationInstance.java new file mode 100644 index 00000000000..cfe53692eeb --- /dev/null +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistrationInstance.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.sc; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.RegistrationInstance; +import org.apache.servicecomb.service.center.client.ServiceCenterRegistration; +import org.apache.servicecomb.service.center.client.model.Microservice; +import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; + +public class SCRegistrationInstance implements RegistrationInstance { + private final Microservice microservice; + + private final MicroserviceInstance microserviceInstance; + + private final ServiceCenterRegistration serviceCenterRegistration; + + public SCRegistrationInstance(Microservice microservice, MicroserviceInstance microserviceInstance, + ServiceCenterRegistration serviceCenterRegistration) { + this.microservice = microservice; + this.microserviceInstance = microserviceInstance; + this.serviceCenterRegistration = serviceCenterRegistration; + } + + @Override + public String getEnvironment() { + return microservice.getEnvironment(); + } + + @Override + public String getApplication() { + return microservice.getAppId(); + } + + @Override + public String getServiceName() { + return microservice.getServiceName(); + } + + @Override + public String getAlias() { + return microservice.getAlias(); + } + + @Override + public String getVersion() { + return microservice.getVersion(); + } + + @Override + public DataCenterInfo getDataCenterInfo() { + return new DataCenterInfo(microserviceInstance.getDataCenterInfo().getName(), + microserviceInstance.getDataCenterInfo().getRegion(), + microserviceInstance.getDataCenterInfo().getAvailableZone()); + } + + @Override + public String getDescription() { + return microservice.getDescription(); + } + + @Override + public Map getProperties() { + return microserviceInstance.getProperties(); + } + + @Override + public Map getSchemas() { + Map result = new HashMap<>(); + serviceCenterRegistration.getSchemaInfos().forEach(info -> result.put(info.getSchemaId(), info.getSchema())); + return result; + } + + @Override + public List getEndpoints() { + return microserviceInstance.getEndpoints(); + } + + @Override + public String getInstanceId() { + return microserviceInstance.getInstanceId(); + } + + @Override + public String getServiceId() { + return microservice.getServiceId(); + } + + @Override + public MicroserviceInstanceStatus getStatus() { + return MicroserviceInstanceStatus.valueOf(microserviceInstance.getStatus().name()); + } + + public Microservice getBackendMicroservice() { + return microservice; + } + + public MicroserviceInstance getBackendMicroserviceInstance() { + return microserviceInstance; + } +} diff --git a/service-registry/registry-service-center/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/service-registry/registry-service-center/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..5d260c3aba6 --- /dev/null +++ b/service-registry/registry-service-center/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.sc.SCConfiguration diff --git a/service-registry/registry-zero-config/pom.xml b/service-registry/registry-zero-config/pom.xml new file mode 100644 index 00000000000..c9a6569f1bc --- /dev/null +++ b/service-registry/registry-zero-config/pom.xml @@ -0,0 +1,38 @@ + + + + + + service-registry-parent + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + registry-zero-config + Java Chassis::Service Registry::Zero Config + + + + org.apache.servicecomb + registry-lightweight + + + diff --git a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/AbstractZeroConfigRegistration.java b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/AbstractZeroConfigRegistration.java new file mode 100644 index 00000000000..b6e577e44a2 --- /dev/null +++ b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/AbstractZeroConfigRegistration.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.zeroconfig; + +import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.ORDER; + +import org.apache.servicecomb.registry.api.RegistrationInstance; +import org.apache.servicecomb.registry.lightweight.AbstractLightweightRegistration; +import org.springframework.beans.factory.annotation.Autowired; + +public abstract class AbstractZeroConfigRegistration + extends AbstractLightweightRegistration { + protected Config config; + + @Autowired + public AbstractZeroConfigRegistration setConfig(Config config) { + this.config = config; + return this; + } + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public boolean enabled() { + return config.isEnabled(); + } + + @Override + public void run() { + super.run(); + + startRegister(config.getHeartbeatInterval()); + } +} diff --git a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/Config.java b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/Config.java new file mode 100644 index 00000000000..a68d89793d9 --- /dev/null +++ b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/Config.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.zeroconfig; + +import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.CFG_ENABLED; +import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.CFG_HEARTBEAT_INTERVAL; +import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.CFG_HEARTBEAT_LOST_TIMES; +import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.CFG_MULTICAST_ADDRESS; +import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.CFG_MULTICAST_GROUP; +import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.CFG_PULL_INTERVAL; +import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.DEFAULT_ADDRESS; +import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.DEFAULT_GROUP; +import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.DEFAULT_HEARTBEAT_INTERVAL; +import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.DEFAULT_HEARTBEAT_LOST_TIMES; +import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.DEFAULT_PULL_INTERVAL; + +import java.time.Duration; + +import org.apache.servicecomb.config.DynamicProperties; +import org.springframework.beans.factory.annotation.Autowired; + +public class Config { + private DynamicProperties dynamicProperties; + + @Autowired + public Config setDynamicProperties(DynamicProperties dynamicProperties) { + this.dynamicProperties = dynamicProperties; + return this; + } + + public boolean isEnabled() { + return dynamicProperties.getBooleanProperty(CFG_ENABLED, true); + } + + public String getMulticastAddress() { + return dynamicProperties.getStringProperty(CFG_MULTICAST_ADDRESS, DEFAULT_ADDRESS); + } + + // (224.0.0.0, 239.255.255.255] + public String getMulticastGroup() { + return dynamicProperties.getStringProperty(CFG_MULTICAST_GROUP, DEFAULT_GROUP); + } + + public Duration getHeartbeatInterval() { + String interval = dynamicProperties.getStringProperty(CFG_HEARTBEAT_INTERVAL, DEFAULT_HEARTBEAT_INTERVAL); + return toDuration(interval); + } + + public Duration getCheckDeadInstancesInterval() { + int lostTimes = dynamicProperties.getIntProperty(CFG_HEARTBEAT_LOST_TIMES, DEFAULT_HEARTBEAT_LOST_TIMES); + return getHeartbeatInterval().multipliedBy(lostTimes); + } + + public Duration getPullInterval() { + String interval = dynamicProperties.getStringProperty(CFG_PULL_INTERVAL, DEFAULT_PULL_INTERVAL); + return toDuration(interval); + } + + private Duration toDuration(String interval) { + return Duration.parse("PT" + interval); + } +} diff --git a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigConst.java b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigConst.java new file mode 100644 index 00000000000..8ebcf99dd53 --- /dev/null +++ b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigConst.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.zeroconfig; + +public interface ZeroConfigConst { + String ZERO_CONFIG_REGISTRY_NAME = "zero-config-registry"; + + int MAX_PACKET_SIZE = 1024; + + String PREFIX = "servicecomb.registry.zero-config."; + + String CFG_ENABLED = PREFIX + "enabled"; + + String CFG_MULTICAST_GROUP = PREFIX + "multicast.group"; + + String CFG_MULTICAST_ADDRESS = PREFIX + "multicast.address"; + + String CFG_HEARTBEAT_INTERVAL = PREFIX + "heartbeat.interval"; + + String CFG_HEARTBEAT_LOST_TIMES = PREFIX + "heartbeat.lost-times"; + + String CFG_PULL_INTERVAL = PREFIX + "pull-interval"; + + String DEFAULT_GROUP = "225.6.7.8"; + + String DEFAULT_ADDRESS = "0.0.0.0:6666"; + + String DEFAULT_HEARTBEAT_INTERVAL = "30s"; + + int DEFAULT_HEARTBEAT_LOST_TIMES = 3; + + String DEFAULT_PULL_INTERVAL = "3s"; + + int ORDER = -8000; +} diff --git a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigDiscovery.java b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigDiscovery.java new file mode 100644 index 00000000000..70b54f8a079 --- /dev/null +++ b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigDiscovery.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.zeroconfig; + +import static org.apache.servicecomb.zeroconfig.ZeroConfigConst.ORDER; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.servicecomb.registry.lightweight.AbstractLightweightDiscovery; +import org.apache.servicecomb.registry.lightweight.model.Microservice; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstances; +import org.springframework.beans.factory.annotation.Autowired; + +public class ZeroConfigDiscovery extends AbstractLightweightDiscovery { + private Config config; + + @Autowired + public ZeroConfigDiscovery setConfig(Config config) { + this.config = config; + return this; + } + + @Override + public String name() { + return ZeroConfigConst.ZERO_CONFIG_REGISTRY_NAME; + } + + @Override + public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { + + } + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public boolean enabled() { + return config.isEnabled(); + } + + @Override + public void run() { + + } + + @Override + public List findServiceInstances(String application, String serviceName) { + MicroserviceInstances microserviceInstances = + store.findServiceInstances(application, serviceName, "0"); + if (microserviceInstances.isMicroserviceNotExist() || microserviceInstances.getInstancesResponse() == null) { + return Collections.emptyList(); + } + List result = new ArrayList<>(); + for (MicroserviceInstance instance : microserviceInstances.getInstancesResponse().getInstances()) { + result.add(new ZeroConfigDiscoveryInstance(store.getMicroservice(instance.getServiceId()).get(), instance)); + } + return result; + } + + @Override + public List findServices(String application) { + List microservices = store.getAllMicroservices(); + return microservices.stream().filter(e -> e.getAppId().equals(application)) + .map(Microservice::getServiceName).collect(Collectors.toList()); + } +} diff --git a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigDiscoveryInstance.java b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigDiscoveryInstance.java new file mode 100644 index 00000000000..e42e0ae0dd2 --- /dev/null +++ b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigDiscoveryInstance.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.zeroconfig; + +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.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.lightweight.model.Microservice; +import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; + +public class ZeroConfigDiscoveryInstance extends AbstractDiscoveryInstance { + private final Microservice microservice; + + private final MicroserviceInstance microserviceInstance; + + public ZeroConfigDiscoveryInstance(Microservice microservice, + MicroserviceInstance microserviceInstance) { + this.microservice = microservice; + this.microserviceInstance = microserviceInstance; + } + + @Override + public MicroserviceInstanceStatus getStatus() { + return microserviceInstance.getStatus(); + } + + @Override + public String getRegistryName() { + return ZeroConfigConst.ZERO_CONFIG_REGISTRY_NAME; + } + + @Override + public String getEnvironment() { + return microservice.getEnvironment(); + } + + @Override + public String getApplication() { + return microservice.getAppId(); + } + + @Override + public String getServiceName() { + return microservice.getServiceName(); + } + + @Override + public String getAlias() { + return microservice.getAlias(); + } + + @Override + public String getVersion() { + return microservice.getVersion(); + } + + @Override + public DataCenterInfo getDataCenterInfo() { + return microserviceInstance.getDataCenterInfo(); + } + + @Override + public String getDescription() { + return microservice.getDescription(); + } + + @Override + public Map getProperties() { + return microserviceInstance.getProperties(); + } + + @Override + public Map getSchemas() { + return microservice.getSchemaMap(); + } + + @Override + public List getEndpoints() { + return microserviceInstance.getEndpoints(); + } + + @Override + public String getInstanceId() { + return microserviceInstance.getInstanceId(); + } +} diff --git a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistration.java b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistration.java new file mode 100644 index 00000000000..1b2383e28f5 --- /dev/null +++ b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistration.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.zeroconfig; + +import java.io.IOException; + +import org.apache.servicecomb.registry.lightweight.MessageType; +import org.apache.servicecomb.registry.lightweight.RegisterInstanceEvent; +import org.apache.servicecomb.zeroconfig.multicast.Multicast; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; + +import com.google.common.eventbus.Subscribe; + +@SuppressWarnings("UnstableApiUsage") +public class ZeroConfigRegistration extends AbstractZeroConfigRegistration implements + InitializingBean { + protected Multicast multicast; + + @Autowired + public ZeroConfigRegistration setMulticast(Multicast multicast) { + this.multicast = multicast; + return this; + } + + @Override + public String name() { + return ZeroConfigConst.ZERO_CONFIG_REGISTRY_NAME; + } + + // delete after support @Conditional + @Override + public boolean enabled() { + return true; + } + + @Override + public void afterPropertiesSet() { + eventBus.register(this); + } + + @Override + protected void doSendRegister() throws IOException { + multicast.send(MessageType.REGISTER, self.buildRegisterRequest()); + } + + @Override + protected void doSendUnregister() throws IOException { + multicast.send(MessageType.UNREGISTER, self.buildUnregisterRequest()); + } + + @Override + public ZeroConfigRegistrationInstance getMicroserviceInstance() { + return new ZeroConfigRegistrationInstance(self); + } + + @SuppressWarnings("unused") + @Subscribe + public void onRegisterInstance(RegisterInstanceEvent event) { + if (event.getInstance().getInstanceId().equals(self.getInstance().getInstanceId())) { + return; + } + + sendRegister(); + } +} diff --git a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistrationInstance.java b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistrationInstance.java new file mode 100644 index 00000000000..9e1955b24a8 --- /dev/null +++ b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistrationInstance.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.zeroconfig; + +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.RegistrationInstance; +import org.apache.servicecomb.registry.lightweight.Self; + +public class ZeroConfigRegistrationInstance implements RegistrationInstance { + private final Self self; + + public ZeroConfigRegistrationInstance(Self self) { + this.self = self; + } + + @Override + public String getEnvironment() { + return self.getMicroservice().getEnvironment(); + } + + @Override + public String getApplication() { + return self.getMicroservice().getAppId(); + } + + @Override + public String getServiceName() { + return self.getMicroservice().getServiceName(); + } + + @Override + public String getAlias() { + return self.getMicroservice().getAlias(); + } + + @Override + public String getVersion() { + return self.getMicroservice().getVersion(); + } + + @Override + public DataCenterInfo getDataCenterInfo() { + return self.getInstance().getDataCenterInfo(); + } + + @Override + public String getDescription() { + return self.getMicroservice().getDescription(); + } + + @Override + public Map getProperties() { + return self.getInstance().getProperties(); + } + + @Override + public Map getSchemas() { + return this.self.getMicroservice().getSchemaMap(); + } + + @Override + public List getEndpoints() { + return this.self.getInstance().getEndpoints(); + } + + @Override + public String getInstanceId() { + return this.self.getInstanceId(); + } + + @Override + public MicroserviceInstanceStatus getStatus() { + return MicroserviceInstanceStatus.UP; + } + + public void addSchema(String schemaId, String content) { + this.self.getMicroservice().addSchema(schemaId, content); + } + + public void addEndpoint(String endpoint) { + this.self.getInstance().getEndpoints().add(endpoint); + } + + public void addProperty(String key, String value) { + this.self.getInstance().getProperties().put(key, value); + } +} diff --git a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistryConfiguration.java b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistryConfiguration.java new file mode 100644 index 00000000000..fa2ba2c5d58 --- /dev/null +++ b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistryConfiguration.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.zeroconfig; + +import java.io.IOException; + +import org.apache.servicecomb.registry.lightweight.MessageExecutor; +import org.apache.servicecomb.zeroconfig.multicast.Multicast; +import org.apache.servicecomb.zeroconfig.multicast.MulticastServer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ZeroConfigRegistryConfiguration { + @Bean + public ZeroConfigRegistration zeroConfigRegistration() { + return new ZeroConfigRegistration(); + } + + @Bean + public MulticastServer multicastServer(Config config, Multicast multicast, MessageExecutor messageExecutor) { + return new MulticastServer(config, multicast, messageExecutor); + } + + @Bean + public ZeroConfigDiscovery zeroConfigDiscovery() { + return new ZeroConfigDiscovery(); + } + + @Bean + public Multicast zeroConfigMulticast(Config config) throws IOException { + return new Multicast(config); + } + + @Bean + public Config zeroConfigModel() { + return new Config(); + } +} diff --git a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/multicast/Multicast.java b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/multicast/Multicast.java new file mode 100644 index 00000000000..c0c37f97ebf --- /dev/null +++ b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/multicast/Multicast.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.zeroconfig.multicast; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.MulticastSocket; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.registry.lightweight.Message; +import org.apache.servicecomb.registry.lightweight.MessageType; +import org.apache.servicecomb.zeroconfig.Config; +import org.apache.servicecomb.zeroconfig.ZeroConfigConst; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.net.HostAndPort; + +public class Multicast { + private static final Logger LOGGER = LoggerFactory.getLogger(Multicast.class); + + private final InetSocketAddress bindAddress; + + // (224.0.0.0, 239.255.255.255] + private final InetAddress group; + + private final MulticastSocket multicastSocket; + + private final byte[] recvBuffer = new byte[ZeroConfigConst.MAX_PACKET_SIZE]; + + private final DatagramPacket recvPacket = new DatagramPacket(recvBuffer, recvBuffer.length); + + @SuppressWarnings("deprecation") + public Multicast(Config config) throws IOException { + this.bindAddress = initBindAddress(config); + this.group = initGroup(config); + LOGGER.info("zero config, address: {}", bindAddress); + LOGGER.info("zero config, group: {}", group); + + this.multicastSocket = new MulticastSocket(bindAddress); + this.multicastSocket.joinGroup(group); + this.multicastSocket.setSoTimeout((int) TimeUnit.SECONDS.toMillis(5)); + } + + public Multicast setSendBufferSize(int size) throws SocketException { + multicastSocket.setSendBufferSize(size); + return this; + } + + public Multicast setReceiveBufferSize(int size) throws SocketException { + multicastSocket.setReceiveBufferSize(size); + return this; + } + + @SuppressWarnings("UnstableApiUsage") + private InetSocketAddress initBindAddress(Config config) { + HostAndPort hostAndPort = HostAndPort.fromString(config.getMulticastAddress()); + return new InetSocketAddress(hostAndPort.getHost(), hostAndPort.getPort()); + } + + private InetAddress initGroup(Config config) throws UnknownHostException { + return InetAddress.getByName(config.getMulticastGroup()); + } + + public void send(MessageType type, T body) throws IOException { + byte[] buffer = Message.of(type, body).encode(); + DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, bindAddress.getPort()); + multicastSocket.send(packet); + } + + public Message recv() throws IOException { + multicastSocket.receive(recvPacket); + + return Message.decode(recvPacket.getData(), recvPacket.getLength()); + } +} diff --git a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/multicast/MulticastServer.java b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/multicast/MulticastServer.java new file mode 100644 index 00000000000..f86299173ba --- /dev/null +++ b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/multicast/MulticastServer.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.zeroconfig.multicast; + +import java.net.SocketTimeoutException; +import java.util.concurrent.Executors; + +import org.apache.servicecomb.registry.lightweight.Message; +import org.apache.servicecomb.registry.lightweight.MessageExecutor; +import org.apache.servicecomb.zeroconfig.Config; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SuppressWarnings("unused") +public class MulticastServer { + private static final Logger LOGGER = LoggerFactory.getLogger(MulticastServer.class); + + private final Multicast multicast; + + private final MessageExecutor messageExecutor; + + public MulticastServer(Config config, Multicast multicast, MessageExecutor messageExecutor) { + this.multicast = multicast; + this.messageExecutor = messageExecutor; + + Executors + .newSingleThreadExecutor(runnable -> new Thread(runnable, "multicast-server-recv")) + .execute(this::recv); + messageExecutor.startCheckDeadInstances(config.getCheckDeadInstancesInterval()); + } + + @SuppressWarnings("InfiniteLoopStatement") + private void recv() { + for (; ; ) { + Message message = recvMsg(); + if (message == null) { + continue; + } + + messageExecutor.processMessage(message); + } + } + + private Message recvMsg() { + try { + return multicast.recv(); + } catch (SocketTimeoutException ignore) { + return null; + } catch (Exception e) { + LOGGER.error("failed to receive or decode message.", e); + return null; + } + } +} diff --git a/service-registry/registry-zero-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/service-registry/registry-zero-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..fb2fe90754d --- /dev/null +++ b/service-registry/registry-zero-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.zeroconfig.ZeroConfigRegistryConfiguration diff --git a/service-registry/registry-zookeeper/pom.xml b/service-registry/registry-zookeeper/pom.xml new file mode 100644 index 00000000000..e5ba4236e18 --- /dev/null +++ b/service-registry/registry-zookeeper/pom.xml @@ -0,0 +1,55 @@ + + + + + + org.apache.servicecomb + service-registry-parent + 3.4.0-SNAPSHOT + + 4.0.0 + + registry-zookeeper + Java Chassis::Service Registry::Zookeeper + + + + org.apache.curator + curator-x-discovery + + + org.apache.curator + curator-framework + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + java-chassis-core + + + + diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConfiguration.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConfiguration.java new file mode 100644 index 00000000000..39c9fd176ad --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConfiguration.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.registry.zookeeper; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ZookeeperConfiguration { + @Bean + @ConfigurationProperties(prefix = ZookeeperConst.ZOOKEEPER_REGISTRY_PREFIX) + public ZookeeperRegistryProperties zookeeperRegistryProperties() { + return new ZookeeperRegistryProperties(); + } + + @Bean + public ZookeeperDiscovery zookeeperDiscovery() { + return new ZookeeperDiscovery(); + } + + @Bean + public ZookeeperRegistration zookeeperRegistration() { + return new ZookeeperRegistration(); + } +} diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConst.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConst.java new file mode 100644 index 00000000000..1d6d5d14117 --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConst.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.registry.zookeeper; + +public class ZookeeperConst { + public static final String ZOOKEEPER_REGISTRY_NAME = "zookeeper-registry"; + + public static final String ZOOKEEPER_DISCOVERY_ROOT = "/servicecomb/registry/%s"; + + public static final String ZOOKEEPER_REGISTRY_PREFIX = "servicecomb.registry.zk"; + + public static final String ZOOKEEPER_DISCOVERY_ENABLED = ZOOKEEPER_REGISTRY_PREFIX + ".%s.%s.enabled"; + + public static final String ZOOKEEPER_DEFAULT_ENVIRONMENT = "production"; +} diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscovery.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscovery.java new file mode 100644 index 00000000000..ab601e11783 --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscovery.java @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.zookeeper; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +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.state.ConnectionState; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.curator.utils.CloseableUtils; +import org.apache.curator.x.discovery.ServiceCache; +import org.apache.curator.x.discovery.ServiceDiscovery; +import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; +import org.apache.curator.x.discovery.ServiceInstance; +import org.apache.curator.x.discovery.details.JsonInstanceSerializer; +import org.apache.curator.x.discovery.details.ServiceCacheListener; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.registry.api.Discovery; +import org.apache.zookeeper.server.auth.DigestLoginModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +public class ZookeeperDiscovery implements Discovery { + 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(ZookeeperDiscovery.class); + + private final Map>> serviceDiscoveries = + new ConcurrentHashMapEx<>(); + + private final Map> serviceNameDiscoveries = + new ConcurrentHashMapEx<>(); + + private Environment environment; + + private ZookeeperRegistryProperties zookeeperRegistryProperties; + + private String basePath; + + private CuratorFramework client; + + private InstanceChangedListener instanceChangedListener; + + @Autowired + @SuppressWarnings("unused") + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + @SuppressWarnings("unused") + public void setZookeeperRegistryProperties(ZookeeperRegistryProperties zookeeperRegistryProperties) { + this.zookeeperRegistryProperties = zookeeperRegistryProperties; + } + + @Override + public String name() { + return ZookeeperConst.ZOOKEEPER_REGISTRY_NAME; + } + + @Override + public boolean enabled(String application, String serviceName) { + return environment.getProperty(String.format(ZookeeperConst.ZOOKEEPER_DISCOVERY_ENABLED, application, serviceName), + boolean.class, true); + } + + @Override + public List findServiceInstances(String application, String serviceName) { + try { + ServiceCache discovery = serviceDiscoveries.computeIfAbsent(application, app -> + new ConcurrentHashMapEx<>()).computeIfAbsent(serviceName, name -> { + JsonInstanceSerializer serializer = + new JsonInstanceSerializer<>(ZookeeperInstance.class); + ServiceDiscovery dis = ServiceDiscoveryBuilder.builder(ZookeeperInstance.class) + .client(client) + .basePath(basePath + "/" + application) + .serializer(serializer) + .build(); + ServiceCache cache = + dis.serviceCacheBuilder().name(serviceName).build(); + cache.addListener(new ServiceCacheListener() { + @Override + public void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) { + LOGGER.warn("zookeeper discovery state changed {}", connectionState); + } + + @Override + public void cacheChanged() { + instanceChangedListener.onInstanceChanged(name(), application, serviceName, + toDiscoveryInstances(cache.getInstances())); + } + }); + try { + CountDownLatch latch = cache.startImmediate(); + if (!latch.await(5000, TimeUnit.SECONDS)) { + throw new IllegalStateException("cache start failed."); + } + } catch (Exception e) { + throw new IllegalStateException(e); + } + return cache; + }); + List> instances = discovery.getInstances(); + return toDiscoveryInstances(instances); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + @Override + public List findServices(String application) { + try { + ServiceDiscovery discovery = serviceNameDiscoveries + .computeIfAbsent(application, app -> { + JsonInstanceSerializer serializer = + new JsonInstanceSerializer<>(ZookeeperInstance.class); + ServiceDiscovery dis = ServiceDiscoveryBuilder.builder(ZookeeperInstance.class) + .client(client) + .basePath(basePath + "/" + application) + .serializer(serializer) + .build(); + try { + dis.start(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + return dis; + }); + return discovery.queryForNames().stream().toList(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + private List toDiscoveryInstances( + List> instances) { + return instances.stream().map(instance -> + new ZookeeperDiscoveryInstance(instance.getPayload())).collect(Collectors.toList()); + } + + @Override + public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { + this.instanceChangedListener = instanceChangedListener; + } + + @Override + public void init() { + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = ZookeeperConst.ZOOKEEPER_DEFAULT_ENVIRONMENT; + } + basePath = String.format(ZookeeperConst.ZOOKEEPER_DISCOVERY_ROOT, env); + } + + @Override + public void run() { + CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() + .connectString(zookeeperRegistryProperties.getConnectString()) + .sessionTimeoutMs(zookeeperRegistryProperties.getSessionTimeoutMillis()) + .retryPolicy(new ExponentialBackoffRetry(1000, 3)); + String authSchema = zookeeperRegistryProperties.getAuthenticationSchema(); + if (StringUtils.isNotEmpty(authSchema)) { + if (!"digest".equals(authSchema)) { + throw new IllegalStateException("Not supported schema now. " + authSchema); + } + if (zookeeperRegistryProperties.getAuthenticationInfo() == null) { + throw new IllegalStateException("Auth info can not be empty. "); + } + + String[] authInfo = zookeeperRegistryProperties.getAuthenticationInfo().split(":"); + Configuration.setConfiguration(new ZookeeperSASLConfig(authInfo[0], authInfo[1])); + } + client = builder.build(); + client.start(); + } + + @Override + public void destroy() { + if (client != null) { + CloseableUtils.closeQuietly(client); + } + } + + @Override + public boolean enabled() { + return zookeeperRegistryProperties.isEnabled(); + } +} diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscoveryInstance.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscoveryInstance.java new file mode 100644 index 00000000000..b01ad50830e --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscoveryInstance.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.registry.zookeeper; + +import org.apache.servicecomb.registry.api.DiscoveryInstance; + +public class ZookeeperDiscoveryInstance extends ZookeeperInstance implements DiscoveryInstance { + public ZookeeperDiscoveryInstance(ZookeeperInstance other) { + super(other); + } + + @Override + public String getRegistryName() { + return ZookeeperConst.ZOOKEEPER_REGISTRY_NAME; + } +} diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperInstance.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperInstance.java new file mode 100644 index 00000000000..b919d97855f --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperInstance.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.registry.zookeeper; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstance; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; + +public class ZookeeperInstance implements MicroserviceInstance { + private String serviceId; + + private String instanceId; + + private String environment; + + private String application; + + private String serviceName; + + private String alias; + + private String version; + + private String description; + + private DataCenterInfo dataCenterInfo; + + private List endpoints = new ArrayList<>(); + + private Map schemas = new HashMap<>(); + + private Map properties = new HashMap<>(); + + private MicroserviceInstanceStatus status; + + public ZookeeperInstance() { + + } + + public ZookeeperInstance(ZookeeperInstance other) { + this.serviceId = other.serviceId; + this.instanceId = other.instanceId; + this.environment = other.environment; + this.application = other.application; + this.serviceName = other.serviceName; + this.alias = other.alias; + this.version = other.version; + this.description = other.description; + this.dataCenterInfo = other.dataCenterInfo; + this.endpoints = other.endpoints; + this.schemas = other.schemas; + this.properties = other.properties; + this.status = other.status; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public void setApplication(String application) { + this.application = application; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setDataCenterInfo(DataCenterInfo dataCenterInfo) { + this.dataCenterInfo = dataCenterInfo; + } + + public void setEndpoints(List endpoints) { + this.endpoints = endpoints; + } + + public void setSchemas(Map schemas) { + this.schemas = schemas; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public void setStatus(MicroserviceInstanceStatus status) { + this.status = status; + } + + @Override + public String getEnvironment() { + return this.environment; + } + + @Override + public String getApplication() { + return this.application; + } + + @Override + public String getServiceName() { + return this.serviceName; + } + + @Override + public String getAlias() { + return alias; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public DataCenterInfo getDataCenterInfo() { + return dataCenterInfo == null ? new DataCenterInfo() : dataCenterInfo; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public Map getProperties() { + return properties; + } + + @Override + public Map getSchemas() { + return schemas; + } + + @Override + public List getEndpoints() { + return endpoints; + } + + public void addSchema(String schemaId, String content) { + this.schemas.put(schemaId, content); + } + + public void addEndpoint(String endpoint) { + this.endpoints.add(endpoint); + } + + public void addProperty(String key, String value) { + this.properties.put(key, value); + } + + @Override + public String getInstanceId() { + return instanceId; + } + + @Override + public String getServiceId() { + return serviceId; + } + + @Override + public MicroserviceInstanceStatus getStatus() { + return this.status; + } +} diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistration.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistration.java new file mode 100644 index 00000000000..55df0d42702 --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistration.java @@ -0,0 +1,225 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.zookeeper; + +import java.util.HashMap; +import java.util.Map; + +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.retry.ExponentialBackoffRetry; +import org.apache.curator.utils.CloseableUtils; +import org.apache.curator.x.discovery.ServiceDiscovery; +import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; +import org.apache.curator.x.discovery.ServiceInstance; +import org.apache.curator.x.discovery.details.JsonInstanceSerializer; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.registry.RegistrationId; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.Registration; +import org.apache.zookeeper.server.auth.DigestLoginModule; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +public class ZookeeperRegistration implements Registration { + 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 Environment environment; + + private ZookeeperRegistryProperties zookeeperRegistryProperties; + + private DataCenterProperties dataCenterProperties; + + private String basePath; + + private CuratorFramework client; + + private ServiceInstance instance; + + private RegistrationId registrationId; + + private ServiceDiscovery dis; + + @Autowired + @SuppressWarnings("unused") + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + @SuppressWarnings("unused") + public void setZookeeperRegistryProperties(ZookeeperRegistryProperties zookeeperRegistryProperties) { + this.zookeeperRegistryProperties = zookeeperRegistryProperties; + } + + @Autowired + public void setRegistrationId(RegistrationId registrationId) { + this.registrationId = registrationId; + } + + @Autowired + @SuppressWarnings("unused") + public void setDataCenterProperties(DataCenterProperties dataCenterProperties) { + this.dataCenterProperties = dataCenterProperties; + } + + @Override + public void init() { + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = ZookeeperConst.ZOOKEEPER_DEFAULT_ENVIRONMENT; + } + basePath = String.format(ZookeeperConst.ZOOKEEPER_DISCOVERY_ROOT, env); + ZookeeperInstance zookeeperInstance = new ZookeeperInstance(); + zookeeperInstance.setInstanceId(registrationId.getInstanceId()); + zookeeperInstance.setEnvironment(env); + zookeeperInstance.setApplication(BootStrapProperties.readApplication(environment)); + zookeeperInstance.setServiceName(BootStrapProperties.readServiceName(environment)); + zookeeperInstance.setAlias(BootStrapProperties.readServiceAlias(environment)); + zookeeperInstance.setDescription(BootStrapProperties.readServiceDescription(environment)); + if (StringUtils.isNotEmpty(dataCenterProperties.getName())) { + DataCenterInfo dataCenterInfo = new DataCenterInfo(); + dataCenterInfo.setName(dataCenterProperties.getName()); + dataCenterInfo.setRegion(dataCenterProperties.getRegion()); + dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone()); + zookeeperInstance.setDataCenterInfo(dataCenterInfo); + } + zookeeperInstance.setProperties(BootStrapProperties.readServiceProperties(environment)); + zookeeperInstance.setVersion(BootStrapProperties.readServiceVersion(environment)); + + zookeeperInstance.setStatus( + MicroserviceInstanceStatus.valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); + try { + this.instance = ServiceInstance.builder().name(zookeeperInstance.getServiceName()) + .id(zookeeperInstance.getInstanceId()).payload(zookeeperInstance).build(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + @Override + public void run() { + CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() + .connectString(zookeeperRegistryProperties.getConnectString()) + .sessionTimeoutMs(zookeeperRegistryProperties.getSessionTimeoutMillis()) + .retryPolicy(new ExponentialBackoffRetry(1000, 3)); + String authSchema = zookeeperRegistryProperties.getAuthenticationSchema(); + if (StringUtils.isNotEmpty(authSchema)) { + if (!"digest".equals(authSchema)) { + throw new IllegalStateException("Not supported schema now. " + authSchema); + } + if (zookeeperRegistryProperties.getAuthenticationInfo() == null) { + throw new IllegalStateException("Auth info can not be empty. "); + } + + String[] authInfo = zookeeperRegistryProperties.getAuthenticationInfo().split(":"); + Configuration.setConfiguration(new ZookeeperDiscovery.ZookeeperSASLConfig(authInfo[0], authInfo[1])); + } + client = builder.build(); + client.start(); + JsonInstanceSerializer serializer = + new JsonInstanceSerializer<>(ZookeeperInstance.class); + dis = ServiceDiscoveryBuilder.builder(ZookeeperInstance.class) + .client(client) + .basePath(basePath + "/" + BootStrapProperties.readApplication(environment)) + .serializer(serializer) + .thisInstance(instance) + .build(); + try { + dis.start(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + @Override + public void destroy() { + if (client != null) { + CloseableUtils.closeQuietly(client); + } + } + + @Override + public String name() { + return ZookeeperConst.ZOOKEEPER_REGISTRY_NAME; + } + + @Override + public ZookeeperRegistrationInstance getMicroserviceInstance() { + return new ZookeeperRegistrationInstance(instance.getPayload()); + } + + @Override + public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { + this.instance.getPayload().setStatus(status); + try { + dis.updateService(instance); + } catch (Exception e) { + throw new IllegalStateException(e); + } + return true; + } + + @Override + public void addSchema(String schemaId, String content) { + if (zookeeperRegistryProperties.isEnableSwaggerRegistration()) { + instance.getPayload().addSchema(schemaId, content); + } + } + + @Override + public void addEndpoint(String endpoint) { + instance.getPayload().addEndpoint(endpoint); + } + + @Override + public void addProperty(String key, String value) { + instance.getPayload().addProperty(key, value); + } + + @Override + public boolean enabled() { + return zookeeperRegistryProperties.isEnabled(); + } +} diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistrationInstance.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistrationInstance.java new file mode 100644 index 00000000000..e5caed18960 --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistrationInstance.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.zookeeper; + +import org.apache.servicecomb.registry.api.RegistrationInstance; + +public class ZookeeperRegistrationInstance extends ZookeeperInstance implements RegistrationInstance { + public ZookeeperRegistrationInstance(ZookeeperInstance instance) { + super(instance); + } +} diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistryProperties.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistryProperties.java new file mode 100644 index 00000000000..0f74278fb58 --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistryProperties.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.registry.zookeeper; + +public class ZookeeperRegistryProperties { + private boolean enabled = true; + + private boolean ephemeral = true; + + private String connectString = "127.0.0.1:2181"; + + private String authenticationSchema; + + private String authenticationInfo; + + private int connectionTimeoutMillis = 1000; + + private int sessionTimeoutMillis = 60000; + + private boolean enableSwaggerRegistration = false; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isEphemeral() { + return ephemeral; + } + + public void setEphemeral(boolean ephemeral) { + this.ephemeral = ephemeral; + } + + public String getConnectString() { + return connectString; + } + + public void setConnectString(String connectString) { + this.connectString = connectString; + } + + public int getConnectionTimeoutMillis() { + return connectionTimeoutMillis; + } + + public void setConnectionTimeoutMillis(int connectionTimeoutMillis) { + this.connectionTimeoutMillis = connectionTimeoutMillis; + } + + public int getSessionTimeoutMillis() { + return sessionTimeoutMillis; + } + + public void setSessionTimeoutMillis(int sessionTimeoutMillis) { + this.sessionTimeoutMillis = sessionTimeoutMillis; + } + + public boolean isEnableSwaggerRegistration() { + return enableSwaggerRegistration; + } + + public void setEnableSwaggerRegistration(boolean enableSwaggerRegistration) { + this.enableSwaggerRegistration = enableSwaggerRegistration; + } + + public String getAuthenticationSchema() { + return authenticationSchema; + } + + public void setAuthenticationSchema(String authenticationSchema) { + this.authenticationSchema = authenticationSchema; + } + + public String getAuthenticationInfo() { + return authenticationInfo; + } + + public void setAuthenticationInfo(String authenticationInfo) { + this.authenticationInfo = authenticationInfo; + } +} diff --git a/service-registry/registry-zookeeper/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/service-registry/registry-zookeeper/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..3fd671eac61 --- /dev/null +++ b/service-registry/registry-zookeeper/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.zookeeper.ZookeeperConfiguration diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/Features.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/Features.java deleted file mode 100644 index e5c92480a97..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/Features.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry; - -public class Features { - private boolean canEncodeEndpoint; - - public boolean isCanEncodeEndpoint() { - return canEncodeEndpoint; - } - - public void setCanEncodeEndpoint(boolean canEncodeEndpoint) { - this.canEncodeEndpoint = canEncodeEndpoint; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/RegistryUtils.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/RegistryUtils.java deleted file mode 100644 index 6eae44cc114..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/RegistryUtils.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry; - -import java.net.InetSocketAddress; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Map; - -import org.apache.http.client.utils.URIBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.eventbus.EventBus; -import com.netflix.config.DynamicPropertyFactory; - -import io.servicecomb.config.ConfigUtil; -import io.servicecomb.config.archaius.sources.MicroserviceConfigLoader; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.foundation.common.net.NetUtils; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.cache.InstanceCacheManager; -import io.servicecomb.serviceregistry.cache.InstanceVersionCacheManager; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.servicecomb.serviceregistry.definition.MicroserviceDefinition; -import io.servicecomb.serviceregistry.registry.ServiceRegistryFactory; - -public final class RegistryUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(RegistryUtils.class); - - private static ServiceRegistry serviceRegistry; - - // value is ip or {interface name} - public static final String PUBLISH_ADDRESS = "cse.service.publishAddress"; - - private static final String PUBLISH_PORT = "cse.{transport_name}.publishPort"; - - private RegistryUtils() { - } - - public static void init() { - EventBus eventBus = new EventBus(); - MicroserviceConfigLoader loader = ConfigUtil.getMicroserviceConfigLoader(); - MicroserviceDefinition microserviceDefinition = new MicroserviceDefinition(loader.getConfigModels()); - serviceRegistry = - ServiceRegistryFactory.getOrCreate(eventBus, ServiceRegistryConfig.INSTANCE, microserviceDefinition); - serviceRegistry.init(); - } - - public static void run() { - serviceRegistry.run(); - } - - public static void destory() { - serviceRegistry.destory(); - } - - public static ServiceRegistry getServiceRegistry() { - return serviceRegistry; - } - - public static void setServiceRegistry(ServiceRegistry serviceRegistry) { - RegistryUtils.serviceRegistry = serviceRegistry; - } - - public static ServiceRegistryClient getServiceRegistryClient() { - return serviceRegistry.getServiceRegistryClient(); - } - - public static InstanceCacheManager getInstanceCacheManager() { - return serviceRegistry.getInstanceCacheManager(); - } - - public static InstanceVersionCacheManager getInstanceVersionCacheManager() { - return serviceRegistry.getInstanceVersionCacheManager(); - } - - public static String getAppId() { - return serviceRegistry.getMicroservice().getAppId(); - } - - public static Microservice getMicroservice() { - return serviceRegistry.getMicroservice(); - } - - public static MicroserviceInstance getMicroserviceInstance() { - return serviceRegistry.getMicroserviceInstance(); - } - - 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; - } - - /** - * 对于配置为0.0.0.0的地址,通过查询网卡地址,转换为实际监听的地址。 - */ - 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.getAuthority()); - 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(); - - if (publicAddressSetting.isEmpty()) { - InetSocketAddress socketAddress = ipPort.getSocketAddress(); - String host = socketAddress.getAddress().getHostAddress(); - if (socketAddress.getAddress().isAnyLocalAddress()) { - host = NetUtils.getHostAddress(); - LOGGER.warn("address {}, auto select a host address to publish {}:{}, maybe not the correct one", - socketAddress, - host, - socketAddress.getPort()); - return new IpPort(host, ipPort.getPort()); - } - - return ipPort; - } - - if (publicAddressSetting.startsWith("{") && publicAddressSetting.endsWith("}")) { - publicAddressSetting = NetUtils - .ensureGetInterfaceAddress( - publicAddressSetting.substring(1, publicAddressSetting.length() - 1)) - .getHostAddress(); - } - - String publishPortKey = PUBLISH_PORT.replace("{transport_name}", schema); - int publishPortSetting = DynamicPropertyFactory.getInstance() - .getIntProperty(publishPortKey, 0) - .get(); - int publishPort = publishPortSetting == 0 ? ipPort.getPort() : publishPortSetting; - return new IpPort(publicAddressSetting, publishPort); - } - - public static List findServiceInstance(String appId, String serviceName, - String versionRule) { - return serviceRegistry.findServiceInstance(appId, serviceName, versionRule); - } - - // update microservice instance properties - public static boolean updateInstanceProperties(Map instanceProperties) { - return serviceRegistry.updateInstanceProperties(instanceProperties); - } - - public static Microservice getMicroservice(String microserviceId) { - return serviceRegistry.getRemoteMicroservice(microserviceId); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/ServiceRegistry.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/ServiceRegistry.java deleted file mode 100644 index 4ab944a52b1..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/ServiceRegistry.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.cache.InstanceCacheManager; -import io.servicecomb.serviceregistry.cache.InstanceVersionCacheManager; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; - -public interface ServiceRegistry { - void init(); - - void run(); - - void destory(); - - Set getCombinedMicroserviceNames(); - - Microservice getMicroservice(); - - MicroserviceInstance getMicroserviceInstance(); - - ServiceRegistryClient getServiceRegistryClient(); - - InstanceCacheManager getInstanceCacheManager(); - - InstanceVersionCacheManager getInstanceVersionCacheManager(); - - List findServiceInstance(String appId, String microserviceName, - String microserviceVersionRule); - - boolean updateMicroserviceProperties(Map properties); - - boolean updateInstanceProperties(Map instanceProperties); - - Microservice getRemoteMicroservice(String microserviceId); - - Features getFeatures(); -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/Const.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/Const.java deleted file mode 100644 index 4aa43838c19..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/Const.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api; - -/** - * Created by on 2017/1/9. - */ -public final class Const { - private Const() { - } - - public static final String REGISTRY_APP_ID = "default"; - - public static final String REGISTRY_SERVICE_NAME = "SERVICECENTER"; - - public static final String REGISTRY_VERSION = "3.0.0"; - - public static final String MS_API_PATH = "/registry/v3"; - - public static final String MICROSERVICE_PATH = "/microservices"; - - public static final String SCHEMA_PATH = "/schemas"; - - public static final String INSTANCES_PATH = "/instances"; - - public static final String HEARTBEAT_PATH = "/heartbeat"; - - public static final String EXISTENCE_PATH = "/existence"; - - public static final String WATCHER_PATH = "/watcher"; - - public static final String APP_SERVICE_SEPARATOR = ":"; - - public static final String PATH_CHECKSESSION = "checksession"; - - public static final String PROPERTIES_PATH = "/properties"; - - public static final String URL_PREFIX = "urlPrefix"; -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/MicroserviceKey.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/MicroserviceKey.java deleted file mode 100644 index e7e333bcec1..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/MicroserviceKey.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -/** - * Created by on 2017/1/9. - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class MicroserviceKey { - private String tenant; - - private String appId; - - private String serviceName; - - private String version; - - private String stage; - - public String getTenant() { - return tenant; - } - - public void setTenant(String tenant) { - this.tenant = tenant; - } - - public String getAppId() { - return appId; - } - - public void setAppId(String appId) { - this.appId = appId; - } - - public String getServiceName() { - return serviceName; - } - - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getStage() { - return stage; - } - - public void setStage(String stage) { - this.stage = stage; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/PropertyExtended.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/PropertyExtended.java deleted file mode 100644 index 1a5b5f38c34..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/PropertyExtended.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api; - -import java.util.Map; - -public interface PropertyExtended { - Map getExtendedProperties(); -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/BasePath.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/BasePath.java deleted file mode 100644 index 7430f223d10..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/BasePath.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.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/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/DataCenterInfo.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/DataCenterInfo.java deleted file mode 100644 index 20683cf9a88..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/DataCenterInfo.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.api.registry; - -public class DataCenterInfo { - - 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/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/HealthCheck.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/HealthCheck.java deleted file mode 100644 index 4fd3ac9dfda..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/HealthCheck.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.registry; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -/** - * Created by on 2017/1/13. - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class HealthCheck { - private HealthCheckMode mode; - - private int port; - - private int interval; - - private int times; - - public int getInterval() { - return interval; - } - - public void setInterval(int interval) { - this.interval = interval; - } - - public int getTimes() { - return times; - } - - public void setTimes(int times) { - this.times = times; - } - - public HealthCheckMode getMode() { - return mode; - } - - public void setMode(HealthCheckMode mode) { - this.mode = mode; - } - - public int getTTL() { - if (this.mode != HealthCheckMode.HEARTBEAT) { - return 0; - } - return getInterval() * (getTimes() + 1); - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/HealthCheckMode.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/HealthCheckMode.java deleted file mode 100644 index fd2eb4dbe13..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/HealthCheckMode.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.registry; - -import com.fasterxml.jackson.annotation.JsonValue; - -/** - * Created by on 2017/1/25. - */ -public enum HealthCheckMode { - UNKNOWN("unknow"), - HEARTBEAT("push"), - PLATFORM("pull"); - - private String name; - - HealthCheckMode(String name) { - this.name = name; - } - - @JsonValue - public String getName() { - return name; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/Microservice.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/Microservice.java deleted file mode 100644 index b73d91db079..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/Microservice.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.registry; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -import io.servicecomb.serviceregistry.api.Const; - -/** - * Created by on 2016/12/5. - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class Microservice { - private String serviceId; - - private String appId; - - private String serviceName; - - private String alias; - - private String version; - - private String description; - - private String level; - - private List schemas = new ArrayList<>(); - - @JsonIgnore - private Map schemaMap = new HashMap<>(); - - private List paths = new ArrayList<>(); - - private MicroserviceStatus status = MicroserviceStatus.UP; - - private Map properties = new HashMap<>(); - - @JsonIgnore - private MicroserviceInstance intance; - - public Microservice() { - } - - public MicroserviceInstance getIntance() { - return intance; - } - - public void setIntance(MicroserviceInstance intance) { - this.intance = intance; - } - - public String getServiceId() { - return serviceId; - } - - public void setServiceId(String serviceId) { - this.serviceId = serviceId; - } - - public String getAppId() { - return appId; - } - - public void setAppId(String appId) { - this.appId = appId; - } - - public String getAlias() { - return alias; - } - - public void setAlias(String alias) { - this.alias = alias; - } - - public String getServiceName() { - return serviceName; - } - - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getLevel() { - return level; - } - - public void setLevel(String level) { - this.level = level; - } - - public List getSchemas() { - return schemas; - } - - public void setSchemas(List schemas) { - this.schemas = schemas; - } - - public void addSchema(String schemaId, String content) { - this.schemaMap.put(schemaId, content); - schemas.add(schemaId); - } - - public Map getSchemaMap() { - return schemaMap; - } - - public String getStatus() { - return status.toString(); - } - - public void setStatus(String status) { - this.status = MicroserviceStatus.valueOf(status); - } - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - - public static String generateAbsoluteMicroserviceName(String appId, String microserviceName) { - StringBuilder sb = new StringBuilder(appId.length() + microserviceName.length() + 1); - sb.append(appId).append(Const.APP_SERVICE_SEPARATOR).append(microserviceName); - return sb.toString(); - } - - public List getPaths() { - return paths; - } - - public void setPaths(List paths) { - this.paths = paths; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/MicroserviceFactory.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/MicroserviceFactory.java deleted file mode 100644 index 74c26003032..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/MicroserviceFactory.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.api.registry; - -import java.util.Map; - -import org.apache.commons.configuration.Configuration; - -import io.servicecomb.serviceregistry.config.ConfigurePropertyUtils; -import io.servicecomb.serviceregistry.config.MicroservicePropertiesLoader; -import io.servicecomb.serviceregistry.definition.DefinitionConst; -import io.servicecomb.serviceregistry.definition.MicroserviceDefinition; - -public class MicroserviceFactory { - public Microservice create(String appId, String microserviceName) { - MicroserviceDefinition microserviceDefinition = MicroserviceDefinition.create(appId, microserviceName); - return create(microserviceDefinition); - } - - public Microservice create(MicroserviceDefinition microserviceDefinition) { - Configuration configuration = microserviceDefinition.getConfiguration(); - Microservice microservice = createMicroserviceFromDefinition(configuration); - microservice.setIntance(MicroserviceInstance.createFromDefinition(configuration)); - return microservice; - } - - private Microservice createMicroserviceFromDefinition(Configuration configuration) { - Microservice microservice = new Microservice(); - microservice.setServiceName(configuration.getString(DefinitionConst.qulifiedServiceNameKey, - DefinitionConst.defaultMicroserviceName)); - microservice.setAppId(configuration.getString(DefinitionConst.appIdKey, DefinitionConst.defaultAppId)); - microservice.setVersion(configuration.getString(DefinitionConst.qulifiedServiceVersionKey, - DefinitionConst.defaultVersion)); - microservice.setDescription(configuration.getString(DefinitionConst.qulifiedServiceDescKey, "")); - microservice.setLevel(configuration.getString(DefinitionConst.qulifiedServiceRoleKey, "FRONT")); - microservice.setPaths(ConfigurePropertyUtils.getMicroservicePaths(configuration)); - Map propertiesMap = MicroservicePropertiesLoader.INSTANCE.loadProperties(configuration); - microservice.setProperties(propertiesMap); - - // set alias name when allow cross app - if (allowCrossApp(propertiesMap)) { - microservice.setAlias(Microservice.generateAbsoluteMicroserviceName(microservice.getAppId(), - microservice.getServiceName())); - } - - return microservice; - } - - private boolean allowCrossApp(Map propertiesMap) { - return Boolean.valueOf(propertiesMap.get(DefinitionConst.allowCrossAppKey)); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/MicroserviceInstance.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/MicroserviceInstance.java deleted file mode 100644 index 743cedeade8..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/MicroserviceInstance.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.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.commons.lang.StringUtils; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.netflix.config.DynamicPropertyFactory; - -import io.servicecomb.serviceregistry.config.InstancePropertiesLoader; -import io.servicecomb.serviceregistry.definition.DefinitionConst; - -/** - * 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; - - 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; - - public String getInstanceId() { - return instanceId; - } - - public void setInstanceId(String instanceId) { - this.instanceId = instanceId; - } - - public String getServiceId() { - return serviceId; - } - - public void setServiceId(String serviceId) { - this.serviceId = serviceId; - } - - 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; - } - - public String getStage() { - return stage; - } - - public void setStage(String stage) { - this.stage = stage; - } - - public DataCenterInfo getDataCenterInfo() { - return dataCenterInfo; - } - - 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.defaultStage); - 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("cse.datacenter.name", null) - .get(); - if (StringUtils.isNotEmpty(dataCenterName)) { - DataCenterInfo dataCenterInfo = new DataCenterInfo(); - dataCenterInfo.setName(dataCenterName); - dataCenterInfo - .setRegion(DynamicPropertyFactory.getInstance().getStringProperty("cse.datacenter.region", null).get()); - dataCenterInfo.setAvailableZone( - DynamicPropertyFactory.getInstance().getStringProperty("cse.datacenter.availableZone", null).get()); - microserviceInstance.setDataCenterInfo(dataCenterInfo); - } - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/MicroserviceInstanceStatus.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/MicroserviceInstanceStatus.java deleted file mode 100644 index cc203b70ff3..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/MicroserviceInstanceStatus.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.registry; - -/** - * Created by on 2016/12/5. - */ -public enum MicroserviceInstanceStatus { - UNKNOWN, - UP, - DOWN, - STARTING, - OUTOFSERVICE -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/MicroserviceStatus.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/MicroserviceStatus.java deleted file mode 100644 index dfe40bbc28c..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/MicroserviceStatus.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.registry; - -/** - * Created by on 2016/12/5. - */ -public enum MicroserviceStatus { - UNKNOWN, - UP, - DOWN -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/WatchAction.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/WatchAction.java deleted file mode 100644 index 5de288a3ea1..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/registry/WatchAction.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.registry; - -import com.fasterxml.jackson.annotation.JsonValue; - -/** - * Created by on 2017/2/21. - */ -public enum WatchAction { - CREATE("CREATE"), - UPDATE("UPDATE"), - DELETE("DELETE"); - - private String name; - - WatchAction(String name) { - this.name = name; - } - - @JsonValue - public String getName() { - return name; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/request/CreateSchemaRequest.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/request/CreateSchemaRequest.java deleted file mode 100644 index 4c27b4077ac..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/request/CreateSchemaRequest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.request; - -public class CreateSchemaRequest { - private String schema; - - public String getSchema() { - return schema; - } - - public void setSchema(String schema) { - this.schema = schema; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/request/CreateServiceRequest.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/request/CreateServiceRequest.java deleted file mode 100644 index 2eca107d7ec..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/request/CreateServiceRequest.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.request; - -import io.servicecomb.serviceregistry.api.registry.Microservice; - -/** - * Created by on 2016/12/5. - */ -public class CreateServiceRequest { - private Microservice service; - - public Microservice getService() { - return service; - } - - public void setService(Microservice service) { - this.service = service; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/request/RegisterInstanceRequest.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/request/RegisterInstanceRequest.java deleted file mode 100644 index b74f3286577..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/request/RegisterInstanceRequest.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.request; - -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; - -/** - * Created by on 2016/12/6. - */ -public class RegisterInstanceRequest { - private MicroserviceInstance instance; - - public MicroserviceInstance getInstance() { - return instance; - } - - public void setInstance(MicroserviceInstance instance) { - this.instance = instance; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/request/UpdatePropertiesRequest.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/request/UpdatePropertiesRequest.java deleted file mode 100644 index 9a18b0df56a..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/request/UpdatePropertiesRequest.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.request; - -import java.util.Map; - -/** - * 更新微服务和实例properties的request - */ -public class UpdatePropertiesRequest { - private Map properties; - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/CreateServiceResponse.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/CreateServiceResponse.java deleted file mode 100644 index 3b9aa1b908b..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/CreateServiceResponse.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -/** - * Created by on 2016/12/5. - */ -public class CreateServiceResponse { - private String serviceId; - - public String getServiceId() { - return serviceId; - } - - public void setServiceId(String serviceId) { - this.serviceId = serviceId; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/FindInstancesResponse.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/FindInstancesResponse.java deleted file mode 100644 index 2d7777d6438..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/FindInstancesResponse.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import java.util.List; - -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; - -/** - * Created by on 2016/12/8. - */ -public class FindInstancesResponse { - private List instances; - - public List getInstances() { - return instances; - } - - public void setInstances(List instances) { - this.instances = instances; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetAllServicesResponse.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetAllServicesResponse.java deleted file mode 100644 index 56f89466cd1..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetAllServicesResponse.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import java.util.List; - -import io.servicecomb.serviceregistry.api.registry.Microservice; - -/** - * Created by on 2016/12/8. - */ -public class GetAllServicesResponse { - private List services; - - public List getServices() { - return services; - } - - public void setServices(List services) { - this.services = services; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetExistenceResponse.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetExistenceResponse.java deleted file mode 100644 index 666f28b1a4e..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetExistenceResponse.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -/** - * Created by on 2016/12/8. - */ -public class GetExistenceResponse { - private String serviceId; - - private String schemaId; - - public String getServiceId() { - return serviceId; - } - - public void setServiceId(String serviceId) { - this.serviceId = serviceId; - } - - public String getSchemaId() { - return schemaId; - } - - public void setSchemaId(String schemaId) { - this.schemaId = schemaId; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetInstancesResponse.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetInstancesResponse.java deleted file mode 100644 index a1090b36454..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetInstancesResponse.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import java.util.List; - -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; - -/** - * Created by on 2016/12/6. - */ -public class GetInstancesResponse { - private List instances; - - public List getInstances() { - return instances; - } - - public void setInstances(List instances) { - this.instances = instances; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetSchemaResponse.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetSchemaResponse.java deleted file mode 100644 index 20239243d3a..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetSchemaResponse.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -public class GetSchemaResponse { - private String schema; - - public String getSchema() { - return schema; - } - - public void setSchema(String schema) { - this.schema = schema; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetServiceResponse.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetServiceResponse.java deleted file mode 100644 index 2296725959b..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/GetServiceResponse.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import io.servicecomb.serviceregistry.api.registry.Microservice; - -/** - * Created by on 2016/12/8. - */ -public class GetServiceResponse { - private Microservice service; - - public Microservice getService() { - return service; - } - - public void setService(Microservice service) { - this.service = service; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/HeartbeatResponse.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/HeartbeatResponse.java deleted file mode 100644 index 093dc149e7b..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/HeartbeatResponse.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -/** - * Created by on 2017/3/13. - */ -public class HeartbeatResponse { - private boolean ok; - - private String message; - - public boolean isOk() { - return ok; - } - - public void setOk(boolean ok) { - this.ok = ok; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/MicroserviceInstanceChangedEvent.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/MicroserviceInstanceChangedEvent.java deleted file mode 100644 index ad30311b6b1..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/MicroserviceInstanceChangedEvent.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import io.servicecomb.serviceregistry.api.MicroserviceKey; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.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/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/RegisterInstanceResponse.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/RegisterInstanceResponse.java deleted file mode 100644 index dd8bda0f3bf..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/RegisterInstanceResponse.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -/** - * Created by on 2016/12/6. - */ -public class RegisterInstanceResponse { - private String instanceId; - - public String getInstanceId() { - return instanceId; - } - - public void setInstanceId(String instanceId) { - this.instanceId = instanceId; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/CacheEndpoint.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/CacheEndpoint.java deleted file mode 100644 index 37f6a4cce2f..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/CacheEndpoint.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.cache; - -import io.servicecomb.serviceregistry.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/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/InstanceCache.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/InstanceCache.java deleted file mode 100644 index 480d4dcf287..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/InstanceCache.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.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.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstanceStatus; - -/** - * 缓存指定微服务的所有实例 - * 当实例状态变化时,需要重新创建InstanceCache,由外部控制 - */ -public class InstanceCache { - private static final Logger LOGGER = LoggerFactory.getLogger(InstanceCache.class); - - // 引入这个原子变量,是为了避免一个服务长期不用,缓存被删除,下次再初始化的初始版本号,有极小概率等于被删除前的版本号的问题 - // 如果相等,可能会导致其他没感知删除的模块,在更新流程上,遇到问题 - private static final AtomicInteger VERSION = new AtomicInteger(); - - private int cacheVersion; - - private String appId; - - private String microserviceName; - - // 1.0或1.0+或latest等等,只是规则,不一定表示一个确定的版本 - private String microserviceVersionRule; - - // key为instanceId - private Map instanceMap; - - // 缓存CacheEndpoint - private volatile Map> transportMap; - - private 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; - } - - 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()) { - // 过滤到不可用实例 - if (instance.getStatus() != MicroserviceInstanceStatus.UP) { - continue; - } - for (String endpoint : instance.getEndpoints()) { - try { - URI uri = URI.create(endpoint); - String transportName = uri.getScheme(); - - List cacheEndpointList = transportMap.get(transportName); - if (cacheEndpointList == null) { - cacheEndpointList = new ArrayList<>(); - transportMap.put(transportName, cacheEndpointList); - } - 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/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/InstanceCacheManager.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/InstanceCacheManager.java deleted file mode 100644 index 02ecb1e520d..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/InstanceCacheManager.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.cache; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; - -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.api.response.MicroserviceInstanceChangedEvent; - -/** - * Created by on 2017/2/21. - */ -public class InstanceCacheManager { - private ServiceRegistry serviceRegistry; - - // key为appId/microserviceName - protected Map cacheMap = new ConcurrentHashMap<>(); - - private final Object lockObj = new Object(); - - public InstanceCacheManager(EventBus eventBus, ServiceRegistry serviceRegistry) { - this.serviceRegistry = serviceRegistry; - eventBus.register(this); - } - - private static String getKey(String appId, String microserviceName) { - if (microserviceName.contains(Const.APP_SERVICE_SEPARATOR)) { - return microserviceName.replace(Const.APP_SERVICE_SEPARATOR, "/"); - } - - StringBuilder sb = new StringBuilder(appId.length() + microserviceName.length() + 1); - sb.append(appId).append("/").append(microserviceName); - return sb.toString(); - } - - private InstanceCache create(String appId, String microserviceName, String microserviceVersionRule) { - List instances = - serviceRegistry.findServiceInstance(appId, microserviceName, microserviceVersionRule); - if (instances == null) { - return null; - } - - Map instMap = new HashMap<>(); - for (MicroserviceInstance instance : instances) { - instMap.put(instance.getInstanceId(), instance); - } - - InstanceCache instCache = new InstanceCache(appId, microserviceName, microserviceVersionRule, instMap); - String key = getKey(appId, microserviceName); - cacheMap.put(key, instCache); - return instCache; - } - - public InstanceCache getOrCreate(String appId, String microserviceName, String microserviceVersionRule) { - String key = getKey(appId, microserviceName); - InstanceCache cache = cacheMap.get(key); - if (cache == null) { - synchronized (lockObj) { - cache = cacheMap.get(key); - if (cache == null) { - cache = create(appId, microserviceName, microserviceVersionRule); - } - } - } - return cache; - } - - @Subscribe - public void onInstanceUpdate(MicroserviceInstanceChangedEvent changedEvent) { - String appId = changedEvent.getKey().getAppId(); - String microserviceName = changedEvent.getKey().getServiceName(); - String version = changedEvent.getKey().getVersion(); - String key = getKey(appId, microserviceName); - - synchronized (lockObj) { - InstanceCache instCache = cacheMap.get(key); - if (instCache == null) { - // 场景1:当重连成功SC时,缓存会清空 - // 场景2:运行过程中,外部条件依赖关系 - // 等下次lb再重新获取最新实例信息 - return; - } - Map instMap = instCache.getInstanceMap(); - - switch (changedEvent.getAction()) { - case CREATE: - case UPDATE: - instMap.put(changedEvent.getInstance().getInstanceId(), changedEvent.getInstance()); - break; - case DELETE: - instMap.remove(changedEvent.getInstance().getInstanceId()); - break; - default: - return; - } - cacheMap.put(key, new InstanceCache(appId, microserviceName, version, instMap)); - } - } - - public void cleanUp() { - synchronized (lockObj) { - cacheMap.clear(); - } - } - - public void updateInstanceMap(String appId, String microserviceName, String microserviceVersionRule, - Map instanceMap) { - InstanceCache cache = new InstanceCache(appId, microserviceName, microserviceVersionRule, instanceMap); - String key = getKey(appId, microserviceName); - cacheMap.put(key, cache); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/InstanceVersionCacheManager.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/InstanceVersionCacheManager.java deleted file mode 100644 index 173bbc115ea..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/InstanceVersionCacheManager.java +++ /dev/null @@ -1,199 +0,0 @@ -package io.servicecomb.serviceregistry.cache; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; - -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.api.response.MicroserviceInstanceChangedEvent; - -public class InstanceVersionCacheManager { - private EventBus eventBus; - - private ServiceRegistry serviceRegistry; - - //所有service版本的Instance缓存 - // init from ALL_VERSION_RULE - // but save by real version, eg: 0.0.1/0.0.2/0.0.3...... - protected Map>> cacheAllMap = new ConcurrentHashMap<>(); - - //versionrule instance缓存 - // key if versionRule, not real version - protected Map>> cacheVRuleMap = new ConcurrentHashMap<>(); - - private static final String ALL_VERSION_RULE = "0+"; - - private static final String MICROSERVICE_DEFAULT_VERSION = "microservice.default.version"; - - private static final Object LOCKOBJECT = new Object(); - - public InstanceVersionCacheManager(EventBus eventBus, ServiceRegistry serviceRegistry) { - this.eventBus = eventBus; - this.serviceRegistry = serviceRegistry; - - this.eventBus.register(this); - } - - private static String getKey(String appId, String microserviceName) { - if (microserviceName.contains(Const.APP_SERVICE_SEPARATOR)) { - return microserviceName.replace(Const.APP_SERVICE_SEPARATOR, "/"); - } - - StringBuilder sb = new StringBuilder(appId.length() + microserviceName.length() + 1); - sb.append(appId).append("/").append(microserviceName); - return sb.toString(); - } - - private Map create(String appId, String microserviceName, - String microserviceVersionRule) { - List instances = - serviceRegistry.findServiceInstance(appId, microserviceName, microserviceVersionRule); - if (instances == null) { - return null; - } - - Map instMap = new HashMap<>(); - for (MicroserviceInstance instance : instances) { - instMap.put(instance.getInstanceId(), instance); - } - return instMap; - } - - public Map getOrCreateAllMap(String appId, String microserviceName, - String microserviceVersion) { - Map> cache = getOrCreateAllMap(appId, microserviceName); - return cache.get(microserviceVersion); - } - - public Map> getOrCreateAllMap(String appId, String microserviceName) { - String key = getKey(appId, microserviceName); - Map> cache = cacheAllMap.get(key); - if (cache == null) { - synchronized (LOCKOBJECT) { - cache = cacheAllMap.get(key); - if (cache == null) { - Map cacheAllInstance = - create(appId, microserviceName, ALL_VERSION_RULE); - cache = createCacheVersionMap(cacheAllInstance); - cacheAllMap.put(key, cache); - } - } - } - return cache; - } - - private Map> createCacheVersionMap( - Map cacheAllInstance) { - Map> cacheVersionMap = - new HashMap>(); - for (Map.Entry ins : cacheAllInstance.entrySet()) { - String microserviceId = ins.getValue().getServiceId(); - Microservice microservice = serviceRegistry.getRemoteMicroservice(microserviceId); - String version = microservice.getVersion(); - if (version == null || "".equals(version)) { - version = MICROSERVICE_DEFAULT_VERSION; - } - if (cacheVersionMap.get(version) == null) { - Map newInsMap = new HashMap(); - newInsMap.put(ins.getKey(), ins.getValue()); - cacheVersionMap.put(version, newInsMap); - } else { - Map insMap = cacheVersionMap.get(version); - insMap.put(ins.getKey(), ins.getValue()); - } - } - - return cacheVersionMap; - } - - public Map> getOrCreateVRuleMap(String appId, String microserviceName, - String microserviceVersionRule) { - String key = getKey(appId, microserviceName); - Map> cache = cacheVRuleMap.get(key); - if (cache == null) { - synchronized (LOCKOBJECT) { - cache = cacheVRuleMap.get(key); - if (cache == null) { - Map cacheVersionRuleInstance = - create(appId, microserviceName, microserviceVersionRule); - cache = createCacheVersionMap(cacheVersionRuleInstance); - cacheVRuleMap.put(key, cache); - } - } - } - return cache; - } - - @Subscribe - public void onInstanceUpdate(MicroserviceInstanceChangedEvent changedEvent) { - String appId = changedEvent.getKey().getAppId(); - String microserviceName = changedEvent.getKey().getServiceName(); - String version = changedEvent.getKey().getVersion(); - String key = getKey(appId, microserviceName); - - synchronized (LOCKOBJECT) { - - Map> allCache = cacheAllMap.get(key); - Map> vRuleCache = cacheAllMap.get(key); - String instanceId = changedEvent.getInstance().getInstanceId(); - switch (changedEvent.getAction()) { - case CREATE: - case UPDATE: - - MicroserviceInstance newIns = changedEvent.getInstance(); - if (allCache != null) { - if (allCache.get(version) == null) { - Map newInsMap = new HashMap(); - newInsMap.put(instanceId, newIns); - allCache.put(version, newInsMap); - } else { - Map insMap = allCache.get(version); - insMap.put(instanceId, newIns); - } - } - if (vRuleCache != null) { - if (vRuleCache.get(version) == null) { - Map newInsMap = new HashMap(); - newInsMap.put(instanceId, newIns); - vRuleCache.put(version, newInsMap); - } else { - Map insMap = vRuleCache.get(version); - insMap.put(instanceId, newIns); - } - } - break; - - case DELETE: - if (allCache != null) { - if (allCache.get(version) != null) { - Map insMap = allCache.get(version); - insMap.remove(instanceId); - } - } - if (vRuleCache != null) { - if (vRuleCache.get(version) != null) { - Map insMap = vRuleCache.get(version); - insMap.remove(instanceId); - } - } - break; - default: - return; - } - } - } - - public void cleanUp() { - synchronized (LOCKOBJECT) { - cacheVRuleMap.clear(); - cacheAllMap.clear(); - } - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/ClientException.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/ClientException.java deleted file mode 100644 index 28e844e31ba..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/ClientException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client; - -/** - * Created by on 2017/3/12. - */ -public class ClientException extends Exception { - - private static final long serialVersionUID = 4579811019552389524L; - - public ClientException(String message) { - super(message); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/Endpoints.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/Endpoints.java deleted file mode 100644 index f3f5488f60d..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/Endpoints.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client; - -import java.util.List; - -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; - -/** - * Created by on 2016/12/27. - */ -public class Endpoints { - private String appId; - - private String serviceName; - - private String version; - - private List instances; - - public String getServiceName() { - return serviceName; - } - - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - public List getInstances() { - return instances; - } - - public void setInstances(List instances) { - this.instances = instances; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getAppId() { - return appId; - } - - public void setAppId(String appId) { - this.appId = appId; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/IpPortManager.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/IpPortManager.java deleted file mode 100644 index f8701df54a1..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/IpPortManager.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client; - -import static io.servicecomb.serviceregistry.api.Const.REGISTRY_APP_ID; -import static io.servicecomb.serviceregistry.api.Const.REGISTRY_SERVICE_NAME; -import static io.servicecomb.serviceregistry.api.Const.REGISTRY_VERSION; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.serviceregistry.cache.CacheEndpoint; -import io.servicecomb.serviceregistry.cache.InstanceCache; -import io.servicecomb.serviceregistry.cache.InstanceCacheManager; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.servicecomb.serviceregistry.utils.Timer; -import io.servicecomb.serviceregistry.utils.TimerException; - -/** - * Created by on 2017/1/9. - */ -public class IpPortManager { - private static final Logger LOGGER = LoggerFactory.getLogger(IpPortManager.class); - - private ServiceRegistryConfig serviceRegistryConfig; - - private InstanceCacheManager instanceCacheManager; - - private String defaultTransport = "rest"; - - private volatile ArrayList defaultIpPort; - - private volatile InstanceCache instanceCache = null; - - private volatile AtomicInteger indexForDefault = new AtomicInteger(); - - private AtomicInteger indexForAuto = new AtomicInteger(); - - private Map addressCanUsed = new ConcurrentHashMap<>(); - - private final Object lockObj = new Object(); - - public IpPortManager(ServiceRegistryConfig serviceRegistryConfig, InstanceCacheManager instanceCacheManager) { - this.serviceRegistryConfig = serviceRegistryConfig; - this.instanceCacheManager = instanceCacheManager; - - try { - // 初始化client发现SR的动态集群扩容能力 - if (serviceRegistryConfig.isRegistryAutoDiscovery()) { - createServiceRegistryCache(); - } - } catch (TimerException e) { - // already write log in createServiceRegistryCache - } - } - - public ArrayList getDefaultIpPortList() { - if (defaultIpPort == null) { - synchronized (lockObj) { - if (defaultIpPort == null) { - defaultTransport = serviceRegistryConfig.getTransport(); - defaultIpPort = serviceRegistryConfig.getIpPort(); - } - } - } - return defaultIpPort; - } - - public void createServiceRegistryCache() throws TimerException { - if (instanceCache != null) { - LOGGER.warn("already cache service registry addresses"); - return; - } - // 绑定微服务与SR的依赖,同时建立cache - Timer timer = Timer.newForeverTimer(); - while (true) { - instanceCache = instanceCacheManager.getOrCreate(REGISTRY_APP_ID, - REGISTRY_SERVICE_NAME, - REGISTRY_VERSION); - if (instanceCache != null) { - break; - } - LOGGER.error("create service registry {}/{}/{} instance caches failed", - REGISTRY_APP_ID, - REGISTRY_SERVICE_NAME, - REGISTRY_VERSION); - - timer.sleep(); - } - LOGGER.info("create service registry {}/{}/{} instance caches successfully", - REGISTRY_APP_ID, - REGISTRY_SERVICE_NAME, - REGISTRY_VERSION); - } - - public IpPort getDefaultIpPort() { - List addresses = getDefaultIpPortList(); - if (addresses == null || addresses.size() == 0) { - LOGGER.warn("not exist any service center address"); - return null; - } - int id = indexForDefault.get(); - return addresses.get(id); - } - - public IpPort nextDefaultIpPort() { - List addresses = getDefaultIpPortList(); - if (addresses == null || addresses.size() == 0) { - LOGGER.warn("not exist any service center address"); - return null; - } - synchronized (lockObj) { - //轮询一遍结束,暂不考虑并发场景 - int id = indexForDefault.get(); - if (id == addresses.size() - 1) { - indexForDefault.set(0); - LOGGER.warn("service center has no available instance"); - return null; - } - - indexForDefault.getAndIncrement(); - LOGGER.info("service center address {}:{} is unreachable, retry another address {}:{}", - addresses.get(id).getHostOrIp(), - addresses.get(id).getPort(), - addresses.get(indexForDefault.get()).getHostOrIp(), - addresses.get(indexForDefault.get()).getPort()); - return addresses.get(indexForDefault.get()); - } - } - - public IpPort get() { - List addresses = getAddressCaches(); - if (addresses == null || addresses.size() == 0) { - return getDefaultIpPort(); - } - - synchronized (lockObj) { - int id = indexForAuto.get(); - addressCanUsed.putIfAbsent(id, true); - return new URIEndpointObject(addresses.get(id).getEndpoint()); - } - } - - public IpPort next() { - List addresses = getAddressCaches(); - if ((addresses == null || addresses.size() == 0)) { - return nextDefaultIpPort(); - } - - synchronized (lockObj) { - int id = indexForAuto.get(); - // 重置可用的地址为false - if (addressCanUsed.get(id) != null && addressCanUsed.get(id)) { - addressCanUsed.put(id, false); - if (id == addresses.size() - 1) { - indexForAuto.set(0); - addressCanUsed.clear(); // 重新轮询 - LOGGER.warn("service center has no available instance"); - return null; - } else { - indexForAuto.getAndIncrement(); - } - - LOGGER.warn("service center instance {} is unreachable, try another instance {}", - addresses.get(id).getEndpoint(), - addresses.get(indexForAuto.get()).getEndpoint()); - } - return new URIEndpointObject(addresses.get(indexForAuto.get()).getEndpoint()); - } - } - - private List getAddressCaches() { - if (instanceCache == null) { - return null; - } - InstanceCache newCache = instanceCacheManager.getOrCreate(REGISTRY_APP_ID, - REGISTRY_SERVICE_NAME, - REGISTRY_VERSION); - if (instanceCache == null || instanceCache.cacheChanged(newCache)) { - synchronized (lockObj) { - if (instanceCache == null || instanceCache.cacheChanged(newCache)) { - indexForAuto.set(0); - addressCanUsed.clear(); - instanceCache = newCache; - } - } - } - - return instanceCache == null ? null : instanceCache.getOrCreateTransportMap().get(defaultTransport); - } - - public void clearInstanceCache() { - synchronized (lockObj) { - instanceCache = null; - } - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java deleted file mode 100644 index 4cd327fe394..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; -import org.yaml.snakeyaml.Yaml; - -import io.servicecomb.foundation.vertx.AsyncResultCallback; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.api.response.HeartbeatResponse; -import io.servicecomb.serviceregistry.api.response.MicroserviceInstanceChangedEvent; -import io.servicecomb.serviceregistry.definition.DefinitionConst; - -public class LocalServiceRegistryClientImpl implements ServiceRegistryClient { - private static final Logger LOGGER = LoggerFactory.getLogger(LocalServiceRegistryClientImpl.class); - - public static final String LOCAL_REGISTRY_FILE_KEY = "local.registry.file"; - - private final String LOCAL_REGISTRY_FILE = System.getProperty(LOCAL_REGISTRY_FILE_KEY); - - // key is microservice id - private Map microserviceIdMap = new ConcurrentHashMap<>(); - - // first key is microservice id - // second key is instance id - private Map> microserviceInstanceMap = new ConcurrentHashMap<>(); - - public LocalServiceRegistryClientImpl() { - if (StringUtils.isEmpty(LOCAL_REGISTRY_FILE)) { - LOGGER.info("create empty local registry."); - return; - } - - try (InputStream is = new FileInputStream(new File(LOCAL_REGISTRY_FILE))) { - initFromData(is); - } catch (IOException e) { - LOGGER.error("can not load local registry file:" + LOCAL_REGISTRY_FILE, e); - } - } - - public LocalServiceRegistryClientImpl(InputStream is) { - initFromData(is); - } - - public LocalServiceRegistryClientImpl(Map data) { - initFromData(data); - } - - private void initFromData(InputStream is) { - Yaml yaml = new Yaml(); - @SuppressWarnings("unchecked") - Map data = yaml.loadAs(is, Map.class); - initFromData(data); - } - - private void initFromData(Map data) { - for (Entry entry : data.entrySet()) { - String name = entry.getKey(); - @SuppressWarnings("unchecked") - List> serviceConfigs = (List>) entry.getValue(); - for (Map serviceConfig : serviceConfigs) { - @SuppressWarnings("unchecked") - List> instancesConfig = - (List>) serviceConfig.get("instances"); - - String appId = (String) serviceConfig.get("appid"); - String version = (String) serviceConfig.get("version"); - String serviceId = (String) serviceConfig.get("id"); - - Microservice microservice = new Microservice(); - microservice.setAppId(appId == null ? DefinitionConst.defaultAppId : appId); - microservice.setServiceName(name); - microservice.setVersion(version); - microservice.setServiceId(serviceId == null ? UUID.randomUUID().toString() : serviceId); - microserviceIdMap.put(microservice.getServiceId(), microservice); - - Map instanceMap = new ConcurrentHashMap<>(); - for (Map instanceConfig : instancesConfig) { - @SuppressWarnings("unchecked") - List endpoints = (List) instanceConfig.get("endpoints"); - - MicroserviceInstance instance = new MicroserviceInstance(); - instance.setInstanceId(UUID.randomUUID().toString()); - instance.setEndpoints(endpoints); - instance.setServiceId(microservice.getServiceId()); - - instanceMap.put(instance.getInstanceId(), instance); - } - microserviceInstanceMap.put(microservice.getServiceId(), instanceMap); - } - } - } - - @Override - public void init() { - - } - - @Override - public List getAllMicroservices() { - return new ArrayList<>(microserviceIdMap.values()); - } - - @Override - public String getMicroserviceId(String appId, String microserviceName, String version) { - for (Entry entry : microserviceIdMap.entrySet()) { - Microservice microservice = entry.getValue(); - // ignore version, because local will not use multiple version now. - if (microservice.getAppId().equals(appId) && microservice.getServiceName().equals(microserviceName)) { - return entry.getKey(); - } - } - - return null; - } - - @Override - public String registerMicroservice(Microservice microservice) { - String serviceId = - microservice.getServiceId() == null ? UUID.randomUUID().toString() : microservice.getServiceId(); - microserviceIdMap.put(serviceId, microservice); - - Map instanceMap = microserviceInstanceMap.get(serviceId); - if (instanceMap == null) { - microserviceInstanceMap.put(serviceId, new ConcurrentHashMap<>()); - } - return serviceId; - } - - @Override - public Microservice getMicroservice(String microserviceId) { - return microserviceIdMap.get(microserviceId); - } - - @Override - public String registerMicroserviceInstance(MicroserviceInstance instance) { - Map instanceMap = microserviceInstanceMap.get(instance.getServiceId()); - if (instanceMap == null) { - throw new IllegalArgumentException("Invalid serviceId of instance, serviceId=" + instance.getServiceId()); - } - - String instanceId = - instance.getInstanceId() == null ? UUID.randomUUID().toString() : instance.getInstanceId(); - instanceMap.put(instanceId, instance); - return instanceId; - } - - @Override - public List getMicroserviceInstance(String consumerId, String providerId) { - Map instanceMap = microserviceInstanceMap.get(providerId); - if (instanceMap == null) { - throw new IllegalArgumentException("Invalid serviceId, serviceId=" + providerId); - } - - return new ArrayList<>(instanceMap.values()); - } - - @Override - public boolean unregisterMicroserviceInstance(String microserviceId, String microserviceInstanceId) { - Map instanceMap = microserviceInstanceMap.get(microserviceId); - if (instanceMap != null) { - instanceMap.remove(microserviceInstanceId); - } - return true; - } - - @Override - public HeartbeatResponse heartbeat(String microserviceId, String microserviceInstanceId) { - HeartbeatResponse response = new HeartbeatResponse(); - response.setMessage("OK"); - response.setOk(true); - return response; - } - - @Override - public void watch(String selfMicroserviceId, AsyncResultCallback callback) { - watch(selfMicroserviceId, callback, v -> { - }, v -> { - }); - } - - @Override - public void watch(String selfMicroserviceId, AsyncResultCallback callback, - AsyncResultCallback onOpen, AsyncResultCallback onClose) { - - } - - @Override - public List findServiceInstance(String selfMicroserviceId, String appId, String serviceName, - String versionRule) { - String microserviceId = getMicroserviceId(appId, serviceName, versionRule); - if (StringUtils.isEmpty(microserviceId)) { - return Collections.emptyList(); - } - - return new ArrayList<>(microserviceInstanceMap.get(microserviceId).values()); - } - - @Override - public boolean isSchemaExist(String microserviceId, String schemaId) { - Microservice microservice = microserviceIdMap.get(microserviceId); - if (microservice == null) { - throw new IllegalArgumentException("Invalid serviceId, serviceId=" + microserviceId); - } - - return microservice.getSchemaMap().containsKey(schemaId); - } - - @Override - public boolean registerSchema(String microserviceId, String schemaId, String schemaContent) { - return true; - } - - @Override - public String getSchema(String microserviceId, String schemaId) { - Microservice microservice = microserviceIdMap.get(microserviceId); - if (microservice == null) { - throw new IllegalArgumentException("Invalid serviceId, serviceId=" + microserviceId); - } - - return microservice.getSchemaMap().get(schemaId); - } - - @Override - public boolean updateMicroserviceProperties(String microserviceId, Map serviceProperties) { - Microservice microservice = microserviceIdMap.get(microserviceId); - if (microservice == null) { - throw new IllegalArgumentException("Invalid serviceId, serviceId=" + microserviceId); - } - - if (serviceProperties != null) { - microservice.getProperties().putAll(serviceProperties); - } - return true; - } - - @Override - public boolean updateInstanceProperties(String microserviceId, String microserviceInstanceId, - Map instanceProperties) { - Map instanceMap = microserviceInstanceMap.get(microserviceId); - if (instanceMap == null) { - throw new IllegalArgumentException("Invalid serviceId, serviceId=" + microserviceId); - } - - MicroserviceInstance microserviceInstance = instanceMap.get(microserviceInstanceId); - if (microserviceInstance == null) { - throw new IllegalArgumentException( - String.format("Invalid argument. microserviceId=%s, microserviceInstanceId=%s.", - microserviceId, - microserviceInstanceId)); - } - - if (instanceProperties != null) { - microserviceInstance.getProperties().putAll(instanceProperties); - } - return true; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/ServiceRegistryClient.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/ServiceRegistryClient.java deleted file mode 100644 index c67be3dc727..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/ServiceRegistryClient.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client; - -import java.util.List; -import java.util.Map; - -import io.servicecomb.foundation.vertx.AsyncResultCallback; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.api.response.HeartbeatResponse; -import io.servicecomb.serviceregistry.api.response.MicroserviceInstanceChangedEvent; - -public interface ServiceRegistryClient { - void init(); - - /** - * get all microservices - */ - List getAllMicroservices(); - - /** - * - * 获取微服务唯一标识 - */ - String getMicroserviceId(String appId, String microserviceName, String versionRule); - - /** - * - * 注册微服务静态信息 - */ - String registerMicroservice(Microservice microservice); - - /** - * - * 根据微服务唯一标识查询微服务静态信息 - */ - Microservice getMicroservice(String microserviceId); - - /** - * 更新微服务properties - */ - boolean updateMicroserviceProperties(String microserviceId, Map serviceProperties); - - /** - * - * 判定schema是否已经注册 - */ - boolean isSchemaExist(String microserviceId, String schemaId); - - /** - * - * 注册schema - */ - boolean registerSchema(String microserviceId, String schemaId, String schemaContent); - - /** - * - * 获取schema内容 - */ - String getSchema(String microserviceId, String schemaId); - - /** - * - * 注册微服务实例 - */ - String registerMicroserviceInstance(MicroserviceInstance instance); - - /** - * - * 根据多个微服务唯一标识查询所有微服务实例信息 - */ - List getMicroserviceInstance(String consumerId, String providerId); - - /** - * 更新微服务实例properties - */ - boolean updateInstanceProperties(String microserviceId, String microserviceInstanceId, - Map instanceProperties); - - /** - * - * 去注册微服务实例 - */ - boolean unregisterMicroserviceInstance(String microserviceId, String microserviceInstanceId); - - /** - * - * 服务端返回失败,表示需要重新注册,重新watch - */ - HeartbeatResponse heartbeat(String microserviceId, String microserviceInstanceId); - - /** - * - * watch实例变化 - */ - void watch(String selfMicroserviceId, AsyncResultCallback callback); - - /** - * - * watch实例变化 - */ - void watch(String selfMicroserviceId, AsyncResultCallback callback, - AsyncResultCallback onOpen, AsyncResultCallback onClose); - - /** - * - * 按照app+interface+version查询实例endpoints信息 - */ - List findServiceInstance(String consumerId, String appId, String serviceName, - String versionRule); -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/AbstractClientPool.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/AbstractClientPool.java deleted file mode 100644 index 68d4d672217..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/AbstractClientPool.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.foundation.ssl.SSLCustom; -import io.servicecomb.foundation.ssl.SSLOption; -import io.servicecomb.foundation.ssl.SSLOptionFactory; -import io.servicecomb.foundation.vertx.VertxTLSBuilder; -import io.servicecomb.foundation.vertx.VertxUtils; -import io.servicecomb.foundation.vertx.client.ClientPoolManager; -import io.servicecomb.foundation.vertx.client.http.HttpClientVerticle; -import io.servicecomb.foundation.vertx.client.http.HttpClientWithContext; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.vertx.core.DeploymentOptions; -import io.vertx.core.Vertx; -import io.vertx.core.http.HttpClientOptions; - -/** - * Created by on 2017/4/28. - */ -public abstract class AbstractClientPool implements ClientPool { - - private static final Logger LOGGER = LoggerFactory.getLogger(AbstractClientPool.class); - - private ClientPoolManager clientMgr = new ClientPoolManager<>(); - - private static final String SSL_KEY = "sc.consumer"; - - public AbstractClientPool() { - create(); - } - - public HttpClientWithContext getClient() { - return this.clientMgr.findThreadBindClientPool(); - } - - public void create() { - // 这里面是同步接口,且好像直接在事件线程中用,保险起见,先使用独立的vertx实例 - Vertx vertx = VertxUtils.getOrCreateVertxByName("registry", null); - HttpClientOptions httpClientOptions = createHttpClientOptions(); - DeploymentOptions deployOptions = - VertxUtils.createClientDeployOptions(this.clientMgr, - ServiceRegistryConfig.INSTANCE.getWorkerPoolSize(), - 1, - httpClientOptions); - try { - VertxUtils.blockDeploy(vertx, HttpClientVerticle.class, deployOptions); - } catch (InterruptedException e) { - LOGGER.error("deploy a registry verticle failed, {}", e.getMessage()); - } - } - - protected void buildSecureClientOptions(HttpClientOptions httpClientOptions) { - SSLOptionFactory factory = - SSLOptionFactory.createSSLOptionFactory(SSL_KEY, null); - SSLOption sslOption; - if (factory == null) { - sslOption = SSLOption.buildFromYaml(SSL_KEY); - } else { - sslOption = factory.createSSLOption(); - } - SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); - VertxTLSBuilder.buildHttpClientOptions(sslOption, sslCustom, httpClientOptions); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/ClientPool.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/ClientPool.java deleted file mode 100644 index ad598614d1b..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/ClientPool.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import io.servicecomb.foundation.vertx.client.http.HttpClientWithContext; -import io.vertx.core.http.HttpClientOptions; - -/** - * Created by on 2017/4/28. - */ -public interface ClientPool { - void create(); - - HttpClientOptions createHttpClientOptions(); - - HttpClientWithContext getClient(); -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/EmptyAuthHeaderProvider.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/EmptyAuthHeaderProvider.java deleted file mode 100644 index dab19631760..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/EmptyAuthHeaderProvider.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import java.util.HashMap; -import java.util.Map; - -import io.servicecomb.foundation.auth.AuthHeaderProvider; - -public class EmptyAuthHeaderProvider implements AuthHeaderProvider { - @Override - public Map authHeaders() { - return new HashMap<>(0); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/HttpClientPool.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/HttpClientPool.java deleted file mode 100644 index 89c113abcbe..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/HttpClientPool.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.http.HttpVersion; - -/** - * Created by on 2017/4/28. - */ -public final class HttpClientPool extends AbstractClientPool { - - private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientPool.class); - - public static final HttpClientPool INSTANCE = new HttpClientPool(); - - private HttpClientPool() { - } - - @Override - public HttpClientOptions createHttpClientOptions() { - HttpVersion ver = ServiceRegistryConfig.INSTANCE.getHttpVersion(); - HttpClientOptions httpClientOptions = new HttpClientOptions(); - httpClientOptions.setProtocolVersion(ver); - httpClientOptions.setConnectTimeout(ServiceRegistryConfig.INSTANCE.getConnectionTimeout()); - httpClientOptions.setIdleTimeout(ServiceRegistryConfig.INSTANCE.getIdleConnectionTimeout()); - if (ver == HttpVersion.HTTP_2) { - LOGGER.debug("service center client protocol version is HTTP/2"); - httpClientOptions.setHttp2ClearTextUpgrade(false); - } - if (ServiceRegistryConfig.INSTANCE.isSsl()) { - LOGGER.debug("service center client performs requests over TLS"); - buildSecureClientOptions(httpClientOptions); - } - return httpClientOptions; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/RequestContext.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/RequestContext.java deleted file mode 100644 index ab0d8a4e244..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/RequestContext.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import io.servicecomb.foundation.common.net.IpPort; -import io.vertx.core.http.HttpMethod; - -/** - * Created by on 2017/1/9. - */ -public class RequestContext { - private IpPort ipPort; - - private String uri; - - private HttpMethod method; - - private RequestParam params; - - public IpPort getIpPort() { - return ipPort; - } - - public void setIpPort(IpPort ipPort) { - this.ipPort = ipPort; - } - - public String getUri() { - return uri; - } - - public void setUri(String uri) { - this.uri = uri; - } - - public HttpMethod getMethod() { - return method; - } - - public void setMethod(HttpMethod method) { - this.method = method; - } - - public RequestParam getParams() { - return params; - } - - public void setParams(RequestParam params) { - this.params = params; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/RequestParam.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/RequestParam.java deleted file mode 100644 index d8d4039a95c..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/RequestParam.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Created by on 2016/12/25. - */ -public class RequestParam { - private static final Logger LOGGER = LoggerFactory.getLogger(RequestParam.class); - - private Map queryParams; - - private Map formFields; - - private byte[] body = null; - - private Map headers; - - private Map cookies; - - public Map getCookies() { - return cookies; - } - - public RequestParam setCookies(Map cookies) { - this.cookies = cookies; - return this; - } - - public Map getHeaders() { - return headers; - } - - public RequestParam setHeaders(Map headers) { - this.headers = headers; - return this; - } - - public byte[] getBody() { - return body; - } - - public RequestParam setBody(byte[] body) { - this.body = body; - return this; - } - - public Map getFormFields() { - return formFields; - } - - public RequestParam setFormFields(Map formFields) { - this.formFields = formFields; - return this; - } - - public String getQueryParams() { - if (queryParams == null || queryParams.size() == 0) { - return ""; - } - StringBuilder stringBuilder = new StringBuilder(); - try { - for (Map.Entry query : queryParams.entrySet()) { - for (String val : query.getValue()) { - stringBuilder.append("&") - .append(URLEncoder.encode(query.getKey(), "UTF-8")); - - if (val != null && !val.isEmpty()) { - stringBuilder.append("=") - .append(URLEncoder.encode(val, "UTF-8")); - } - } - } - } catch (UnsupportedEncodingException e) { - LOGGER.error("get query params failed.", e); - return ""; - } - return stringBuilder.substring(1); - } - - public RequestParam setQueryParams(Map queryParams) { - this.queryParams = queryParams; - return this; - } - - public RequestParam addQueryParam(String key, String value) { - if (queryParams == null) { - queryParams = new HashMap<>(); - } - if (!queryParams.containsKey(key)) { - queryParams.put(key, new String[] {value}); - } else { - queryParams.put(key, (String[]) Arrays.asList(queryParams.get(key), value).toArray()); - } - return this; - } - - public RequestParam addHeader(String key, String value) { - if (headers == null) { - headers = new HashMap<>(); - } - headers.put(key, value); - return this; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/RestResponse.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/RestResponse.java deleted file mode 100644 index 4f212a300e3..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/RestResponse.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import io.vertx.core.http.HttpClientResponse; - -/** - * Created by on 2017/1/9. - */ -public class RestResponse { - - private RequestContext requestContext; - - private HttpClientResponse response; - - public RestResponse(RequestContext requestContext, HttpClientResponse response) { - this.requestContext = requestContext; - this.response = response; - } - - public RequestContext getRequestContext() { - return requestContext; - } - - public void setRequestContext(RequestContext requestContext) { - this.requestContext = requestContext; - } - - public HttpClientResponse getResponse() { - return response; - } - - public void setResponse(HttpClientResponse response) { - this.response = response; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/RestUtils.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/RestUtils.java deleted file mode 100644 index 810184814ac..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/RestUtils.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import java.util.HashMap; -import java.util.Map; -import java.util.ServiceLoader; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.foundation.auth.AuthHeaderProvider; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.foundation.vertx.client.http.HttpClientWithContext; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.vertx.core.Handler; -import io.vertx.core.MultiMap; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.CaseInsensitiveHeaders; -import io.vertx.core.http.HttpClientRequest; -import io.vertx.core.http.HttpMethod; - -final class RestUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(RestUtils.class); - - private static final String HEADER_CONTENT_TYPE = "Content-Type"; - - private static final String HEADER_USER_AGENT = "User-Agent"; - - private static final String HEADER_TENANT_NAME = "x-domain-name"; - - private static final ServiceLoader authHeaderProviders = - ServiceLoader.load(AuthHeaderProvider.class); - - private RestUtils() { - } - - public static void httpDo(RequestContext requestContext, Handler responseHandler) { - HttpClientWithContext vertxHttpClient = HttpClientPool.INSTANCE.getClient(); - vertxHttpClient.runOnContext(httpClient -> { - IpPort ipPort = requestContext.getIpPort(); - HttpMethod httpMethod = requestContext.getMethod(); - RequestParam requestParam = requestContext.getParams(); - - if (ipPort == null) { - LOGGER.error("request address is null"); - responseHandler.handle(new RestResponse(requestContext, null)); - return; - } - - // query params - StringBuilder url = new StringBuilder(requestContext.getUri()); - String queryParams = requestParam.getQueryParams(); - if (!queryParams.isEmpty()) { - url.append(url.lastIndexOf("?") > 0 ? "&" : "?") - .append(queryParams); - } - - HttpClientRequest httpClientRequest = httpClient - .request(httpMethod, ipPort.getPort(), ipPort.getHostOrIp(), url.toString(), response -> { - responseHandler.handle(new RestResponse(requestContext, response)); - }); - - httpClientRequest.setTimeout(ServiceRegistryConfig.INSTANCE.getRequestTimeout()) - .exceptionHandler(e -> { - LOGGER.error("{} {} fail, endpoint is {}:{}, message: {}", - httpMethod, - url.toString(), - ipPort.getHostOrIp(), - ipPort.getPort(), - e.getMessage()); - responseHandler.handle(new RestResponse(requestContext, null)); - }); - - // headers - addDefaultHeaders(httpClientRequest); - - if (requestParam.getHeaders() != null && requestParam.getHeaders().size() > 0) { - for (Map.Entry header : requestParam.getHeaders().entrySet()) { - httpClientRequest.putHeader(header.getKey(), header.getValue()); - } - } - - // cookies header - if (requestParam.getCookies() != null && requestParam.getCookies().size() > 0) { - StringBuilder stringBuilder = new StringBuilder(); - for (Map.Entry cookie : requestParam.getCookies().entrySet()) { - stringBuilder.append(cookie.getKey()) - .append("=") - .append(cookie.getValue()) - .append("; "); - } - httpClientRequest.putHeader("Cookie", stringBuilder.toString()); - } - - // body - if (httpMethod != HttpMethod.GET && requestParam.getBody() != null && requestParam.getBody().length > 0) { - httpClientRequest.end(Buffer.buffer(requestParam.getBody())); - } else { - httpClientRequest.end(); - } - }); - } - - public static RequestContext createRequestContext(HttpMethod method, IpPort ipPort, String uri, - RequestParam requestParam) { - RequestContext requestContext = new RequestContext(); - requestContext.setMethod(method); - requestContext.setIpPort(ipPort); - requestContext.setUri(uri); - requestContext.setParams(requestParam); - return requestContext; - } - - public static void addDefaultHeaders(HttpClientRequest request) { - request.headers().addAll(getDefaultHeaders()); - } - - public static MultiMap getDefaultHeaders() { - // add token header - return new CaseInsensitiveHeaders().add(HEADER_CONTENT_TYPE, "application/json") - .add(HEADER_USER_AGENT, "cse-serviceregistry-client/1.0.0") - .add(HEADER_TENANT_NAME, ServiceRegistryConfig.INSTANCE.getTenantName()) - .addAll(authHeaders()); - } - - public static void get(IpPort ipPort, String uri, RequestParam requestParam, - Handler responseHandler) { - httpDo(createRequestContext(HttpMethod.GET, ipPort, uri, requestParam), responseHandler); - } - - public static void post(IpPort ipPort, String uri, RequestParam requestParam, - Handler responseHandler) { - httpDo(createRequestContext(HttpMethod.POST, ipPort, uri, requestParam), responseHandler); - } - - public static void put(IpPort ipPort, String uri, RequestParam requestParam, - Handler responseHandler) { - httpDo(createRequestContext(HttpMethod.PUT, ipPort, uri, requestParam), responseHandler); - } - - public static void delete(IpPort ipPort, String uri, RequestParam requestParam, - Handler responseHandler) { - httpDo(createRequestContext(HttpMethod.DELETE, ipPort, uri, requestParam), responseHandler); - } - - private static Map authHeaders() { - Map headers = new HashMap<>(); - authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders())); - return headers; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java deleted file mode 100644 index e9c5179f622..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java +++ /dev/null @@ -1,694 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import static io.servicecomb.serviceregistry.api.Const.EXISTENCE_PATH; -import static io.servicecomb.serviceregistry.api.Const.HEARTBEAT_PATH; -import static io.servicecomb.serviceregistry.api.Const.INSTANCES_PATH; -import static io.servicecomb.serviceregistry.api.Const.MICROSERVICE_PATH; -import static io.servicecomb.serviceregistry.api.Const.MS_API_PATH; -import static io.servicecomb.serviceregistry.api.Const.PROPERTIES_PATH; -import static io.servicecomb.serviceregistry.api.Const.SCHEMA_PATH; -import static io.servicecomb.serviceregistry.api.Const.WATCHER_PATH; -import static java.util.Collections.emptyList; - -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; - -import javax.ws.rs.core.Response.Status; -import javax.xml.ws.Holder; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.foundation.common.utils.JsonUtils; -import io.servicecomb.foundation.vertx.AsyncResultCallback; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.api.request.CreateSchemaRequest; -import io.servicecomb.serviceregistry.api.request.CreateServiceRequest; -import io.servicecomb.serviceregistry.api.request.RegisterInstanceRequest; -import io.servicecomb.serviceregistry.api.request.UpdatePropertiesRequest; -import io.servicecomb.serviceregistry.api.response.CreateServiceResponse; -import io.servicecomb.serviceregistry.api.response.FindInstancesResponse; -import io.servicecomb.serviceregistry.api.response.GetAllServicesResponse; -import io.servicecomb.serviceregistry.api.response.GetExistenceResponse; -import io.servicecomb.serviceregistry.api.response.GetInstancesResponse; -import io.servicecomb.serviceregistry.api.response.GetSchemaResponse; -import io.servicecomb.serviceregistry.api.response.GetServiceResponse; -import io.servicecomb.serviceregistry.api.response.HeartbeatResponse; -import io.servicecomb.serviceregistry.api.response.MicroserviceInstanceChangedEvent; -import io.servicecomb.serviceregistry.api.response.RegisterInstanceResponse; -import io.servicecomb.serviceregistry.client.ClientException; -import io.servicecomb.serviceregistry.client.IpPortManager; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.vertx.core.Handler; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpClientResponse; - -public final class ServiceRegistryClientImpl implements ServiceRegistryClient { - private static final Logger LOGGER = LoggerFactory.getLogger(ServiceRegistryClientImpl.class); - - private IpPortManager ipPortManager; - - // key是本进程的微服务id和服务管理中心的id - // extract this, ServiceRegistryClient is better to be no status. - private Map watchServices = new ConcurrentHashMap<>(); - - public ServiceRegistryClientImpl(IpPortManager ipPortManager) { - this.ipPortManager = ipPortManager; - } - - @Override - public void init() { - } - - private boolean retry(RequestContext requestContext, Handler responseHandler) { - IpPort ipPort = ipPortManager.next(); - if (ipPort == null) { - return false; - } - requestContext.setIpPort(ipPortManager.get()); - RestUtils.httpDo(requestContext, responseHandler); - return true; - } - - @SuppressWarnings("unchecked") - private Handler syncHandler(CountDownLatch countDownLatch, Class cls, - Holder holder) { - return restResponse -> { - RequestContext requestContext = restResponse.getRequestContext(); - HttpClientResponse response = restResponse.getResponse(); - if (response == null) { - // 请求失败,触发请求SC的其他实例 - if (!retry(requestContext, syncHandler(countDownLatch, cls, holder))) { - countDownLatch.countDown(); - } - return; - } - response.bodyHandler( - bodyBuffer -> { - if (cls.getName().equals(HttpClientResponse.class.getName())) { - holder.value = (T) response; - countDownLatch.countDown(); - return; - } - try { - holder.value = - JsonUtils.readValue(bodyBuffer.getBytes(), cls); - } catch (Exception e) { - LOGGER.warn(bodyBuffer.toString()); - } - countDownLatch.countDown(); - }); - }; - } - - static class ResponseWrapper { - HttpClientResponse response; - - Buffer bodyBuffer; - } - - // temporary copy from syncHandler - // we will use swagger invocation to replace RestUtils later. - private Handler syncHandlerEx(CountDownLatch countDownLatch, Holder holder) { - return restResponse -> { - RequestContext requestContext = restResponse.getRequestContext(); - HttpClientResponse response = restResponse.getResponse(); - if (response == null) { - // invoke failed, call another SC instance - if (!retry(requestContext, syncHandlerEx(countDownLatch, holder))) { - countDownLatch.countDown(); - } - return; - } - - response.bodyHandler(bodyBuffer -> { - ResponseWrapper responseWrapper = new ResponseWrapper(); - responseWrapper.response = response; - responseWrapper.bodyBuffer = bodyBuffer; - holder.value = responseWrapper; - countDownLatch.countDown(); - }); - }; - } - - @Override - public List getAllMicroservices() { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.get(ipPort, - MS_API_PATH + MICROSERVICE_PATH, - new RequestParam(), - syncHandler(countDownLatch, GetAllServicesResponse.class, holder)); - try { - countDownLatch.await(); - if (holder.value != null) { - return holder.value.getServices(); - } - } catch (Exception e) { - LOGGER.error("query all microservices failed", e); - } - return emptyList(); - } - - @Override - public String getMicroserviceId(String appId, String microserviceName, String versionRule) { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.get(ipPort, - MS_API_PATH + EXISTENCE_PATH, - new RequestParam().addQueryParam("type", "microservice") - .addQueryParam("appId", appId) - .addQueryParam("serviceName", microserviceName) - .addQueryParam("version", versionRule), - syncHandler(countDownLatch, GetExistenceResponse.class, holder)); - try { - countDownLatch.await(); - if (holder.value != null) { - return holder.value.getServiceId(); - } - } catch (Exception e) { - LOGGER.error("query microservice id {}/{}/{} fail", - appId, - microserviceName, - versionRule, - e); - } - return null; - } - - @Override - public boolean isSchemaExist(String microserviceId, String schemaId) { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.get(ipPort, - MS_API_PATH + EXISTENCE_PATH, - new RequestParam().addQueryParam("type", "schema") - .addQueryParam("serviceId", microserviceId) - .addQueryParam("schemaId", schemaId), - syncHandler(countDownLatch, GetExistenceResponse.class, holder)); - try { - countDownLatch.await(); - } catch (Exception e) { - LOGGER.error("query schema exist {}/{} fail", - microserviceId, - schemaId, - e); - } - return holder.value != null; - } - - @Override - public boolean registerSchema(String microserviceId, String schemaId, String schemaContent) { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - - try { - CreateSchemaRequest request = new CreateSchemaRequest(); - request.setSchema(schemaContent); - byte[] body = JsonUtils.writeValueAsBytes(request); - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.put(ipPort, - MS_API_PATH + MICROSERVICE_PATH + "/" + microserviceId + SCHEMA_PATH + "/" + schemaId, - new RequestParam().setBody(body), - syncHandlerEx(countDownLatch, holder)); - countDownLatch.await(); - - if (holder.value == null) { - LOGGER.error("Register schema {}/{} failed.", microserviceId, schemaId); - return false; - } - - if (!Status.Family.SUCCESSFUL.equals(Status.Family.familyOf(holder.value.response.statusCode()))) { - LOGGER.error("Register schema {}/{} failed, statusCode: {}, statusMessage: {}, description: {}.", - microserviceId, - schemaId, - holder.value.response.statusCode(), - holder.value.response.statusMessage(), - holder.value.bodyBuffer.toString()); - return false; - } - - LOGGER.info("register schema {}/{} success.", - microserviceId, - schemaId); - return true; - } catch (Exception e) { - LOGGER.error("register schema {}/{} fail.", - microserviceId, - schemaId, - e); - } - return false; - } - - @Override - public String getSchema(String microserviceId, String schemaId) { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.get(ipPort, - MS_API_PATH + MICROSERVICE_PATH + "/" + microserviceId + SCHEMA_PATH + "/" + schemaId, - new RequestParam(), - syncHandler(countDownLatch, GetSchemaResponse.class, holder)); - try { - countDownLatch.await(); - } catch (Exception e) { - LOGGER.error("query schema exist {}/{} failed", - microserviceId, - schemaId, - e); - } - if (holder.value != null) { - return holder.value.getSchema(); - } - - return null; - } - - @Override - public String registerMicroservice(Microservice microservice) { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - try { - CreateServiceRequest request = new CreateServiceRequest(); - request.setService(microservice); - byte[] body = JsonUtils.writeValueAsBytes(request); - - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("register microservice: {}", new String(body, Charset.defaultCharset())); - } - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.post(ipPort, - MS_API_PATH + MICROSERVICE_PATH, - new RequestParam().setBody(body), - syncHandler(countDownLatch, CreateServiceResponse.class, holder)); - countDownLatch.await(); - if (holder.value != null) { - return holder.value.getServiceId(); - } - } catch (Exception e) { - LOGGER.error("register microservice {}/{}/{} failed", - microservice.getAppId(), - microservice.getServiceName(), - microservice.getVersion(), - e); - } - return null; - } - - @Override - public Microservice getMicroservice(String microserviceId) { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - - StringBuilder url = new StringBuilder(MS_API_PATH); - url.append(MICROSERVICE_PATH).append("/").append(microserviceId); - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.get(ipPort, - url.toString(), - new RequestParam(), - syncHandler(countDownLatch, GetServiceResponse.class, holder)); - try { - countDownLatch.await(); - if (holder.value != null) { - return holder.value.getService(); - } - } catch (Exception e) { - LOGGER.error("query microservice {} failed", microserviceId, e); - } - return null; - } - - @Override - public String registerMicroserviceInstance(MicroserviceInstance instance) { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - - StringBuilder url = new StringBuilder(MS_API_PATH); - url.append(MICROSERVICE_PATH) - .append("/") - .append(instance.getServiceId()) - .append(INSTANCES_PATH); - - try { - RegisterInstanceRequest request = new RegisterInstanceRequest(); - request.setInstance(instance); - byte[] body = JsonUtils.writeValueAsBytes(request); - - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("register microservice: {}", new String(body, Charset.defaultCharset())); - } - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.post(ipPort, - url.toString(), - new RequestParam().setBody(body), - syncHandler(countDownLatch, RegisterInstanceResponse.class, holder)); - countDownLatch.await(); - if (holder.value != null) { - return holder.value.getInstanceId(); - } - } catch (Exception e) { - LOGGER.error("register microservice instance {} failed", instance.getServiceId(), e); - } - return null; - } - - @Override - public List getMicroserviceInstance(String consumerId, String providerId) { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - - StringBuilder url = new StringBuilder(MS_API_PATH); - url.append(MICROSERVICE_PATH) - .append("/") - .append(providerId) - .append(INSTANCES_PATH); - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.get(ipPort, - url.toString(), - new RequestParam().addHeader("X-ConsumerId", consumerId), - syncHandler(countDownLatch, GetInstancesResponse.class, holder)); - try { - countDownLatch.await(); - if (holder.value != null) { - return holder.value.getInstances(); - } - } catch (Exception e) { - LOGGER.error("query microservice instances {} failed", providerId, e); - } - return null; - } - - @Override - public boolean unregisterMicroserviceInstance(String microserviceId, String microserviceInstanceId) { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - - StringBuilder url = new StringBuilder(MS_API_PATH); - url.append(MICROSERVICE_PATH) - .append("/") - .append(microserviceId) - .append(INSTANCES_PATH) - .append("/") - .append(microserviceInstanceId); - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.delete(ipPort, - url.toString(), - new RequestParam(), - syncHandler(countDownLatch, HttpClientResponse.class, holder)); - try { - countDownLatch.await(); - if (holder.value != null) { - if (holder.value.statusCode() == Status.OK.getStatusCode()) { - return true; - } - LOGGER.warn(holder.value.statusMessage()); - } - } catch (Exception e) { - LOGGER.error("unregister microservice instance {}/{} failed", - microserviceId, - microserviceInstanceId, - e); - } - return false; - } - - @Override - public HeartbeatResponse heartbeat(String microserviceId, String microserviceInstanceId) { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - - StringBuilder url = new StringBuilder(MS_API_PATH); - url.append(MICROSERVICE_PATH) - .append("/") - .append(microserviceId) - .append(INSTANCES_PATH) - .append("/") - .append(microserviceInstanceId) - .append(HEARTBEAT_PATH); - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.put(ipPort, - url.toString(), - new RequestParam(), - syncHandler(countDownLatch, HttpClientResponse.class, holder)); - - try { - countDownLatch.await(); - if (holder.value != null) { - HeartbeatResponse response = new HeartbeatResponse(); - response.setMessage(holder.value.statusMessage()); - if (holder.value.statusCode() == Status.OK.getStatusCode()) { - response.setOk(true); - return response; - } - LOGGER.warn(holder.value.statusMessage()); - return response; - } - } catch (Exception e) { - LOGGER.error("update microservice instance {}/{} heartbeat failed", - microserviceId, - microserviceInstanceId, - e); - } - return null; - } - - public void watch(String selfMicroserviceId, AsyncResultCallback callback) { - watch(selfMicroserviceId, callback, v -> { - }, v -> { - }); - } - - public void watch(String selfMicroserviceId, AsyncResultCallback callback, - AsyncResultCallback onOpen, AsyncResultCallback onClose) { - Boolean alreadyWatch = watchServices.get(selfMicroserviceId); - if (alreadyWatch == null) { - synchronized (ServiceRegistryClientImpl.class) { - alreadyWatch = watchServices.get(selfMicroserviceId); - if (alreadyWatch == null) { - watchServices.put(selfMicroserviceId, true); - - String url = MS_API_PATH + MICROSERVICE_PATH + "/" + selfMicroserviceId + WATCHER_PATH; - - IpPort ipPort = ipPortManager.get(); - if (ipPort == null) { - LOGGER.error("request address is null, watch microservice {}", - selfMicroserviceId); - watchErrorHandler(new Exception("request address is null"), - selfMicroserviceId, - callback); - return; - } - WebsocketUtils.open(ipPort, url, o -> { - onOpen.success(o); - LOGGER.info( - "watching microservice {} successfully, " - + "the chosen service center address is {}:{}", - selfMicroserviceId, - ipPort.getHostOrIp(), - ipPort.getPort()); - }, c -> { - watchErrorHandler(new ClientException("connection is closed accidentally"), - selfMicroserviceId, - callback); - onClose.success(null); - }, bodyBuffer -> { - MicroserviceInstanceChangedEvent response = null; - try { - response = JsonUtils.readValue(bodyBuffer.getBytes(), - MicroserviceInstanceChangedEvent.class); - } catch (Exception e) { - LOGGER.error("watcher handle microservice {} response failed, {}", - selfMicroserviceId, - bodyBuffer.toString()); - return; - } - try { - callback.success(response); - } catch (Exception e) { - LOGGER.error("notify watcher failed, microservice {}", - selfMicroserviceId, - e); - } - }, e -> { - watchErrorHandler(e, selfMicroserviceId, callback); - onClose.success(null); - }, f -> { - watchErrorHandler(f, selfMicroserviceId, callback); - }); - } - } - } - } - - @Override - public List findServiceInstance(String consumerId, String appId, String serviceName, - String versionRule) { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - - StringBuilder url = new StringBuilder(MS_API_PATH); - url.append(INSTANCES_PATH); - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.get(ipPort, - url.toString(), - new RequestParam().addQueryParam("appId", appId) - .addQueryParam("serviceName", serviceName) - .addQueryParam("version", versionRule) - .addHeader("X-ConsumerId", consumerId), - syncHandler(countDownLatch, FindInstancesResponse.class, holder)); - try { - countDownLatch.await(); - if (holder.value == null) { - return null; // error - } - List list = holder.value.getInstances(); - if (list == null) { - return new ArrayList<>(); - } - return list; - } catch (Exception e) { - LOGGER.error("find microservice instance {}/{}/{} failed", - appId, - serviceName, - versionRule, - e); - } - return null; - } - - private void watchErrorHandler(Throwable e, String selfMicroserviceId, - AsyncResultCallback callback) { - LOGGER.error( - "watcher connect to service center server failed, microservice {}, {}", - selfMicroserviceId, - e.getMessage()); - callback.fail(e); - watchServices.remove(selfMicroserviceId); - } - - @Override - public boolean updateMicroserviceProperties(String microserviceId, Map serviceProperties) { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - - StringBuilder url = new StringBuilder(MS_API_PATH); - url.append(MICROSERVICE_PATH) - .append("/") - .append(microserviceId) - .append(PROPERTIES_PATH); - - try { - UpdatePropertiesRequest request = new UpdatePropertiesRequest(); - request.setProperties(serviceProperties); - byte[] body = JsonUtils.writeValueAsBytes(request); - - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("update properties of microservice: {}", new String(body, Charset.defaultCharset())); - } - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.put(ipPort, - url.toString(), - new RequestParam().setBody(body), - syncHandler(countDownLatch, HttpClientResponse.class, holder)); - - countDownLatch.await(); - if (holder.value != null) { - if (holder.value.statusCode() == Status.OK.getStatusCode()) { - return true; - } - LOGGER.warn(holder.value.statusMessage()); - } - } catch (Exception e) { - LOGGER.error("update properties of microservice {} failed", - microserviceId, - e); - } - return false; - } - - @Override - public boolean updateInstanceProperties(String microserviceId, String microserviceInstanceId, - Map instanceProperties) { - Holder holder = new Holder<>(); - IpPort ipPort = ipPortManager.get(); - - StringBuilder url = new StringBuilder(MS_API_PATH); - url.append(MICROSERVICE_PATH) - .append("/") - .append(microserviceId) - .append(INSTANCES_PATH) - .append("/") - .append(microserviceInstanceId) - .append(PROPERTIES_PATH); - - try { - UpdatePropertiesRequest request = new UpdatePropertiesRequest(); - request.setProperties(instanceProperties); - byte[] body = JsonUtils.writeValueAsBytes(request); - - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("update properties of microservice instance: {}", - new String(body, Charset.defaultCharset())); - } - - CountDownLatch countDownLatch = new CountDownLatch(1); - RestUtils.put(ipPort, - url.toString(), - new RequestParam().setBody(body), - syncHandler(countDownLatch, HttpClientResponse.class, holder)); - - countDownLatch.await(); - if (holder.value != null) { - if (holder.value.statusCode() == Status.OK.getStatusCode()) { - return true; - } - LOGGER.warn(holder.value.statusMessage()); - } - } catch (Exception e) { - LOGGER.error("update properties of microservice instance {}/{} failed", - microserviceId, - microserviceInstanceId, - e); - } - return false; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/WebsocketClientPool.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/WebsocketClientPool.java deleted file mode 100644 index 866403c4b78..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/WebsocketClientPool.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.http.HttpVersion; - -/** - * Created by on 2017/4/28. - */ -public final class WebsocketClientPool extends AbstractClientPool { - - private static final Logger LOGGER = LoggerFactory.getLogger(WebsocketClientPool.class); - - public static final WebsocketClientPool INSTANCE = new WebsocketClientPool(); - - private WebsocketClientPool() { - } - - @Override - public HttpClientOptions createHttpClientOptions() { - HttpVersion ver = ServiceRegistryConfig.INSTANCE.getHttpVersion(); - HttpClientOptions httpClientOptions = new HttpClientOptions(); - httpClientOptions.setProtocolVersion(ver); - httpClientOptions.setConnectTimeout(ServiceRegistryConfig.INSTANCE.getConnectionTimeout()); - // idl timeout in seconds. we add 30 seconds for websocket idle timeout. - httpClientOptions.setIdleTimeout(ServiceRegistryConfig.INSTANCE.getIdleConnectionTimeout() + 30); - if (ver == HttpVersion.HTTP_2) { - LOGGER.debug("service center ws client protocol version is HTTP/2"); - httpClientOptions.setHttp2ClearTextUpgrade(false); - } - if (ServiceRegistryConfig.INSTANCE.isSsl()) { - LOGGER.debug("service center ws client performs requests over TLS"); - buildSecureClientOptions(httpClientOptions); - } - return httpClientOptions; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/WebsocketUtils.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/WebsocketUtils.java deleted file mode 100644 index bf80904b774..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/WebsocketUtils.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.foundation.vertx.client.http.HttpClientWithContext; -import io.vertx.core.Handler; -import io.vertx.core.buffer.Buffer; - -/** - * Created by on 2017/4/28. - */ -public final class WebsocketUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(WebsocketUtils.class); - - private WebsocketUtils() { - } - - public static void open(IpPort ipPort, String url, Handler onOpen, Handler onClose, - Handler onMessage, Handler onException, - Handler onConnectFailed) { - HttpClientWithContext vertxHttpClient = WebsocketClientPool.INSTANCE.getClient(); - vertxHttpClient.runOnContext(client -> { - client.websocket(ipPort.getPort(), - ipPort.getHostOrIp(), - url, - RestUtils.getDefaultHeaders(), - ws -> { - onOpen.handle(null); - - ws.exceptionHandler(v -> { - onException.handle(v); - try { - ws.close(); - } catch (Exception err) { - LOGGER.error("ws close error.", err); - } - }); - ws.closeHandler(v -> { - onClose.handle(v); - try { - ws.close(); - } catch (Exception err) { - LOGGER.error("ws close error.", err); - } - }); - ws.handler(onMessage); - }, - onConnectFailed); - }); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/config/AbstractPropertiesLoader.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/config/AbstractPropertiesLoader.java deleted file mode 100644 index b6c4a64ddea..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/config/AbstractPropertiesLoader.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.config; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.configuration.Configuration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -import io.servicecomb.serviceregistry.api.PropertyExtended; - -/** - * 加载微服务和微服务实例的properties - */ -public abstract class AbstractPropertiesLoader { - private static final Logger LOGGER = LoggerFactory.getLogger(AbstractPropertiesLoader.class); - - protected static final String PROPERTIES = ".properties"; - - protected static final String EXTENDED_CLASS_FOR_COMPATIBLE = ".propertyExtentedClass"; - - protected static final String EXTENDED_CLASS = ".propertyExtendedClass"; - - protected abstract String getConfigOptionPrefix(); - - public Map loadProperties(Configuration configuration) { - Map propertiesMap = new HashMap<>(); - loadPropertiesFromConfigMap(configuration, propertiesMap); - loadPropertiesFromExtendedClass(configuration, propertiesMap); - - return propertiesMap; - } - - protected void loadPropertiesFromConfigMap(Configuration configuration, Map propertiesMap) { - String configKeyPrefix = mergeStrings(getConfigOptionPrefix(), PROPERTIES); - propertiesMap.putAll(ConfigurePropertyUtils.getPropertiesWithPrefix(configuration, configKeyPrefix)); - } - - protected void loadPropertiesFromExtendedClass(Configuration configuration, Map propertiesMap) { - String extendedPropertyClass = readExtendedPropertyClassName(configuration, EXTENDED_CLASS); - if (StringUtils.isEmpty(extendedPropertyClass)) { - extendedPropertyClass = readExtendedPropertyClassName(configuration, EXTENDED_CLASS_FOR_COMPATIBLE); - if (StringUtils.isEmpty(extendedPropertyClass)) { - return; - } - } - - try { - Class classExtenalProperty = Class.forName(extendedPropertyClass); - if (!PropertyExtended.class.isAssignableFrom(classExtenalProperty)) { - 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) classExtenalProperty.newInstance(); - Map extendedPropertiesMap = instance.getExtendedProperties(); - if (extendedPropertiesMap != null && !extendedPropertiesMap.isEmpty()) { - propertiesMap.putAll(extendedPropertiesMap); - } - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { - String errMsg = "Fail to create instance of class: " + extendedPropertyClass; - LOGGER.error(errMsg); - throw new Error(errMsg, e); - } - } - - private String readExtendedPropertyClassName(Configuration configuration, String keyName) { - String configKey = mergeStrings(getConfigOptionPrefix(), keyName); - return configuration.getString(configKey, ""); - } - - protected static String mergeStrings(String... strArr) { - return String.join("", strArr); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/config/ConfigurePropertyUtils.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/config/ConfigurePropertyUtils.java deleted file mode 100644 index 6c5506ad1d6..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/config/ConfigurePropertyUtils.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.config; - -import java.util.ArrayList; -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 com.netflix.config.DynamicPropertyFactory; - -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.serviceregistry.api.registry.BasePath; - -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; - } - - @SuppressWarnings("unchecked") - public static List getMicroservicePaths(Configuration configuration) { - List basePaths = new ArrayList<>(); - for (Object path : configuration.getList("service_description.paths")) { - BasePath basePath = new BasePath(); - Map pathMap = (Map) path; - basePath.setPath(buildPath((String) pathMap.get("path"))); - basePath.setProperty((Map) pathMap.get("property")); - basePaths.add(basePath); - } - return basePaths; - } - - private static String buildPath(String path) { - String prefix = System.getProperty(Const.URL_PREFIX); - if (StringUtils.isNotEmpty(prefix)) { - if (!path.startsWith(prefix)) { - path = prefix + path; - } - } - return path; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/config/InstancePropertiesLoader.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/config/InstancePropertiesLoader.java deleted file mode 100644 index 864ad0924b5..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/config/InstancePropertiesLoader.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.config; - -public final class InstancePropertiesLoader extends AbstractPropertiesLoader { - - public static final InstancePropertiesLoader INSTANCE = new InstancePropertiesLoader(); - - private InstancePropertiesLoader() { - } - - @Override - protected String getConfigOptionPrefix() { - return "instance_description"; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/config/MicroservicePropertiesLoader.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/config/MicroservicePropertiesLoader.java deleted file mode 100644 index 3ddc8ed0128..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/config/MicroservicePropertiesLoader.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.config; - -import static io.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_SERVICE; - -public final class MicroservicePropertiesLoader extends AbstractPropertiesLoader { - - public static final MicroservicePropertiesLoader INSTANCE = new MicroservicePropertiesLoader(); - - private MicroservicePropertiesLoader() { - } - - @Override - protected String getConfigOptionPrefix() { - return CONFIG_SERVICE; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/config/ServiceRegistryConfig.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/config/ServiceRegistryConfig.java deleted file mode 100644 index cd49e92d8e7..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/config/ServiceRegistryConfig.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.config; - -import java.net.URI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.config.DynamicBooleanProperty; -import com.netflix.config.DynamicIntProperty; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicStringProperty; - -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.foundation.common.net.NetUtils; -import io.vertx.core.http.HttpVersion; - -/** - * Created by on 2016/12/23. - */ -public final class ServiceRegistryConfig { - private static final Logger LOGGER = LoggerFactory.getLogger(ServiceRegistryConfig.class); - - public static final ServiceRegistryConfig INSTANCE = new ServiceRegistryConfig(); - - private static final int PROTOCOL_HTTP_PORT = 80; - - private static final int DEFAULT_TIMEOUT_IN_MS = 30000; - - private static final int DEFAULT_TIMEOUT_IN_SECONDS = 30; - - private static final int DEFAULT_REQUEST_TIMEOUT_IN_MS = 30000; - - private static final int DEFAULT_CHECK_INTERVAL_IN_S = 30; - - private static final int DEFAULT_CHECK_TIMES = 3; - - public static final String AUTH_ENABLED = "cse.auth.enabled"; - - public static final String TENANT_NAME = "cse.config.client.tenantName"; - - public static final String TENANT_ACCESS_KEY = "cse.auth.accessKey"; - - public static final String TENANT_SECRET_KEY = "cse.auth.secretKey"; - - public static final String NO_TENANT = "default"; - - private boolean ssl = true; - - private ServiceRegistryConfig() { - - } - - public HttpVersion getHttpVersion() { - DynamicStringProperty property = - DynamicPropertyFactory.getInstance() - .getStringProperty("cse.service.registry.client.httpVersion", "HTTP_1_1"); - return HttpVersion.valueOf(property.get()); - } - - public int getWorkerPoolSize() { - DynamicIntProperty property = - DynamicPropertyFactory.getInstance() - .getIntProperty("cse.service.registry.client.workerPoolSize", 1); - int workerPoolSize = property.get(); - if (workerPoolSize <= 0) { - return Runtime.getRuntime().availableProcessors(); - } - return workerPoolSize; - } - - public boolean isSsl() { - getIpPort(); - return this.ssl; - } - - public ArrayList getIpPort() { - DynamicStringProperty property = - DynamicPropertyFactory.getInstance() - .getStringProperty("cse.service.registry.address", "https://127.0.0.1:30100"); - List uriLsit = Arrays.asList(property.get().split(",")); - ArrayList ipPortList = new ArrayList(); - for (int i = 0; i < uriLsit.size(); i++) { - try { - URI uri = new URI(uriLsit.get(i)); - StringBuilder sb = new StringBuilder(uri.getHost()); - sb.append(':').append(uri.getPort() < 0 ? PROTOCOL_HTTP_PORT : uri.getPort()); - this.ssl = uri.getScheme().startsWith("https"); - ipPortList.add(NetUtils.parseIpPort(sb.toString())); - } catch (Exception e) { - LOGGER.error("cse.service.registry.address invalid : {}", uriLsit.get(i), e); - } - } - return ipPortList; - } - - public String getTransport() { - return "rest"; - } - - public int getConnectionTimeout() { - DynamicIntProperty property = - DynamicPropertyFactory.getInstance() - .getIntProperty("cse.service.registry.client.timeout.connection", DEFAULT_TIMEOUT_IN_MS); - int timeout = property.get(); - return timeout < 0 ? DEFAULT_TIMEOUT_IN_MS : timeout; - } - - public int getIdleConnectionTimeout() { - DynamicIntProperty property = - DynamicPropertyFactory.getInstance() - .getIntProperty("cse.service.registry.client.timeout.idle", DEFAULT_TIMEOUT_IN_SECONDS); - int timeout = property.get(); - return timeout < 1 ? DEFAULT_TIMEOUT_IN_SECONDS : timeout; - } - - public int getRequestTimeout() { - DynamicIntProperty property = - DynamicPropertyFactory.getInstance() - .getIntProperty("cse.service.registry.client.timeout.request", DEFAULT_REQUEST_TIMEOUT_IN_MS); - int timeout = property.get(); - return timeout < 1 ? DEFAULT_REQUEST_TIMEOUT_IN_MS : timeout; - } - - public int getHeartbeatInterval() { - DynamicIntProperty property = - DynamicPropertyFactory.getInstance() - .getIntProperty("cse.service.registry.instance.healthCheck.interval", - DEFAULT_CHECK_INTERVAL_IN_S); - int interval = property.get(); - return interval < 0 ? DEFAULT_CHECK_INTERVAL_IN_S : interval; - } - - public boolean isRegistryAutoDiscovery() { - DynamicBooleanProperty property = - DynamicPropertyFactory.getInstance() - .getBooleanProperty("cse.service.registry.autodiscovery", - false); - return property.get(); - } - - public int getResendHeartBeatTimes() { - DynamicIntProperty property = - DynamicPropertyFactory.getInstance() - .getIntProperty("cse.service.registry.instance.healthCheck.times", - DEFAULT_CHECK_TIMES); - int times = property.get(); - return times < 0 ? DEFAULT_CHECK_TIMES : times; - } - - public boolean isPreferIpAddress() { - DynamicBooleanProperty property = - DynamicPropertyFactory.getInstance() - .getBooleanProperty("cse.service.registry.instance.preferIpAddress", - false); - return property.get(); - } - - public boolean isWatch() { - DynamicBooleanProperty property = - DynamicPropertyFactory.getInstance() - .getBooleanProperty("cse.service.registry.instance.watch", - true); - return property.get(); - } - - public boolean isClientAuthEnabled() { - String isAuthEnabled = getProperty("false", AUTH_ENABLED); - return Boolean.parseBoolean(isAuthEnabled); - } - - public String getTenantName() { - String tenantName = getProperty(NO_TENANT, TENANT_NAME); - return tenantName; - } - - public String getAccessKey() { - String tenantName = getProperty(null, TENANT_ACCESS_KEY); - return tenantName; - } - - public String getSecretKey() { - String tenantName = getProperty(null, TENANT_SECRET_KEY); - return tenantName; - } - - private String getProperty(String defaultValue, String... keys) { - String property = null; - for (String key : keys) { - property = DynamicPropertyFactory.getInstance().getStringProperty(key, null).get(); - if (property != null) { - break; - } - } - - if (property != null) { - return property; - } else { - return defaultValue; - } - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/definition/DefinitionConst.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/definition/DefinitionConst.java deleted file mode 100644 index 0e263930ccd..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/definition/DefinitionConst.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.definition; - -import static io.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_SERVICE; - -public interface DefinitionConst { - String appIdKey = "APPLICATION_ID"; - - String serviceDescriptionKey = CONFIG_SERVICE; - - String nameKey = "name"; - - String qulifiedServiceNameKey = serviceDescriptionKey + "." + nameKey; - - String qulifiedServiceVersionKey = serviceDescriptionKey + ".version"; - - String qulifiedServiceDescKey = serviceDescriptionKey + ".description"; - - String qulifiedServiceRoleKey = serviceDescriptionKey + ".role"; - - String allowCrossAppKey = "allowCrossApp"; - - String defaultAppId = "default"; - - String defaultMicroserviceName = "defaultMicroservice"; - - String defaultVersion = "1.0.0"; - - String defaultStage = "prod"; -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/definition/MicroserviceDefinition.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/definition/MicroserviceDefinition.java deleted file mode 100644 index 288c866f334..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/definition/MicroserviceDefinition.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.definition; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.commons.configuration.Configuration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -import io.servicecomb.config.ConfigUtil; -import io.servicecomb.config.archaius.sources.ConfigModel; - -public class MicroserviceDefinition { - private static final Logger LOGGER = LoggerFactory.getLogger(MicroserviceDefinition.class); - - // microservice maybe combined from many microservices - // if a and b combined to ab, then combinedFrom value is a,b - // if not combined, just only one microservice, then combinedFrom is empty - private Set combinedFrom = new HashSet<>(); - - private List configModels; - - private Configuration configuration; - - private String microserviceName; - - public String getMicroserviceName() { - return microserviceName; - } - - public static MicroserviceDefinition create(String appId, String microserviceName) { - ConfigModel configModel = createConfigModel(appId, microserviceName); - return new MicroserviceDefinition(Arrays.asList(configModel)); - } - - public static ConfigModel createConfigModel(String appId, String microserviceName) { - Map descMap = new HashMap<>(); - descMap.put(DefinitionConst.nameKey, microserviceName); - - Map config = new HashMap<>(); - config.put(DefinitionConst.appIdKey, appId); - config.put(DefinitionConst.serviceDescriptionKey, descMap); - - ConfigModel configModel = new ConfigModel(); - configModel.setConfig(config); - return configModel; - } - - public MicroserviceDefinition(List configModels) { - if (configModels == null) { - configModels = Collections.emptyList(); - } - - this.configModels = configModels; - this.configuration = ConfigUtil.createLocalConfig(configModels); - this.microserviceName = - configuration.getString(DefinitionConst.qulifiedServiceNameKey, DefinitionConst.defaultMicroserviceName); - - // log paths first, even microserviceName is invalid, this can help user to find problems - logConfigPath(); - checkMicroserviceName(microserviceName); - - initCombinedFrom(configModels); - } - - public Set getCombinedFrom() { - return combinedFrom; - } - - private void initCombinedFrom(List configModels) { - for (ConfigModel model : configModels) { - Configuration conf = ConfigUtil.createLocalConfig(Arrays.asList(model)); - String name = - conf.getString(DefinitionConst.qulifiedServiceNameKey, DefinitionConst.defaultMicroserviceName); - if (!StringUtils.isEmpty(name)) { - checkMicroserviceName(name); - combinedFrom.add(name); - } - } - - combinedFrom.remove(microserviceName); - } - - private void checkMicroserviceName(String name) { - // the configuration we used - // when resolve placeholder failed - // the result will remains ${var} - if (StringUtils.isEmpty(name) || name.indexOf("${") != -1) { - throw new IllegalArgumentException(String.format( - "MicroserviceName '%s' is invalid. you must configure '%s' or set the placeholder value.", - name, - DefinitionConst.qulifiedServiceNameKey)); - } - } - - // microserviceName maybe null - public void logConfigPath() { - List pathList = new ArrayList<>(); - for (ConfigModel configModel : configModels) { - if (configModel.getUrl() != null) { - pathList.add(configModel.getUrl().toString()); - } - } - LOGGER.info("load microservice config, name={}, paths={}", - microserviceName, - pathList); - } - - public List getConfigModels() { - return configModels; - } - - public Configuration getConfiguration() { - return configuration; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/registry/AbstractServiceRegistry.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/registry/AbstractServiceRegistry.java deleted file mode 100644 index 11650f4212b..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/registry/AbstractServiceRegistry.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.registry; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; - -import io.servicecomb.serviceregistry.Features; -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.serviceregistry.api.registry.BasePath; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceFactory; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.cache.InstanceCacheManager; -import io.servicecomb.serviceregistry.cache.InstanceVersionCacheManager; -import io.servicecomb.serviceregistry.client.IpPortManager; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.servicecomb.serviceregistry.definition.MicroserviceDefinition; -import io.servicecomb.serviceregistry.task.MicroserviceServiceCenterTask; -import io.servicecomb.serviceregistry.task.ServiceCenterTask; -import io.servicecomb.serviceregistry.task.event.ExceptionEvent; -import io.servicecomb.serviceregistry.task.event.RecoveryEvent; -import io.servicecomb.serviceregistry.task.event.ShutdownEvent; - -public abstract class AbstractServiceRegistry implements ServiceRegistry { - private static final Logger LOGGER = LoggerFactory.getLogger(AbstractServiceRegistry.class); - - private Features features = new Features(); - - private MicroserviceFactory microserviceFactory = new MicroserviceFactory(); - - protected EventBus eventBus; - - protected MicroserviceDefinition microserviceDefinition; - - protected Microservice microservice; - - protected InstanceCacheManager instanceCacheManager; - - protected IpPortManager ipPortManager; - - protected InstanceVersionCacheManager instanceVersionCacheManager; - - protected ServiceRegistryClient srClient; - - protected ServiceRegistryConfig serviceRegistryConfig; - - protected ServiceCenterTask serviceCenterTask; - - // any exeption event will set cache not avaiable, but not clear cache - // any recovery event will clear cache - // - // TODO: clear cache is not good, maybe cause no cache data can be used - // it's better to replace the old cache by the new cache, if can't get new cache, then always use old cache. - protected boolean cacheAvaiable; - - public AbstractServiceRegistry(EventBus eventBus, ServiceRegistryConfig serviceRegistryConfig, - MicroserviceDefinition microserviceDefinition) { - this.eventBus = eventBus; - this.serviceRegistryConfig = serviceRegistryConfig; - this.microserviceDefinition = microserviceDefinition; - this.microservice = microserviceFactory.create(microserviceDefinition); - } - - @Override - public void init() { - instanceCacheManager = new InstanceCacheManager(eventBus, this); - ipPortManager = new IpPortManager(serviceRegistryConfig, instanceCacheManager); - instanceVersionCacheManager = new InstanceVersionCacheManager(eventBus, this); - if (srClient == null) { - srClient = createServiceRegistryClient(); - } - - createServiceCenterTask(); - - eventBus.register(this); - } - - @Override - public Features getFeatures() { - return features; - } - - public EventBus getEventBus() { - return eventBus; - } - - @Override - public Set getCombinedMicroserviceNames() { - return microserviceDefinition.getCombinedFrom(); - } - - @Override - public ServiceRegistryClient getServiceRegistryClient() { - return srClient; - } - - public void setServiceRegistryClient(ServiceRegistryClient serviceRegistryClient) { - this.srClient = serviceRegistryClient; - } - - public IpPortManager getIpPortManager() { - return ipPortManager; - } - - @Override - public InstanceCacheManager getInstanceCacheManager() { - return instanceCacheManager; - } - - @Override - public InstanceVersionCacheManager getInstanceVersionCacheManager() { - return instanceVersionCacheManager; - } - - protected abstract ServiceRegistryClient createServiceRegistryClient(); - - @Override - public void run() { - loadStaticConfiguration(); - - // try register - // if failed, then retry in thread - serviceCenterTask.init(); - } - - private void loadStaticConfiguration() { - // TODO 如果yaml定义了paths规则属性,替换默认值,现需要DynamicPropertyFactory支持数组获取 - List paths = microservice.getPaths(); - for (BasePath path : paths) { - if (path.getProperty() == null) { - path.setProperty(new HashMap<>()); - } - path.getProperty().put(Const.PATH_CHECKSESSION, "false"); - } - } - - private void createServiceCenterTask() { - MicroserviceServiceCenterTask task = - new MicroserviceServiceCenterTask(eventBus, serviceRegistryConfig, srClient, microservice); - serviceCenterTask = new ServiceCenterTask(eventBus, serviceRegistryConfig, task); - } - - @Subscribe - public void onException(ExceptionEvent event) { - cacheAvaiable = false; - } - - @Subscribe - public void onRecovered(RecoveryEvent event) { - if (!cacheAvaiable) { - cacheAvaiable = true; - - instanceCacheManager.cleanUp(); - ipPortManager.clearInstanceCache(); - instanceVersionCacheManager.cleanUp(); - LOGGER.info( - "Reconnected to service center, clean up the provider's microservice instances cache."); - } - } - - public boolean unregsiterInstance() { - MicroserviceInstance microserviceInstance = microservice.getIntance(); - boolean result = srClient.unregisterMicroserviceInstance(microserviceInstance.getServiceId(), - microserviceInstance.getInstanceId()); - if (!result) { - LOGGER.error("Unregister microservice instance failed. microserviceId={} instanceId={}", - microserviceInstance.getServiceId(), - microserviceInstance.getInstanceId()); - return false; - } - LOGGER.info("Unregister microservice instance success. microserviceId={} instanceId={}", - microserviceInstance.getServiceId(), - microserviceInstance.getInstanceId()); - return true; - } - - public List findServiceInstance(String appId, String serviceName, - String versionRule) { - List instances = srClient.findServiceInstance(microservice.getServiceId(), - appId, - serviceName, - versionRule); - if (instances == null) { - LOGGER.error("find empty instances from service center. service={}/{}", appId, serviceName); - return null; - } - - LOGGER.info("find instances[{}] from service center success. service={}/{}", - instances.size(), - appId, - serviceName); - for (MicroserviceInstance instance : instances) { - LOGGER.info("service id={}, instance id={}, endpoints={}", - instance.getServiceId(), - instance.getInstanceId(), - instance.getEndpoints()); - } - return instances; - } - - @Override - public boolean updateMicroserviceProperties(Map properties) { - boolean success = srClient.updateMicroserviceProperties(microservice.getServiceId(), - properties); - if (success) { - microservice.setProperties(properties); - } - return success; - } - - public boolean updateInstanceProperties(Map instanceProperties) { - MicroserviceInstance microserviceInstance = microservice.getIntance(); - boolean success = srClient.updateInstanceProperties(microserviceInstance.getServiceId(), - microserviceInstance.getInstanceId(), - instanceProperties); - if (success) { - microserviceInstance.setProperties(instanceProperties); - } - return success; - } - - public Microservice getRemoteMicroservice(String microserviceId) { - return srClient.getMicroservice(microserviceId); - } - - public Microservice getMicroservice() { - return microservice; - } - - public MicroserviceInstance getMicroserviceInstance() { - return microservice.getIntance(); - } - - public void destory() { - eventBus.post(new ShutdownEvent()); - unregsiterInstance(); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/registry/LocalServiceRegistry.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/registry/LocalServiceRegistry.java deleted file mode 100644 index ee2a74c84d7..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/registry/LocalServiceRegistry.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.registry; - -import com.google.common.eventbus.EventBus; - -import io.servicecomb.serviceregistry.client.LocalServiceRegistryClientImpl; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.servicecomb.serviceregistry.definition.MicroserviceDefinition; - -public class LocalServiceRegistry extends AbstractServiceRegistry { - public LocalServiceRegistry(EventBus eventBus, ServiceRegistryConfig serviceRegistryConfig, - MicroserviceDefinition microserviceDefinition) { - super(eventBus, serviceRegistryConfig, microserviceDefinition); - } - - protected ServiceRegistryClient createServiceRegistryClient() { - return new LocalServiceRegistryClientImpl(); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/registry/RemoteServiceRegistry.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/registry/RemoteServiceRegistry.java deleted file mode 100644 index 8187d8b4671..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/registry/RemoteServiceRegistry.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.registry; - -import com.google.common.eventbus.EventBus; - -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.servicecomb.serviceregistry.client.http.ServiceRegistryClientImpl; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.servicecomb.serviceregistry.definition.MicroserviceDefinition; - -public class RemoteServiceRegistry extends AbstractServiceRegistry { - public RemoteServiceRegistry(EventBus eventBus, ServiceRegistryConfig serviceRegistryConfig, - MicroserviceDefinition microserviceDefinition) { - super(eventBus, serviceRegistryConfig, microserviceDefinition); - } - - protected ServiceRegistryClient createServiceRegistryClient() { - return new ServiceRegistryClientImpl(ipPortManager); - } - - @Override - public void run() { - super.run(); - - Thread registryThread = new Thread(serviceCenterTask, "RegistryThread"); - registryThread.start(); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/registry/ServiceRegistryFactory.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/registry/ServiceRegistryFactory.java deleted file mode 100644 index 16e504f5a93..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/registry/ServiceRegistryFactory.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.registry; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -import com.google.common.eventbus.EventBus; - -import io.servicecomb.config.archaius.sources.MicroserviceConfigLoader; -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.client.LocalServiceRegistryClientImpl; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.servicecomb.serviceregistry.definition.MicroserviceDefinition; - -/** - * Created by on 2017/3/31. - */ -public final class ServiceRegistryFactory { - private static final Logger LOGGER = LoggerFactory.getLogger(ServiceRegistryFactory.class); - - private static final Object LOCK = new Object(); - - private static volatile ServiceRegistry serviceRegistry; - - private ServiceRegistryFactory() { - } - - public static ServiceRegistry getServiceRegistry() { - return serviceRegistry; - } - - public static ServiceRegistry getOrCreate(EventBus eventBus, ServiceRegistryConfig serviceRegistryConfig, - MicroserviceDefinition microserviceDefinition) { - if (serviceRegistry == null) { - synchronized (LOCK) { - if (serviceRegistry == null) { - serviceRegistry = create(eventBus, serviceRegistryConfig, microserviceDefinition); - } - } - } - return serviceRegistry; - } - - public static ServiceRegistry createLocal() { - EventBus eventBus = new EventBus(); - ServiceRegistryConfig serviceRegistryConfig = ServiceRegistryConfig.INSTANCE; - MicroserviceConfigLoader loader = new MicroserviceConfigLoader(); - loader.loadAndSort(); - - MicroserviceDefinition microserviceDefinition = new MicroserviceDefinition(loader.getConfigModels()); - return new LocalServiceRegistry(eventBus, serviceRegistryConfig, microserviceDefinition); - } - - public static ServiceRegistry create(EventBus eventBus, ServiceRegistryConfig serviceRegistryConfig, - MicroserviceDefinition microserviceDefinition) { - String localModeFile = System.getProperty(LocalServiceRegistryClientImpl.LOCAL_REGISTRY_FILE_KEY); - if (!StringUtils.isEmpty(localModeFile)) { - LOGGER.info( - "It is running in the local development mode, the local file {} is using as the local registry", - localModeFile); - - return new LocalServiceRegistry(eventBus, serviceRegistryConfig, microserviceDefinition); - } - - LOGGER.info("It is running in the normal mode, a separated service registry is required"); - return new RemoteServiceRegistry(eventBus, serviceRegistryConfig, microserviceDefinition); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/AbstractRegisterTask.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/AbstractRegisterTask.java deleted file mode 100644 index eb290775209..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/AbstractRegisterTask.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import com.google.common.eventbus.EventBus; - -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; - -public abstract class AbstractRegisterTask extends AbstractTask { - protected boolean registered; - - public AbstractRegisterTask(EventBus eventBus, ServiceRegistryClient srClient, Microservice microservice) { - super(eventBus, srClient, microservice); - } - - public boolean isRegistered() { - return registered; - } - - @Override - public void doRun() { - if (registered) { - return; - } - - if (doRegister()) { - registered = true; - taskStatus = TaskStatus.FINISHED; - } - } - - protected abstract boolean doRegister(); -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/AbstractTask.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/AbstractTask.java deleted file mode 100644 index ae5392917af..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/AbstractTask.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import com.google.common.eventbus.EventBus; - -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; - -public abstract class AbstractTask implements Runnable { - protected TaskStatus taskStatus = TaskStatus.INIT; - - protected EventBus eventBus; - - protected ServiceRegistryClient srClient; - - protected Microservice microservice; - - public AbstractTask(EventBus eventBus, ServiceRegistryClient srClient, Microservice microservice) { - this.eventBus = eventBus; - this.srClient = srClient; - this.microservice = microservice; - - this.eventBus.register(this); - } - - public EventBus getEventBus() { - return eventBus; - } - - public Microservice getMicroservice() { - return microservice; - } - - @Override - public void run() { - if (taskStatus == TaskStatus.READY) { - // if this task is actually run, we send a notification - doRun(); - eventBus.post(this); - } - } - - abstract protected void doRun(); - - protected boolean isSameMicroservice(Microservice otherMicroservice) { - return microservice.getServiceName().equals(otherMicroservice.getServiceName()); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/CompositeTask.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/CompositeTask.java deleted file mode 100644 index 4451300ade4..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/CompositeTask.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import java.util.ArrayList; -import java.util.List; - -public class CompositeTask implements Runnable { - private List taskList = new ArrayList<>(); - - public void addTask(Runnable task) { - taskList.add(task); - } - - public int getTaskCount() { - return taskList.size(); - } - - public void run() { - for (Runnable task : taskList) { - task.run(); - } - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/HeartbeatResult.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/HeartbeatResult.java deleted file mode 100644 index b903e42bc9e..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/HeartbeatResult.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -public enum HeartbeatResult { - DISCONNECTED, - SUCCESS, - INSTANCE_NOT_REGISTERED -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceInstanceHeartbeatTask.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceInstanceHeartbeatTask.java deleted file mode 100644 index b20b924fe9d..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceInstanceHeartbeatTask.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; - -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.api.response.HeartbeatResponse; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; - -public class MicroserviceInstanceHeartbeatTask extends AbstractTask { - private static final Logger LOGGER = LoggerFactory.getLogger(MicroserviceInstanceHeartbeatTask.class); - - private MicroserviceInstance microserviceInstance; - - private HeartbeatResult heartbeatResult; - - public MicroserviceInstanceHeartbeatTask(EventBus eventBus, ServiceRegistryClient srClient, - Microservice microservice) { - super(eventBus, srClient, microservice); - this.microserviceInstance = microservice.getIntance(); - } - - @Subscribe - public void onMicroserviceWatchTask(MicroserviceWatchTask task) { - if (task.taskStatus == TaskStatus.READY && isSameMicroservice(task.getMicroservice())) { - this.taskStatus = TaskStatus.READY; - } - } - - public HeartbeatResult getHeartbeatResult() { - return heartbeatResult; - } - - // only got service center response, and result is not ok, means need to register instance again. - public boolean isNeedRegisterInstance() { - return HeartbeatResult.INSTANCE_NOT_REGISTERED.equals(heartbeatResult); - } - - @Override - public void doRun() { - // will always run heartbeat when it is ready - heartbeatResult = heartbeat(); - } - - private HeartbeatResult heartbeat() { - HeartbeatResponse response = - srClient.heartbeat(microserviceInstance.getServiceId(), microserviceInstance.getInstanceId()); - if (response == null) { - LOGGER.error("Disconnected from service center and heartbeat failed for microservice instance={}/{}", - microserviceInstance.getServiceId(), - microserviceInstance.getInstanceId()); - return HeartbeatResult.DISCONNECTED; - } - - if (!response.isOk()) { - LOGGER.error("Update heartbeat to service center failed, microservice instance={}/{} does not exist", - microserviceInstance.getServiceId(), - microserviceInstance.getInstanceId()); - return HeartbeatResult.INSTANCE_NOT_REGISTERED; - } - - return HeartbeatResult.SUCCESS; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceInstanceRegisterTask.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceInstanceRegisterTask.java deleted file mode 100644 index d6250c8e603..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceInstanceRegisterTask.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; - -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; - -public class MicroserviceInstanceRegisterTask extends AbstractRegisterTask { - private static final Logger LOGGER = LoggerFactory.getLogger(MicroserviceInstanceRegisterTask.class); - - private ServiceRegistryConfig serviceRegistryConfig; - - private MicroserviceInstance microserviceInstance; - - public MicroserviceInstanceRegisterTask(EventBus eventBus, ServiceRegistryConfig serviceRegistryConfig, - ServiceRegistryClient srClient, - Microservice microservice) { - super(eventBus, srClient, microservice); - - this.serviceRegistryConfig = serviceRegistryConfig; - this.microserviceInstance = microservice.getIntance(); - } - - @Subscribe - public void onMicroserviceRegisterTask(MicroserviceRegisterTask task) { - if (task.taskStatus == TaskStatus.FINISHED && isSameMicroservice(task.getMicroservice())) { - this.taskStatus = TaskStatus.READY; - this.registered = false; - } else { - this.taskStatus = TaskStatus.INIT; - } - } - - @Override - protected boolean doRegister() { - LOGGER.info("running microservice instance register task."); - String hostName = ""; - if (serviceRegistryConfig.isPreferIpAddress()) { - hostName = RegistryUtils.getPublishAddress(); - } else { - hostName = RegistryUtils.getPublishHostName(); - } - microserviceInstance.setHostName(hostName); - microserviceInstance.getHealthCheck().setInterval(serviceRegistryConfig.getHeartbeatInterval()); - microserviceInstance.getHealthCheck().setTimes(serviceRegistryConfig.getResendHeartBeatTimes()); - - String instanceId = srClient.registerMicroserviceInstance(microserviceInstance); - if (StringUtils.isEmpty(instanceId)) { - LOGGER.error("Register microservice instance failed. microserviceId={}", - microserviceInstance.getServiceId()); - return false; - } - microserviceInstance.setInstanceId(instanceId); - LOGGER.info( - "Register microservice instance success. microserviceId={} instanceId={} endpoints={} lease {}s", - microserviceInstance.getServiceId(), - instanceId, - microserviceInstance.getEndpoints(), - microserviceInstance.getHealthCheck().getTTL()); - - return true; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceRegisterTask.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceRegisterTask.java deleted file mode 100644 index a25eddaba20..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceRegisterTask.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import java.util.HashSet; -import java.util.Map.Entry; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; - -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; - -public class MicroserviceRegisterTask extends AbstractRegisterTask { - private static final Logger LOGGER = LoggerFactory.getLogger(MicroserviceRegisterTask.class); - - private boolean schemaIdSetMatch; - - public MicroserviceRegisterTask(EventBus eventBus, ServiceRegistryClient srClient, Microservice microservice) { - super(eventBus, srClient, microservice); - this.taskStatus = TaskStatus.READY; - } - - public boolean isSchemaIdSetMatch() { - return schemaIdSetMatch; - } - - @Subscribe - public void onMicroserviceInstanceHeartbeatTask(MicroserviceInstanceHeartbeatTask task) { - if (task.getHeartbeatResult() != HeartbeatResult.SUCCESS && isSameMicroservice(task.getMicroservice())) { - LOGGER.info("read MicroserviceInstanceHeartbeatTask status is {}", task.taskStatus); - this.taskStatus = TaskStatus.READY; - this.registered = false; - } - } - - @Override - protected boolean doRegister() { - LOGGER.info("running microservice register task."); - String serviceId = srClient.getMicroserviceId(microservice.getAppId(), - microservice.getServiceName(), - microservice.getVersion()); - if (!StringUtils.isEmpty(serviceId)) { - // 已经注册过了,不需要重新注册 - microservice.setServiceId(serviceId); - LOGGER.info( - "Microservice exists in service center, no need to register. id={} appId={}, name={}, version={}", - serviceId, - microservice.getAppId(), - microservice.getServiceName(), - microservice.getVersion()); - - if (!checkSchemaIdSet()) { - return false; - } - } else { - serviceId = srClient.registerMicroservice(microservice); - if (StringUtils.isEmpty(serviceId)) { - LOGGER.error( - "Registry microservice failed. appId={}, name={}, version={}", - microservice.getAppId(), - microservice.getServiceName(), - microservice.getVersion()); - return false; - } - - schemaIdSetMatch = true; - // 重新注册服务场景下,instanceId不应该缓存 - microservice.getIntance().setInstanceId(null); - - LOGGER.info( - "Registry Microservice successfully. id={} appId={}, name={}, version={}, schemaIds={}", - serviceId, - microservice.getAppId(), - microservice.getServiceName(), - microservice.getVersion(), - microservice.getSchemas()); - } - - microservice.setServiceId(serviceId); - microservice.getIntance().setServiceId(microservice.getServiceId()); - - return registerSchemas(); - } - - private boolean checkSchemaIdSet() { - Microservice existMicroservice = srClient.getMicroservice(microservice.getServiceId()); - if (existMicroservice == null) { - LOGGER.error("Error to get microservice from service center when check schema set"); - return false; - } - Set existSchemas = new HashSet<>(existMicroservice.getSchemas()); - Set localSchemas = new HashSet<>(microservice.getSchemas()); - schemaIdSetMatch = existSchemas.equals(localSchemas); - - if (!schemaIdSetMatch) { - LOGGER.error( - "SchemaIds is different between local and service center. Please change microservice version. " - + "id={} appId={}, name={}, version={}, local schemaIds={}, service center schemaIds={}", - microservice.getServiceId(), - microservice.getAppId(), - microservice.getServiceName(), - microservice.getVersion(), - localSchemas, - existSchemas); - return true; - } - - LOGGER.info( - "SchemaIds is equals to service center. id={} appId={}, name={}, version={}, schemaIds={}", - microservice.getServiceId(), - microservice.getAppId(), - microservice.getServiceName(), - microservice.getVersion(), - localSchemas); - return true; - } - - private boolean registerSchemas() { - for (Entry entry : microservice.getSchemaMap().entrySet()) { - String schemaId = entry.getKey(); - String content = entry.getValue(); - - boolean exists = srClient.isSchemaExist(microservice.getServiceId(), schemaId); - LOGGER.info("schemaId {} exists {}", schemaId, exists); - if (!exists) { - if (!srClient.registerSchema(microservice.getServiceId(), schemaId, content)) { - return false; - } - } - } - - return true; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceServiceCenterTask.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceServiceCenterTask.java deleted file mode 100644 index 760568b0aa9..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceServiceCenterTask.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import com.google.common.eventbus.EventBus; - -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; - -public class MicroserviceServiceCenterTask extends CompositeTask { - public MicroserviceServiceCenterTask(EventBus eventBus, ServiceRegistryConfig serviceRegistryConfig, - ServiceRegistryClient srClient, Microservice microservice) { - addTask(new MicroserviceRegisterTask(eventBus, srClient, microservice)); - addTask(new MicroserviceInstanceRegisterTask(eventBus, serviceRegistryConfig, srClient, microservice)); - addTask(new MicroserviceWatchTask(eventBus, serviceRegistryConfig, srClient, microservice)); - addTask(new MicroserviceInstanceHeartbeatTask(eventBus, srClient, microservice)); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceWatchTask.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceWatchTask.java deleted file mode 100644 index 5b1fd0758ac..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceWatchTask.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; - -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.response.MicroserviceInstanceChangedEvent; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.servicecomb.serviceregistry.task.event.ExceptionEvent; -import io.servicecomb.serviceregistry.task.event.RecoveryEvent; - -public class MicroserviceWatchTask extends AbstractTask { - private static final Logger LOGGER = LoggerFactory.getLogger(MicroserviceWatchTask.class); - - private ServiceRegistryConfig serviceRegistryConfig; - - public MicroserviceWatchTask(EventBus eventBus, ServiceRegistryConfig serviceRegistryConfig, - ServiceRegistryClient srClient, Microservice microservice) { - super(eventBus, srClient, microservice); - this.serviceRegistryConfig = serviceRegistryConfig; - } - - @Subscribe - public void onMicroserviceInstanceRegisterTask(MicroserviceInstanceRegisterTask task) { - if (task.taskStatus == TaskStatus.FINISHED && isSameMicroservice(task.getMicroservice())) { - this.taskStatus = TaskStatus.READY; - } - } - - @Override - public void doRun() { - // will always run watch when it is ready - if (!needToWatch()) { - return; - } - - srClient.watch(microservice.getServiceId(), - (event) -> { - if (event.failed()) { - eventBus.post(new ExceptionEvent(event.cause())); - return; - } - - MicroserviceInstanceChangedEvent changedEvent = event.result(); - if (isProviderInstancesChanged(changedEvent) && !serviceRegistryConfig.isWatch()) { - return; - } - if (!isProviderInstancesChanged(changedEvent) - && !serviceRegistryConfig.isRegistryAutoDiscovery()) { - return; - } - - onMicroserviceInstanceChanged(changedEvent); - }, - open -> { - eventBus.post(new RecoveryEvent()); - }, - close -> { - }); - } - - private void onMicroserviceInstanceChanged(MicroserviceInstanceChangedEvent changedEvent) { - switch (changedEvent.getAction()) { - case CREATE: - LOGGER.info("microservice {}/{} REGISTERED an instance {}, {}.", - changedEvent.getKey().getAppId(), - changedEvent.getKey().getServiceName(), - changedEvent.getInstance().getInstanceId(), - changedEvent.getInstance().getEndpoints()); - break; - case DELETE: - LOGGER.info("microservice {}/{} UNREGISTERED an instance {}, {}.", - changedEvent.getKey().getAppId(), - changedEvent.getKey().getServiceName(), - changedEvent.getInstance().getInstanceId(), - changedEvent.getInstance().getEndpoints()); - break; - case UPDATE: - LOGGER.info("microservice {}/{} UPDATE an instance {} status or metadata, {}.", - changedEvent.getKey().getAppId(), - changedEvent.getKey().getServiceName(), - changedEvent.getInstance().getInstanceId(), - changedEvent.getInstance().getEndpoints()); - break; - default: - break; - } - - eventBus.post(changedEvent); - } - - private boolean needToWatch() { - return serviceRegistryConfig.isWatch() || serviceRegistryConfig.isRegistryAutoDiscovery(); - } - - private boolean isProviderInstancesChanged(MicroserviceInstanceChangedEvent changedEvent) { - return !Const.REGISTRY_APP_ID.equals(changedEvent.getKey().getAppId()) - && !Const.REGISTRY_SERVICE_NAME.equals(changedEvent.getKey().getServiceName()); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/ServiceCenterTask.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/ServiceCenterTask.java deleted file mode 100644 index 6d3fdbd2d8d..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/ServiceCenterTask.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import java.util.concurrent.TimeUnit; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; - -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.servicecomb.serviceregistry.task.event.ExceptionEvent; -import io.servicecomb.serviceregistry.task.event.ShutdownEvent; - -public class ServiceCenterTask implements Runnable { - private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterTask.class); - - private EventBus eventBus; - - private ServiceRegistryConfig serviceRegistryConfig; - - private MicroserviceServiceCenterTask microserviceServiceCenterTask; - - private boolean registerInstanceSuccess = false; - - private volatile boolean running = true; - - // for fast recovery, register interval is different with heartbeat interval - private int[] registerIntervalSeconds = new int[] {1, 2, 3, 10, 20, 30, 40, 50, 60}; - - private int registerRetryCount; - - private int interval; - - private ServiceCenterTaskMonitor serviceCenterTaskMonitor = new ServiceCenterTaskMonitor(); - - public ServiceCenterTask(EventBus eventBus, ServiceRegistryConfig serviceRegistryConfig, - MicroserviceServiceCenterTask microserviceServiceCenterTask) { - this.eventBus = eventBus; - this.serviceRegistryConfig = serviceRegistryConfig; - this.microserviceServiceCenterTask = microserviceServiceCenterTask; - - this.eventBus.register(this); - } - - public int getInterval() { - return interval; - } - - @Subscribe - public void onShutdown(ShutdownEvent event) { - LOGGER.info("service center task is shutdown."); - this.running = false; - } - - // messages given in register error - @Subscribe - public void onRegisterTask(AbstractRegisterTask task) { - LOGGER.info("read {} status is {}", task.getClass().getSimpleName(), task.taskStatus); - if (task.taskStatus == TaskStatus.FINISHED) { - registerInstanceSuccess = true; - } else { - onException(); - } - } - - // messages given in heartbeat - @Subscribe - public void onMicroserviceInstanceHeartbeatTask(MicroserviceInstanceHeartbeatTask task) { - if (task.getHeartbeatResult() != HeartbeatResult.SUCCESS) { - LOGGER.info("read MicroserviceInstanceHeartbeatTask status is {}", task.taskStatus); - onException(); - } - } - - // messages given in watch error - @Subscribe - public void onExceptionEvent(ExceptionEvent event) { - LOGGER.info("read exception event, message is :{}", event.getThrowable().getMessage()); - onException(); - } - - private void onException() { - if (registerInstanceSuccess) { - registerInstanceSuccess = false; - registerRetryCount = 0; - } - } - - public void init() { - microserviceServiceCenterTask.run(); - } - - @Override - public void run() { - while (running) { - try { - calcSleepInterval(); - TimeUnit.SECONDS.sleep(interval); - serviceCenterTaskMonitor.beginCycle(interval); - microserviceServiceCenterTask.run(); - serviceCenterTaskMonitor.endCycle(); - } catch (Throwable e) { - LOGGER.warn("unexpected exception caught from service center task. ", e); - continue; - } - } - } - - public void calcSleepInterval() { - if (registerInstanceSuccess) { - interval = serviceRegistryConfig.getHeartbeatInterval(); - return; - } - - if (registerRetryCount >= registerIntervalSeconds.length) { - registerRetryCount = registerIntervalSeconds.length - 1; - } - interval = registerIntervalSeconds[registerRetryCount]; - registerRetryCount++; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/ServiceCenterTaskMonitor.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/ServiceCenterTaskMonitor.java deleted file mode 100644 index d18ebbabf4a..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/ServiceCenterTaskMonitor.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * monitor ServiceCenterTask status and print diagnosis data - */ -public class ServiceCenterTaskMonitor { - private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterTaskMonitor.class); - - private static final long MAX_TIME_TAKEN = 1000; - - private long beginTime = 0; - - private long lastEndTime = 0; - - private int interval = -1; - - public void beginCycle(int interval) { - this.beginTime = System.currentTimeMillis(); - if (this.interval != interval) { - LOGGER.warn("sc task interval changed from {} to {}", this.interval, interval); - this.interval = interval; - } else { - if (this.beginTime - this.lastEndTime > interval * 1000 + MAX_TIME_TAKEN) { - LOGGER.warn("sc task postponed for {}ms for some reason.", this.beginTime - this.lastEndTime); - } - } - } - - public void endCycle() { - this.lastEndTime = System.currentTimeMillis(); - if (this.lastEndTime - this.beginTime > MAX_TIME_TAKEN) { - LOGGER.warn("sc task taken more than {}ms to execute", this.lastEndTime - this.beginTime); - } - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/TaskStatus.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/TaskStatus.java deleted file mode 100644 index c9040dd4d11..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/TaskStatus.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -public enum TaskStatus { - INIT, - READY, - FINISHED -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/event/ExceptionEvent.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/event/ExceptionEvent.java deleted file mode 100644 index c3ccadea57e..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/event/ExceptionEvent.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task.event; - -public class ExceptionEvent { - private Throwable throwable; - - public ExceptionEvent(Throwable throwable) { - this.throwable = throwable; - } - - public Throwable getThrowable() { - return throwable; - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/event/RecoveryEvent.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/event/RecoveryEvent.java deleted file mode 100644 index d5a5ebcc745..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/event/RecoveryEvent.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task.event; - -public class RecoveryEvent { - -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/event/ShutdownEvent.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/event/ShutdownEvent.java deleted file mode 100644 index 8eaafc3c99a..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/event/ShutdownEvent.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task.event; - -public class ShutdownEvent { - -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/utils/Timer.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/utils/Timer.java deleted file mode 100644 index 75ed34546ef..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/utils/Timer.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.utils; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Created by on 2017/3/12. - */ -public class Timer { - public static final int DEFAULT_MAX_TIMEOUT = 60; - - public static final int DEFAULT_RETRY_TIMES = 3; - - public static final int DEFAULT_STEP_SIZE = 10; - - private int max = 0; - - private int times = 0; // when less then zero then timing forever - - private int stepSize = 0; - - private boolean increase = true; - - private AtomicInteger current = new AtomicInteger(0); - - public static Timer newDefaultTimer() { - return new Timer(); - } - - public static Timer newForeverTimer() { - return new Timer(0, -1); - } - - public Timer() { - this(DEFAULT_MAX_TIMEOUT, DEFAULT_RETRY_TIMES, DEFAULT_STEP_SIZE, true); - } - - public Timer(int max) { - this(max, DEFAULT_RETRY_TIMES, DEFAULT_STEP_SIZE, true); - } - - public Timer(int max, int times) { - this(max, times, DEFAULT_STEP_SIZE, true); - } - - public Timer(int max, int times, int stepSize, boolean increase) { - this.max = (max <= 0 ? DEFAULT_MAX_TIMEOUT : max); - this.times = times; - this.stepSize = (stepSize <= 0 ? DEFAULT_STEP_SIZE : stepSize); - this.increase = increase; - } - - public int getMax() { - return this.max; - } - - public int getTimes() { - return this.times; - } - - public int getCurrent() { - return this.current.get(); - } - - public int getNextTimeout() { - if (this.increase) { - int timeout = (this.current.get() + 1) * this.stepSize; - return (timeout > this.max ? this.max : timeout); - } - return (this.stepSize > this.max ? this.max : this.stepSize); - } - - public int nextTimeout() { - int current = this.current.incrementAndGet(); - if (this.increase) { - int timeout = current * this.stepSize; - return (timeout > this.max ? this.max : timeout); - } - return (this.stepSize > this.max ? this.max : this.stepSize); - } - - public void sleep() throws TimerException { - if (this.times >= 0 && this.times <= this.current.get()) { - throw new TimerException(); - } - try { - TimeUnit.SECONDS.sleep(nextTimeout()); - } catch (InterruptedException e) { - } - } - - public void reset() { - this.current.set(0); - } -} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/utils/TimerException.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/utils/TimerException.java deleted file mode 100644 index e2baa6a9898..00000000000 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/utils/TimerException.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.utils; - -/** - * Created by on 2017/3/12. - */ -public class TimerException extends Exception { - - private static final long serialVersionUID = 4579811019552389524L; -} diff --git a/service-registry/src/main/resources/META-INF/services/io.servicecomb.foundation.auth.AuthHeaderProvider b/service-registry/src/main/resources/META-INF/services/io.servicecomb.foundation.auth.AuthHeaderProvider deleted file mode 100644 index ec8cc24b8aa..00000000000 --- a/service-registry/src/main/resources/META-INF/services/io.servicecomb.foundation.auth.AuthHeaderProvider +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -io.servicecomb.serviceregistry.client.http.EmptyAuthHeaderProvider diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/ArchaiusUtils.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/ArchaiusUtils.java deleted file mode 100644 index ea5656b60f7..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/ArchaiusUtils.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry; - -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicProperty; -import com.netflix.config.DynamicPropertyFactory; - -import mockit.Deencapsulation; - -public class ArchaiusUtils { - public static void resetConfig() { - Deencapsulation.setField(ConfigurationManager.class, "instance", null); - Deencapsulation.setField(ConfigurationManager.class, "customConfigurationInstalled", false); - Deencapsulation.setField(DynamicPropertyFactory.class, "config", null); - Deencapsulation.setField(DynamicPropertyFactory.class, "initializedWithDefaultConfig", false); - Deencapsulation.setField(DynamicProperty.class, "dynamicPropertySupportImpl", null); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/MicroServicePropertyExtendedStub.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/MicroServicePropertyExtendedStub.java deleted file mode 100644 index e47e4e992b5..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/MicroServicePropertyExtendedStub.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry; - -import java.util.HashMap; -import java.util.Map; - -import io.servicecomb.serviceregistry.api.PropertyExtended; - -public class MicroServicePropertyExtendedStub implements PropertyExtended { - - /* (non-Javadoc) - * @see io.servicecomb.serviceregistry.api.PropertyExtended#getExtendedProperties() - */ - @Override - public Map getExtendedProperties() { - Map properties = new HashMap<>(); - properties.put("ek0", "ev0"); - return properties; - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/TestRegistry.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/TestRegistry.java deleted file mode 100644 index b92612ae83d..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/TestRegistry.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry; - -import static io.servicecomb.serviceregistry.RegistryUtils.PUBLISH_ADDRESS; - -import java.net.InetAddress; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.message.BasicNameValuePair; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.ConcurrentMapConfiguration; -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicStringProperty; - -import io.servicecomb.config.ConfigUtil; -import io.servicecomb.foundation.common.net.NetUtils; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.registry.ServiceRegistryFactory; -import mockit.Deencapsulation; -import mockit.Expectations; -import mockit.Mocked; - -public class TestRegistry { - private static final AbstractConfiguration inMemoryConfig = new ConcurrentMapConfiguration(); - - @BeforeClass - public static void initSetup() throws Exception { - AbstractConfiguration dynamicConfig = ConfigUtil.createDynamicConfig(); - ConcurrentCompositeConfiguration configuration = new ConcurrentCompositeConfiguration(); - configuration.addConfiguration(dynamicConfig); - configuration.addConfiguration(inMemoryConfig); - - ConfigurationManager.install(configuration); - } - - @AfterClass - public static void classTeardown() { - Deencapsulation.setField(ConfigurationManager.class, "instance", null); - Deencapsulation.setField(ConfigurationManager.class, "customConfigurationInstalled", false); - Deencapsulation.setField(DynamicPropertyFactory.class, "config", null); - RegistryUtils.setServiceRegistry(null); - } - - @Before - public void setUp() throws Exception { - inMemoryConfig.clear(); - } - - @Test - public void testDelegate() { - ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal(); - serviceRegistry.init(); - serviceRegistry.run(); - - RegistryUtils.setServiceRegistry(serviceRegistry); - Assert.assertEquals(serviceRegistry, RegistryUtils.getServiceRegistry()); - - Assert.assertEquals(serviceRegistry.getServiceRegistryClient(), RegistryUtils.getServiceRegistryClient()); - Assert.assertEquals(serviceRegistry.getInstanceCacheManager(), RegistryUtils.getInstanceCacheManager()); - Assert.assertEquals(serviceRegistry.getInstanceVersionCacheManager(), - RegistryUtils.getInstanceVersionCacheManager()); - - Microservice microservice = RegistryUtils.getMicroservice(); - Assert.assertEquals(serviceRegistry.getMicroservice(), microservice); - Assert.assertEquals(serviceRegistry.getMicroserviceInstance(), RegistryUtils.getMicroserviceInstance()); - - List instanceList = RegistryUtils.findServiceInstance("default", "default", "0.0.1"); - Assert.assertEquals(1, instanceList.size()); - Assert.assertEquals(RegistryUtils.getMicroservice().getServiceId(), instanceList.get(0).getServiceId()); - - Map properties = new HashMap<>(); - properties.put("k", "v"); - RegistryUtils.updateInstanceProperties(properties); - Assert.assertEquals(properties, RegistryUtils.getMicroserviceInstance().getProperties()); - - Assert.assertEquals(microservice, RegistryUtils.getMicroservice(microservice.getServiceId())); - - Assert.assertEquals("default", RegistryUtils.getAppId()); - } - - @Test - public void testRegistryUtilGetPublishAddress(@Mocked InetAddress ethAddress) { - new Expectations(NetUtils.class) { - { - NetUtils.getHostAddress(); - result = "1.1.1.1"; - } - }; - String address = RegistryUtils.getPublishAddress(); - Assert.assertEquals("1.1.1.1", address); - - new Expectations(DynamicPropertyFactory.getInstance()) { - { - DynamicPropertyFactory.getInstance().getStringProperty(PUBLISH_ADDRESS, ""); - result = new DynamicStringProperty(PUBLISH_ADDRESS, "") { - public String get() { - return "127.0.0.1"; - } - }; - } - }; - Assert.assertEquals("127.0.0.1", RegistryUtils.getPublishAddress()); - - new Expectations(DynamicPropertyFactory.getInstance()) { - { - ethAddress.getHostAddress(); - result = "1.1.1.1"; - NetUtils.ensureGetInterfaceAddress("eth100"); - result = ethAddress; - DynamicPropertyFactory.getInstance().getStringProperty(PUBLISH_ADDRESS, ""); - result = new DynamicStringProperty(PUBLISH_ADDRESS, "") { - public String get() { - return "{eth100}"; - } - }; - } - }; - Assert.assertEquals("1.1.1.1", RegistryUtils.getPublishAddress()); - } - - @Test - public void testRegistryUtilGetHostName(@Mocked InetAddress ethAddress) { - new Expectations(NetUtils.class) { - { - NetUtils.getHostName(); - result = "testHostName"; - } - }; - String host = RegistryUtils.getPublishHostName(); - Assert.assertEquals("testHostName", host); - - inMemoryConfig.addProperty(PUBLISH_ADDRESS, "127.0.0.1"); - Assert.assertEquals("127.0.0.1", RegistryUtils.getPublishHostName()); - - new Expectations(DynamicPropertyFactory.getInstance()) { - { - ethAddress.getHostName(); - result = "testHostName"; - NetUtils.ensureGetInterfaceAddress("eth100"); - result = ethAddress; - } - }; - inMemoryConfig.addProperty(PUBLISH_ADDRESS, "{eth100}"); - Assert.assertEquals("testHostName", RegistryUtils.getPublishHostName()); - } - - @Test - public void testGetRealListenAddress() throws Exception { - new Expectations(NetUtils.class) { - { - NetUtils.getHostAddress(); - result = "1.1.1.1"; - } - }; - - Assert.assertEquals("rest://172.0.0.0:8080", RegistryUtils.getPublishAddress("rest", "172.0.0.0:8080")); - Assert.assertEquals(null, RegistryUtils.getPublishAddress("rest", null)); - - URI uri = new URI(RegistryUtils.getPublishAddress("rest", "0.0.0.0:8080")); - Assert.assertEquals("1.1.1.1:8080", uri.getAuthority()); - - new Expectations(DynamicPropertyFactory.getInstance()) { - { - DynamicPropertyFactory.getInstance().getStringProperty(PUBLISH_ADDRESS, ""); - result = new DynamicStringProperty(PUBLISH_ADDRESS, "") { - public String get() { - return "1.1.1.1"; - } - }; - } - }; - Assert.assertEquals("rest://1.1.1.1:8080", RegistryUtils.getPublishAddress("rest", "172.0.0.0:8080")); - - InetAddress ethAddress = Mockito.mock(InetAddress.class); - Mockito.when(ethAddress.getHostAddress()).thenReturn("1.1.1.1"); - new Expectations(DynamicPropertyFactory.getInstance()) { - { - NetUtils.ensureGetInterfaceAddress("eth20"); - result = ethAddress; - DynamicPropertyFactory.getInstance().getStringProperty(PUBLISH_ADDRESS, ""); - result = new DynamicStringProperty(PUBLISH_ADDRESS, "") { - public String get() { - return "{eth20}"; - } - }; - } - }; - String query = URLEncodedUtils.format(Arrays.asList(new BasicNameValuePair("country", "中 国")), - StandardCharsets.UTF_8.name()); - Assert.assertEquals("rest://1.1.1.1:8080?" + query, - RegistryUtils.getPublishAddress("rest", "172.0.0.0:8080?" + query)); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/TestMicroserviceKey.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/TestMicroserviceKey.java deleted file mode 100644 index 81cef7110d3..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/TestMicroserviceKey.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestMicroserviceKey { - - MicroserviceKey oMicroserviceKey = null; - - @Before - public void setUp() throws Exception { - oMicroserviceKey = new MicroserviceKey(); - } - - @After - public void tearDown() throws Exception { - oMicroserviceKey = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oMicroserviceKey.getAppId()); - Assert.assertNull(oMicroserviceKey.getTenant()); - Assert.assertNull(oMicroserviceKey.getServiceName()); - Assert.assertNull(oMicroserviceKey.getStage()); - Assert.assertNull(oMicroserviceKey.getVersion()); - } - - @Test - public void testIntializedValues() { - initFields(); //Initialize the Object - Assert.assertEquals("1", oMicroserviceKey.getVersion()); - Assert.assertEquals("testServiceName", oMicroserviceKey.getServiceName()); - Assert.assertEquals("Test", oMicroserviceKey.getStage()); - Assert.assertEquals("testTenantName", oMicroserviceKey.getTenant()); - Assert.assertEquals(Const.REGISTRY_APP_ID, oMicroserviceKey.getAppId()); - } - - private void initFields() { - oMicroserviceKey.setAppId(Const.REGISTRY_APP_ID); - oMicroserviceKey.setServiceName("testServiceName"); - oMicroserviceKey.setTenant("testTenantName"); - oMicroserviceKey.setVersion("1"); - oMicroserviceKey.setStage("Test"); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestBasePath.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestBasePath.java deleted file mode 100644 index e5c0f55c12c..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestBasePath.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.registry; - -import java.util.HashMap; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -/** - * Created by on 2017/3/29. - */ -public class TestBasePath { - private BasePath oBasePath; - - @Before - public void setUp() throws Exception { - oBasePath = new BasePath(); - } - - @After - public void tearDown() throws Exception { - oBasePath = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oBasePath.getPath()); - Assert.assertNull(oBasePath.getProperty()); - } - - @Test - public void testIntializedValues() { - initBasePath(); //Initialize the Values - Assert.assertEquals("a", oBasePath.getPath()); - Assert.assertNotNull(oBasePath.getProperty()); - } - - private void initBasePath() { - oBasePath.setPath("a"); - oBasePath.setProperty(new HashMap<>()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestHealthCheck.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestHealthCheck.java deleted file mode 100644 index 5321935daab..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestHealthCheck.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.registry; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestHealthCheck { - - HealthCheck oHealthCheck = null; - - @Before - public void setUp() throws Exception { - oHealthCheck = new HealthCheck(); - } - - @After - public void tearDown() throws Exception { - oHealthCheck = null; - } - - @Test - public void testDefaultValues() { - Assert.assertEquals(0, oHealthCheck.getInterval()); - Assert.assertEquals(0, oHealthCheck.getPort()); - Assert.assertEquals(0, oHealthCheck.getTimes()); - Assert.assertNull(oHealthCheck.getMode()); - Assert.assertEquals(0, oHealthCheck.getTTL()); - } - - @Test - public void testIntializedValues() { - initHealthCheck(); //Initialize the Values - Assert.assertEquals(10, oHealthCheck.getInterval()); - Assert.assertEquals(8080, oHealthCheck.getPort()); - Assert.assertEquals(2, oHealthCheck.getTimes()); - Assert.assertEquals(HealthCheckMode.PLATFORM, oHealthCheck.getMode()); - Assert.assertEquals(0, oHealthCheck.getTTL()); //TTL Values will changes based on the Mode - Assert.assertEquals("pull", oHealthCheck.getMode().getName()); - - //Testing Different modes of the HealthCheckMode - oHealthCheck.setMode(HealthCheckMode.HEARTBEAT); - Assert.assertEquals(HealthCheckMode.HEARTBEAT, oHealthCheck.getMode()); - Assert.assertEquals(30, oHealthCheck.getTTL()); //TTL Values will changes based on the Mode - Assert.assertEquals("push", oHealthCheck.getMode().getName()); - - oHealthCheck.setMode(HealthCheckMode.UNKNOWN); - Assert.assertEquals(HealthCheckMode.UNKNOWN, oHealthCheck.getMode()); - Assert.assertEquals(0, oHealthCheck.getTTL()); //TTL Values will changes based on the Mode - Assert.assertEquals("unknow", oHealthCheck.getMode().getName()); - } - - private void initHealthCheck() { - oHealthCheck.setInterval(10); - oHealthCheck.setMode(HealthCheckMode.PLATFORM); - oHealthCheck.setPort(8080); - oHealthCheck.setTimes(2); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestMicroService.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestMicroService.java deleted file mode 100644 index 31f56063bb5..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestMicroService.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.registry; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestMicroService { - - Microservice oMicroservice = null; - - Map oMapProperties = null; - - List oListSchemas = null; - - @Before - public void setUp() throws Exception { - oMicroservice = new Microservice(); - oMapProperties = new HashMap<>(); - oListSchemas = new ArrayList<>(); - } - - @After - public void tearDown() throws Exception { - oMicroservice = null; - oMapProperties = null; - oListSchemas = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oMicroservice.getAppId()); - Assert.assertNull(oMicroservice.getDescription()); - Assert.assertNull(oMicroservice.getLevel()); - Assert.assertEquals(0, oMicroservice.getProperties().size()); - Assert.assertEquals(0, oMicroservice.getSchemas().size()); - Assert.assertNull(oMicroservice.getServiceId()); - Assert.assertNull(oMicroservice.getServiceName()); - Assert.assertEquals(MicroserviceInstanceStatus.UP.toString(), oMicroservice.getStatus()); - Assert.assertNull(oMicroservice.getVersion()); - Assert.assertEquals(0, oMicroservice.getPaths().size()); - } - - @Test - public void testIntializedValues() { - initMicroservice(); //Initialize the Object - Assert.assertEquals("testAppID", oMicroservice.getAppId()); - Assert.assertEquals("This is the test", oMicroservice.getDescription()); - Assert.assertEquals("INFO", oMicroservice.getLevel()); - Assert.assertEquals("testServiceID", oMicroservice.getServiceId()); - Assert.assertEquals("testServiceName", oMicroservice.getServiceName()); - Assert.assertEquals(MicroserviceInstanceStatus.DOWN.toString(), oMicroservice.getStatus()); - Assert.assertEquals("1.0.0", oMicroservice.getVersion()); - Assert.assertEquals("fakeProxy", oMicroservice.getProperties().get("proxy")); - Assert.assertEquals(1, oMicroservice.getSchemas().size()); - Assert.assertEquals(1, oMicroservice.getPaths().size()); - } - - private void initMicroservice() { - oMicroservice.setAppId("testAppID"); - oMicroservice.setDescription("This is the test"); - oMicroservice.setLevel("INFO"); - oMicroservice.setServiceId("testServiceID"); - oMicroservice.setServiceName("testServiceName"); - oMicroservice.setStatus(MicroserviceInstanceStatus.DOWN.toString()); - oMicroservice.setVersion("1.0.0"); - oMapProperties.put("proxy", "fakeProxy"); - oListSchemas.add("testSchemas"); - oMicroservice.setProperties(oMapProperties); - oMicroservice.setSchemas(oListSchemas); - oMicroservice.getPaths().add(new BasePath()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestMicroServiceInstance.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestMicroServiceInstance.java deleted file mode 100644 index 6ee05a51432..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestMicroServiceInstance.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.registry; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicPropertyFactory; - -import io.servicecomb.config.ConfigUtil; -import io.servicecomb.serviceregistry.RegistryUtils; -import mockit.Deencapsulation; - -public class TestMicroServiceInstance { - - MicroserviceInstance oMicroserviceInstance = null; - - Map oMapProperties = null; - - List oListEndpoints = null; - - HealthCheck oMockHealthCheck = null; - - @AfterClass - public static void classTeardown() { - Deencapsulation.setField(ConfigurationManager.class, "instance", null); - Deencapsulation.setField(ConfigurationManager.class, "customConfigurationInstalled", false); - Deencapsulation.setField(DynamicPropertyFactory.class, "config", null); - RegistryUtils.setServiceRegistry(null); - } - - @Before - public void setUp() throws Exception { - oMicroserviceInstance = new MicroserviceInstance(); - oMapProperties = new HashMap<>(); - oListEndpoints = new ArrayList<>(); - oMockHealthCheck = Mockito.mock(HealthCheck.class); - } - - @After - public void tearDown() throws Exception { - oMicroserviceInstance = null; - oMapProperties = null; - oListEndpoints = null; - oMockHealthCheck = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oMicroserviceInstance.getHostName()); - Assert.assertNull(oMicroserviceInstance.getInstanceId()); - Assert.assertNull(oMicroserviceInstance.getServiceId()); - Assert.assertEquals(0, oMicroserviceInstance.getProperties().size()); - Assert.assertEquals(0, oMicroserviceInstance.getEndpoints().size()); - Assert.assertNull(oMicroserviceInstance.getHealthCheck()); - Assert.assertNull(oMicroserviceInstance.getStage()); - Assert.assertEquals(MicroserviceInstanceStatus.UP, oMicroserviceInstance.getStatus()); - } - - @Test - public void testIntializedValues() { - initMicroserviceInstance(); //Initialize the Object - Assert.assertEquals("testHostName", oMicroserviceInstance.getHostName()); - Assert.assertEquals("testInstanceID", oMicroserviceInstance.getInstanceId()); - Assert.assertEquals(1, oMicroserviceInstance.getEndpoints().size()); - Assert.assertEquals("testServiceID", oMicroserviceInstance.getServiceId()); - Assert.assertEquals(oMockHealthCheck, oMicroserviceInstance.getHealthCheck()); - Assert.assertEquals(MicroserviceInstanceStatus.DOWN, oMicroserviceInstance.getStatus()); - Assert.assertEquals("Test", oMicroserviceInstance.getStage()); - Assert.assertEquals("china", oMicroserviceInstance.getProperties().get("region")); - } - - private void initMicroserviceInstance() { - oMicroserviceInstance.setHostName("testHostName"); - oMicroserviceInstance.setInstanceId("testInstanceID"); - oMicroserviceInstance.setStage("Test"); - oMicroserviceInstance.setServiceId("testServiceID"); - oMicroserviceInstance.setStatus(MicroserviceInstanceStatus.DOWN); - oMapProperties.put("region", "china"); - oListEndpoints.add("testEndpoints"); - oMicroserviceInstance.setProperties(oMapProperties); - oMicroserviceInstance.setEndpoints(oListEndpoints); - oMicroserviceInstance.setHealthCheck(oMockHealthCheck); - } - - @Test - public void testCreateMicroserviceInstanceFromFile() { - AbstractConfiguration config = ConfigUtil.createDynamicConfig(); - ConcurrentCompositeConfiguration configuration = new ConcurrentCompositeConfiguration(); - configuration.addConfiguration(config); - ConfigurationManager.install(configuration); - MicroserviceInstance instance = MicroserviceInstance.createFromDefinition(config); - Assert.assertEquals(instance.getDataCenterInfo().getName(), "myDC"); - Assert.assertEquals(instance.getDataCenterInfo().getRegion(), "my-Region"); - Assert.assertEquals(instance.getDataCenterInfo().getAvailableZone(), "my-Zone"); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestMicroserviceFactory.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestMicroserviceFactory.java deleted file mode 100644 index edfdb178daf..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/registry/TestMicroserviceFactory.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.registry; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.config.archaius.sources.MicroserviceConfigLoader; -import io.servicecomb.serviceregistry.definition.DefinitionConst; -import io.servicecomb.serviceregistry.definition.MicroserviceDefinition; -import mockit.Deencapsulation; - -public class TestMicroserviceFactory { - @Test - public void testAllowCrossApp() { - MicroserviceFactory factory = new MicroserviceFactory(); - Map propertiesMap = new HashMap<>(); - Assert.assertFalse(Deencapsulation.invoke(factory, "allowCrossApp", propertiesMap)); - - propertiesMap.put(DefinitionConst.allowCrossAppKey, "true"); - Assert.assertTrue(Deencapsulation.invoke(factory, "allowCrossApp", propertiesMap)); - - propertiesMap.put(DefinitionConst.allowCrossAppKey, "false"); - Assert.assertFalse(Deencapsulation.invoke(factory, "allowCrossApp", propertiesMap)); - - propertiesMap.put(DefinitionConst.allowCrossAppKey, "asfas"); - Assert.assertFalse(Deencapsulation.invoke(factory, "allowCrossApp", propertiesMap)); - } - - @Test - public void testInit() { - MicroserviceConfigLoader loader = new MicroserviceConfigLoader(); - loader.loadAndSort(); - - MicroserviceDefinition microserviceDefinition = new MicroserviceDefinition(loader.getConfigModels()); - MicroserviceFactory factory = new MicroserviceFactory(); - Microservice microservice = factory.create(microserviceDefinition); - - String microserviceName = "default"; - - Assert.assertEquals(microserviceName, microservice.getServiceName()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/request/TestCreateSchemaRequest.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/request/TestCreateSchemaRequest.java deleted file mode 100644 index 50d0278681b..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/request/TestCreateSchemaRequest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.api.request; - -import org.junit.Assert; -import org.junit.Test; - -public class TestCreateSchemaRequest { - @Test - public void testProperties() { - CreateSchemaRequest request = new CreateSchemaRequest(); - - Assert.assertEquals(null, request.getSchema()); - - request.setSchema("schema"); - Assert.assertEquals("schema", request.getSchema()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/request/TestCreateServiceRequest.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/request/TestCreateServiceRequest.java deleted file mode 100644 index e5c71ab6143..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/request/TestCreateServiceRequest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.request; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.serviceregistry.api.registry.Microservice; - -public class TestCreateServiceRequest { - - CreateServiceRequest oCreateServiceRequest = null; - - Microservice oMockMicroservice = null; - - @Before - public void setUp() throws Exception { - oCreateServiceRequest = new CreateServiceRequest(); - oMockMicroservice = Mockito.mock(Microservice.class); - } - - @After - public void tearDown() throws Exception { - oCreateServiceRequest = null; - oMockMicroservice = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oCreateServiceRequest.getService()); - } - - @Test - public void testIntializedValues() { - initCreateServiceRequest(); //Initialize the Object - Assert.assertEquals(oMockMicroservice, oCreateServiceRequest.getService()); - } - - private void initCreateServiceRequest() { - oCreateServiceRequest.setService(oMockMicroservice); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/request/TestRegisterInstanceRequest.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/request/TestRegisterInstanceRequest.java deleted file mode 100644 index 68fd9973aa5..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/request/TestRegisterInstanceRequest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.request; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; - -public class TestRegisterInstanceRequest { - - RegisterInstanceRequest oRegisterInstanceRequest = null; - - MicroserviceInstance oMockMicroserviceInstance = null; - - @Before - public void setUp() throws Exception { - oRegisterInstanceRequest = new RegisterInstanceRequest(); - oMockMicroserviceInstance = Mockito.mock(MicroserviceInstance.class); - } - - @After - public void tearDown() throws Exception { - oRegisterInstanceRequest = null; - oMockMicroserviceInstance = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oRegisterInstanceRequest.getInstance()); - } - - @Test - public void testIntializedValues() { - initRegisterInstanceRequest(); //Initialize the Object - Assert.assertEquals(oMockMicroserviceInstance, oRegisterInstanceRequest.getInstance()); - } - - private void initRegisterInstanceRequest() { - oRegisterInstanceRequest.setInstance(oMockMicroserviceInstance); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/request/TestUpdatePropertiesRequest.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/request/TestUpdatePropertiesRequest.java deleted file mode 100644 index 8827194dbc9..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/request/TestUpdatePropertiesRequest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.api.request; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - -public class TestUpdatePropertiesRequest { - @Test - public void testProperties() { - UpdatePropertiesRequest request = new UpdatePropertiesRequest(); - - Assert.assertEquals(null, request.getProperties()); - - Map properties = new HashMap<>(); - properties.put("k", "v"); - request.setProperties(properties); - Assert.assertEquals(properties, request.getProperties()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestCreateServiceResponse.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestCreateServiceResponse.java deleted file mode 100644 index 61bf2377f96..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestCreateServiceResponse.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestCreateServiceResponse { - - CreateServiceResponse oCreateServiceResponse = null; - - @Before - public void setUp() throws Exception { - oCreateServiceResponse = new CreateServiceResponse(); - } - - @After - public void tearDown() throws Exception { - oCreateServiceResponse = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oCreateServiceResponse.getServiceId()); - } - - @Test - public void testIntializedValues() { - initFields(); //Initialize the Object - Assert.assertEquals("testServiceId", oCreateServiceResponse.getServiceId()); - } - - private void initFields() { - oCreateServiceResponse.setServiceId("testServiceId"); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestFindInstancesResponse.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestFindInstancesResponse.java deleted file mode 100644 index 882e0eb97a8..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestFindInstancesResponse.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; - -public class TestFindInstancesResponse { - - FindInstancesResponse oFindInstancesResponse = null; - - List oListMicroserviceInstance = null; - - @Before - public void setUp() throws Exception { - oFindInstancesResponse = new FindInstancesResponse(); - oListMicroserviceInstance = new ArrayList<>(); - oListMicroserviceInstance.add(Mockito.mock(MicroserviceInstance.class)); - } - - @After - public void tearDown() throws Exception { - oFindInstancesResponse = null; - oListMicroserviceInstance = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oFindInstancesResponse.getInstances()); - } - - @Test - public void testIntializedValues() { - initFields(); //Initialize the Object - Assert.assertEquals(1, oFindInstancesResponse.getInstances().size()); - } - - private void initFields() { - oFindInstancesResponse.setInstances(oListMicroserviceInstance); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestGetAllServicesResponse.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestGetAllServicesResponse.java deleted file mode 100644 index e8818989ecf..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestGetAllServicesResponse.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.serviceregistry.api.registry.Microservice; - -public class TestGetAllServicesResponse { - - GetAllServicesResponse oGetAllServicesResponse = null; - - Microservice oMockMicroservice = null; - - @Before - public void setUp() throws Exception { - oGetAllServicesResponse = new GetAllServicesResponse(); - oMockMicroservice = Mockito.mock(Microservice.class); - } - - @After - public void tearDown() throws Exception { - oGetAllServicesResponse = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oGetAllServicesResponse.getServices()); - } - - @Test - public void testIntializedValues() { - initFields(); //Initialize the Object - List list = oGetAllServicesResponse.getServices(); - Assert.assertEquals(oMockMicroservice, list.get(0)); - } - - private void initFields() { - List list = new ArrayList<>(); - list.add(oMockMicroservice); - oGetAllServicesResponse.setServices(list); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestGetExistenceResponse.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestGetExistenceResponse.java deleted file mode 100644 index 423d03edbdd..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestGetExistenceResponse.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestGetExistenceResponse { - - GetExistenceResponse oGetExistenceResponse = null; - - @Before - public void setUp() throws Exception { - oGetExistenceResponse = new GetExistenceResponse(); - } - - @After - public void tearDown() throws Exception { - oGetExistenceResponse = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oGetExistenceResponse.getSchemaId()); - Assert.assertNull(oGetExistenceResponse.getServiceId()); - } - - @Test - public void testIntializedValues() { - initFields(); //Initialize the Object - Assert.assertEquals("testSchemaId", oGetExistenceResponse.getSchemaId()); - Assert.assertEquals("testServiceId", oGetExistenceResponse.getServiceId()); - } - - private void initFields() { - oGetExistenceResponse.setSchemaId("testSchemaId"); - oGetExistenceResponse.setServiceId("testServiceId"); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestGetInstancesResponse.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestGetInstancesResponse.java deleted file mode 100644 index ee4e8c2502f..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestGetInstancesResponse.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; - -public class TestGetInstancesResponse { - - GetInstancesResponse oGetInstancesResponse = null; - - List oListMicroserviceInstance = null; - - @Before - public void setUp() throws Exception { - oGetInstancesResponse = new GetInstancesResponse(); - oListMicroserviceInstance = new ArrayList<>(); - oListMicroserviceInstance.add(Mockito.mock(MicroserviceInstance.class)); - } - - @After - public void tearDown() throws Exception { - oGetInstancesResponse = null; - oListMicroserviceInstance = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oGetInstancesResponse.getInstances()); - } - - @Test - public void testIntializedValues() { - initFields(); //Initialize the Object - Assert.assertEquals(1, oGetInstancesResponse.getInstances().size()); - } - - private void initFields() { - oGetInstancesResponse.setInstances(oListMicroserviceInstance); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestGetServiceResponse.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestGetServiceResponse.java deleted file mode 100644 index 9cbf6a4ff88..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestGetServiceResponse.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.serviceregistry.api.registry.Microservice; - -public class TestGetServiceResponse { - - GetServiceResponse oGetServiceResponse = null; - - Microservice oMockMicroservice = null; - - @Before - public void setUp() throws Exception { - oGetServiceResponse = new GetServiceResponse(); - oMockMicroservice = Mockito.mock(Microservice.class); - } - - @After - public void tearDown() throws Exception { - oGetServiceResponse = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oGetServiceResponse.getService()); - } - - @Test - public void testIntializedValues() { - initFields(); //Initialize the Object - Assert.assertEquals(oMockMicroservice, oGetServiceResponse.getService()); - } - - private void initFields() { - oGetServiceResponse.setService(oMockMicroservice); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestHeartbeatResponse.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestHeartbeatResponse.java deleted file mode 100644 index c107fa22baf..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestHeartbeatResponse.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestHeartbeatResponse { - - HeartbeatResponse oHeartbeatResponse = null; - - @Before - public void setUp() throws Exception { - oHeartbeatResponse = new HeartbeatResponse(); - } - - @After - public void tearDown() throws Exception { - oHeartbeatResponse = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oHeartbeatResponse.getMessage()); - Assert.assertEquals(false, oHeartbeatResponse.isOk()); - } - - @Test - public void testIntializedValues() { - initFields(); //Initialize the Object - Assert.assertEquals("testMessage", oHeartbeatResponse.getMessage()); - Assert.assertEquals(true, oHeartbeatResponse.isOk()); - } - - private void initFields() { - oHeartbeatResponse.setMessage("testMessage"); - oHeartbeatResponse.setOk(true); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestMicroserviceInstanceChangedEvent.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestMicroserviceInstanceChangedEvent.java deleted file mode 100644 index 65f116fd9f1..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestMicroserviceInstanceChangedEvent.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.serviceregistry.api.MicroserviceKey; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.api.registry.WatchAction; - -public class TestMicroserviceInstanceChangedEvent { - - MicroserviceInstanceChangedEvent oMicroserviceInstanceChangedEvent = null; - - MicroserviceKey oMockMicroserviceKey = null; - - MicroserviceInstance oMockMicroserviceInstance = null; - - @Before - public void setUp() throws Exception { - oMicroserviceInstanceChangedEvent = new MicroserviceInstanceChangedEvent(); - oMockMicroserviceKey = Mockito.mock(MicroserviceKey.class); - oMockMicroserviceInstance = Mockito.mock(MicroserviceInstance.class); - } - - @After - public void tearDown() throws Exception { - oMicroserviceInstanceChangedEvent = null; - oMockMicroserviceKey = null; - oMockMicroserviceInstance = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oMicroserviceInstanceChangedEvent.getAction()); - Assert.assertNull(oMicroserviceInstanceChangedEvent.getInstance()); - Assert.assertNull(oMicroserviceInstanceChangedEvent.getKey()); - } - - @Test - public void testIntializedValues() { - initFields(); //Initialize the Object - Assert.assertEquals(WatchAction.CREATE, oMicroserviceInstanceChangedEvent.getAction()); - Assert.assertEquals("CREATE", oMicroserviceInstanceChangedEvent.getAction().getName()); - Assert.assertEquals(oMockMicroserviceInstance, oMicroserviceInstanceChangedEvent.getInstance()); - Assert.assertEquals(oMockMicroserviceKey, oMicroserviceInstanceChangedEvent.getKey()); - - //Test Different Actions - oMicroserviceInstanceChangedEvent.setAction(WatchAction.UPDATE); - Assert.assertEquals("UPDATE", oMicroserviceInstanceChangedEvent.getAction().getName()); - - oMicroserviceInstanceChangedEvent.setAction(WatchAction.DELETE); - Assert.assertEquals("DELETE", oMicroserviceInstanceChangedEvent.getAction().getName()); - } - - private void initFields() { - oMicroserviceInstanceChangedEvent.setInstance(oMockMicroserviceInstance); - oMicroserviceInstanceChangedEvent.setKey(oMockMicroserviceKey); - oMicroserviceInstanceChangedEvent.setAction(WatchAction.CREATE); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestRegisterInstanceResponse.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestRegisterInstanceResponse.java deleted file mode 100644 index dd297a9689b..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestRegisterInstanceResponse.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.api.response; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestRegisterInstanceResponse { - - RegisterInstanceResponse oRegisterInstanceResponse = null; - - @Before - public void setUp() throws Exception { - oRegisterInstanceResponse = new RegisterInstanceResponse(); - } - - @After - public void tearDown() throws Exception { - oRegisterInstanceResponse = null; - } - - @Test - public void testDefaultValues() { - Assert.assertNull(oRegisterInstanceResponse.getInstanceId()); - } - - @Test - public void testIntializedValues() { - initFields(); //Initialize the Object - Assert.assertEquals("testInstanceID", oRegisterInstanceResponse.getInstanceId()); - } - - private void initFields() { - oRegisterInstanceResponse.setInstanceId("testInstanceID"); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/cache/TestInstanceCache.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/cache/TestInstanceCache.java deleted file mode 100644 index 33463c79f52..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/cache/TestInstanceCache.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.cache; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstanceStatus; - -public class TestInstanceCache { - private static InstanceCache instanceCache = null; - - @BeforeClass - public static void beforeClass() { - MicroserviceInstance instance = new MicroserviceInstance(); - instance.setStatus(MicroserviceInstanceStatus.UP); - List endpoints = new ArrayList<>(); - endpoints.add("rest://127.0.0.1:8080"); - instance.setEndpoints(endpoints); - instance.setInstanceId("1"); - - Map instMap = new HashMap<>(); - instMap.put(instance.getInstanceId(), instance); - instanceCache = new InstanceCache("testAppID", "testMicroServiceName", "1.0", instMap); - } - - @Test - public void testGetMethod() { - Assert.assertEquals("testAppID", instanceCache.getAppId()); - Assert.assertEquals("testMicroServiceName", instanceCache.getMicroserviceName()); - Assert.assertEquals("1.0", instanceCache.getMicroserviceVersionRule()); - Assert.assertNotNull(instanceCache.getInstanceMap()); - } - - @Test - public void testGetOrCreateTransportMap() { - Map> transportMap = instanceCache.getOrCreateTransportMap(); - Assert.assertEquals(1, transportMap.size()); - } - - @Test - public void testCacheChanged() { - InstanceCache newCache = - new InstanceCache("testAppID", "testMicroServiceName", "1.0", instanceCache.getInstanceMap()); - Assert.assertTrue(instanceCache.cacheChanged(newCache)); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/cache/TestInstanceCacheManager.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/cache/TestInstanceCacheManager.java deleted file mode 100644 index ec815348da0..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/cache/TestInstanceCacheManager.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.cache; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.api.MicroserviceKey; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.WatchAction; -import io.servicecomb.serviceregistry.api.response.MicroserviceInstanceChangedEvent; -import io.servicecomb.serviceregistry.registry.ServiceRegistryFactory; - -public class TestInstanceCacheManager { - @Test - public void testInstanceUpdate() { - ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal(); - Microservice microservice = serviceRegistry.getMicroservice(); - serviceRegistry.init(); - InstanceCacheManager oInstanceCacheManager = serviceRegistry.getInstanceCacheManager(); - - MicroserviceInstanceChangedEvent oChangedEnvent = new MicroserviceInstanceChangedEvent(); - oChangedEnvent.setAction(WatchAction.UPDATE); - MicroserviceKey oKey = new MicroserviceKey(); - oKey.setAppId(microservice.getAppId()); - oKey.setVersion(microservice.getVersion()); - oKey.setServiceName(microservice.getServiceName()); - oChangedEnvent.setKey(oKey); - oChangedEnvent.setInstance(microservice.getIntance()); - oInstanceCacheManager.onInstanceUpdate(oChangedEnvent); - oChangedEnvent.setAction(WatchAction.DELETE); - oInstanceCacheManager.onInstanceUpdate(oChangedEnvent); - oChangedEnvent.setAction(WatchAction.CREATE); - oInstanceCacheManager.onInstanceUpdate(oChangedEnvent); - Assert.assertEquals("UP", microservice.getIntance().getStatus().toString()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImplTest.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImplTest.java deleted file mode 100644 index dad86391d4f..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImplTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client; - -import java.io.InputStream; -import java.util.List; - -import org.hamcrest.core.Is; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; - -public class LocalServiceRegistryClientImplTest { - - InputStream is; - - @Before - public void loadRegistryFile() { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - is = loader.getResourceAsStream("registry.yaml"); - } - - @Test - public void testLoadRegistryFile() { - LocalServiceRegistryClientImpl registryClient = new LocalServiceRegistryClientImpl(is); - Assert.assertNotNull(registryClient); - Assert.assertThat(registryClient.getAllMicroservices().size(), Is.is(1)); - List m = registryClient.findServiceInstance("", "myapp", "springmvctest", ""); - Assert.assertEquals(1, m.size()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/ServiceRegistryDemo.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/client/ServiceRegistryDemo.java deleted file mode 100644 index 5f88edad23e..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/ServiceRegistryDemo.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.Log4jUtils; -import io.servicecomb.serviceregistry.RegistryUtils; - -public class ServiceRegistryDemo { - public static void main(String[] args) throws Exception { - Log4jUtils.init(); - BeanUtils.init(); - - // 1、自注册 2、服务心跳 3、实例变化监听 - RegistryUtils.init(); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/TestEndpoints.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/client/TestEndpoints.java deleted file mode 100644 index bc5e02bedb5..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/TestEndpoints.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.client; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; - -public class TestEndpoints { - @Test - public void testAll() { - Endpoints ep = new Endpoints(); - - List instances = new ArrayList<>(); - - ep.setAppId("appId"); - ep.setServiceName("sn"); - ep.setInstances(instances); - ep.setVersion("v1"); - - Assert.assertEquals("appId", ep.getAppId()); - Assert.assertEquals("sn", ep.getServiceName()); - Assert.assertEquals(instances, ep.getInstances()); - Assert.assertEquals("v1", ep.getVersion()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/TestIpPortManager.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/client/TestIpPortManager.java deleted file mode 100644 index bee01c1e863..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/TestIpPortManager.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.cache.CacheEndpoint; -import io.servicecomb.serviceregistry.cache.InstanceCache; -import io.servicecomb.serviceregistry.registry.AbstractServiceRegistry; -import io.servicecomb.serviceregistry.registry.ServiceRegistryFactory; -import mockit.Deencapsulation; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestIpPortManager { - @Mocked - ServiceRegistryClient srClient; - - AbstractServiceRegistry serviceRegistry; - - IpPortManager manager; - - @Before - public void setup() { - serviceRegistry = (AbstractServiceRegistry) ServiceRegistryFactory.createLocal(); - serviceRegistry.setServiceRegistryClient(srClient); - serviceRegistry.init(); - - manager = serviceRegistry.getIpPortManager(); - } - - InstanceCache instanceCache = null; - - Map> oListTransportMap = null; - - @Test - public void testCreateServiceRegistryCache() { - List list = new ArrayList(); - MicroserviceInstance e1 = new MicroserviceInstance(); - List endpoints = new ArrayList<>(); - endpoints.add("rest://127.0.0.1:8080"); - e1.setEndpoints(endpoints); - list.add(e1); - - new Expectations() { - { - srClient.findServiceInstance(anyString, anyString, anyString, anyString); - result = list; - } - }; - - try { - manager.createServiceRegistryCache(); - Assert.assertNotNull(manager.get()); - Assert.assertNull(manager.next()); - } catch (Exception e) { - Assert.fail(e.getMessage()); - } - } - - @Test - public void testCreateServiceRegistryCacheWithInstanceCache() { - - boolean validAssert = true; - - List list = new ArrayList(); - MicroserviceInstance e1 = new MicroserviceInstance(); - list.add(e1); - - new MockUp() { - - @Mock - public List findServiceInstance(String appId, String serviceName, - String versionRule) { - return list; - } - }; - - try { - Deencapsulation.setField(manager, "instanceCache", Mockito.mock(InstanceCache.class)); - manager.createServiceRegistryCache(); - Assert.assertNotNull(manager.get()); - Assert.assertNull(manager.next()); - } catch (Exception e) { - - validAssert = false; - } - - Assert.assertTrue(validAssert); - } - - @Test - public void testGetDefaultIpPortAddressesisNUll() { - new MockUp() { - @Mock - public ArrayList getDefaultIpPortList() { - return null; - } - }; - Assert.assertNull(manager.getDefaultIpPort()); - } - - @Test - public void testNextDefaultIpPortAddressesisNUll() { - new MockUp() { - @Mock - public ArrayList getDefaultIpPortList() { - return null; - } - }; - Assert.assertNull(manager.nextDefaultIpPort()); - } - - @Test - public void testNextDefaultIpPortAddressesSize() { - - new MockUp() { - @Mock - public ArrayList getDefaultIpPortList() { - List ipPorts = new ArrayList(); - IpPort ipport1 = new IpPort(); - ipport1.setHostOrIp("hostOrIp"); - IpPort ipport2 = new IpPort(); - ipport2.setHostOrIp("hostOrIp"); - ipPorts.add(ipport1); - ipPorts.add(ipport2); - return (ArrayList) ipPorts; - } - }; - IpPort ipPort = manager.nextDefaultIpPort(); - Assert.assertEquals("hostOrIp", ipPort.getHostOrIp()); - } - - @Test - public void testNextDefaultIpPortAddresses() { - - Assert.assertNull(manager.nextDefaultIpPort()); - } - - @Test - public void testNextAddressesSize() { - - new MockUp() { - @Mock - public List getAddressCaches() { - List addressCaches = new ArrayList<>(); - addressCaches.add(new CacheEndpoint("http://127.0.0.3:8080", null)); - addressCaches.add(new CacheEndpoint("http://127.0.0.3:8080", null)); - return addressCaches; - } - }; - - Map addressCanUsed = new HashMap(); - addressCanUsed.get(0); - addressCanUsed.put(0, true); - Deencapsulation.setField(manager, "addressCanUsed", addressCanUsed); - Assert.assertEquals(manager.next().getHostOrIp(), "127.0.0.3"); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/MockAuthHeaderProvider.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/MockAuthHeaderProvider.java deleted file mode 100644 index 8da495ccab5..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/MockAuthHeaderProvider.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import java.util.HashMap; -import java.util.Map; - -import io.servicecomb.foundation.auth.AuthHeaderProvider; - -public class MockAuthHeaderProvider implements AuthHeaderProvider { - @Override - public Map authHeaders() { - HashMap headers = new HashMap<>(); - headers.put("X-Service-AK", "blah..."); - return headers; - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/RestUtilsTest.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/RestUtilsTest.java deleted file mode 100644 index 20a765b4833..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/RestUtilsTest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -import org.junit.Test; - -import io.vertx.core.MultiMap; - -public class RestUtilsTest { - @Test - public void defaultHeadersContainServiceRegistryAndAuthentication() throws Exception { - MultiMap headers = RestUtils.getDefaultHeaders(); - - assertThat(headers.get("Content-Type"), is("application/json")); - assertThat(headers.get("User-Agent"), is("cse-serviceregistry-client/1.0.0")); - assertThat(headers.get("x-domain-name"), is("default")); - assertThat(headers.get("X-Service-AK"), is("blah...")); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/TestClientPool.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/TestClientPool.java deleted file mode 100644 index 65ed75f9c74..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/TestClientPool.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import org.junit.Test; - -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.vertx.core.http.HttpVersion; -import mockit.Mock; -import mockit.MockUp; - -public class TestClientPool { - @Test - public void testHttpClientPool() { - new MockUp() { - @Mock - public HttpVersion getHttpVersion() { - return HttpVersion.HTTP_2; - } - - @Mock - public boolean isSsl() { - return true; - } - }; - HttpClientPool.INSTANCE.create(); - } - - @Test - public void testWebsocketClientPool() { - new MockUp() { - @Mock - public HttpVersion getHttpVersion() { - return HttpVersion.HTTP_2; - } - - @Mock - public boolean isSsl() { - return true; - } - }; - WebsocketClientPool.INSTANCE.create(); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/TestClienthttp.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/TestClienthttp.java deleted file mode 100644 index d07d1e0671f..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/TestClienthttp.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import java.util.ArrayList; -import java.util.concurrent.CountDownLatch; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.foundation.vertx.AsyncResultCallback; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceFactory; -import io.servicecomb.serviceregistry.cache.InstanceCacheManager; -import io.servicecomb.serviceregistry.client.Endpoints; -import io.servicecomb.serviceregistry.client.IpPortManager; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.vertx.core.Handler; -import io.vertx.core.buffer.Buffer; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestClienthttp { - @SuppressWarnings("unchecked") - @Test - public void testServiceRegistryClientImpl(@Mocked IpPortManager manager) { - IpPort ipPort = new IpPort("127.0.0.1", 8853); - new Expectations() { - { - manager.get(); - result = ipPort; - } - }; - - new MockUp() { - @Mock - public void await() throws InterruptedException { - } - }; - new MockUp() { - @Mock - void httpDo(RequestContext requestContext, Handler responseHandler) { - } - }; - - new MockUp() { - @Mock - void open(IpPort ipPort, String url, Handler onOpen, Handler onClose, - Handler onMessage, Handler onException, - Handler onConnectFailed) { - } - }; - - MicroserviceFactory microserviceFactory = new MicroserviceFactory(); - Microservice microservice = microserviceFactory.create("app", "ms"); - - ServiceRegistryClientImpl oClient = new ServiceRegistryClientImpl(manager); - oClient.init(); - oClient.registerMicroservice(microservice); - oClient.registerMicroserviceInstance(microservice.getIntance()); - Assert.assertEquals(null, oClient.getMicroservice(microservice.getServiceId())); - Assert.assertEquals(null, oClient.getMicroserviceInstance("testConsumerID", "testproviderID")); - Assert.assertEquals(null, - oClient.findServiceInstance(microservice.getServiceId(), - microservice.getAppId(), - microservice.getServiceName(), - microservice.getVersion())); - Assert.assertEquals(null, - oClient.getMicroserviceId(microservice.getAppId(), - microservice.getServiceName(), - microservice.getVersion())); - Assert.assertEquals(null, - oClient.heartbeat(microservice.getServiceId(), - microservice.getIntance().getInstanceId())); - oClient.watch("", - Mockito.mock(AsyncResultCallback.class)); - Assert.assertEquals(false, - oClient.unregisterMicroserviceInstance(microservice.getServiceId(), - microservice.getIntance().getInstanceId())); - } - - @Test - public void testRequestContext() { - RequestContext oContext = new RequestContext(); - oContext.setUri("//test"); - oContext.setMethod(io.vertx.core.http.HttpMethod.POST); - oContext.setIpPort(new IpPort("145.0.0.1", 8080)); - oContext.setParams(null); - - Assert.assertEquals("//test", oContext.getUri()); - Assert.assertEquals(io.vertx.core.http.HttpMethod.POST, oContext.getMethod()); - Assert.assertEquals(8080, oContext.getIpPort().getPort()); - Assert.assertEquals(null, oContext.getParams()); - - RestResponse oResponse = new RestResponse(null, null); - oResponse.setRequestContext(oContext); - Assert.assertEquals(oContext, oResponse.getRequestContext()); - Assert.assertEquals(null, oResponse.getResponse()); - } - - @Test - public void testRequestParam() { - RequestParam oParam = new RequestParam(); - oParam.setCookies(null); - oParam.setBody(null); - oParam.setHeaders(null); - oParam.addHeader("testKey", "testValue"); - oParam.addQueryParam("testParam", "ValueParam"); - oParam.addQueryParam("testParam1", "ValueParam"); - Assert.assertEquals(null, oParam.getCookies()); - Assert.assertEquals(null, oParam.getBody()); - Assert.assertNotEquals(null, oParam.getHeaders()); - Assert.assertNotEquals(null, oParam.getQueryParams()); - oParam.setQueryParams(null); - Assert.assertEquals("", oParam.getQueryParams()); - oParam.setFormFields(null); - Assert.assertEquals(null, oParam.getFormFields()); - Endpoints oEndpoints = new Endpoints(); - oEndpoints.setInstances(null); - oEndpoints.setVersion("1.0"); - Assert.assertEquals(null, oEndpoints.getInstances()); - Assert.assertEquals("1.0", oEndpoints.getVersion()); - } - - @Test - public void testIpPortManager(@Mocked InstanceCacheManager instanceCacheManager) throws Exception { - IpPortManager oManager = new IpPortManager(ServiceRegistryConfig.INSTANCE, instanceCacheManager); - ArrayList oIPPort = oManager.getDefaultIpPortList(); - oManager.next(); - Assert.assertEquals(oIPPort.get(0).getHostOrIp(), oManager.get().getHostOrIp()); - - try { - //This will return Null as the address cache is not able to get the address of the microservice which is registered above. - Assert.assertNull(oManager.next()); - } catch (Exception e) { - // TODO: Currently the address cache is failing because of absence of Thread - // TODO: Need to find out a way to Assert it properly - Assert.assertEquals("/ by zero", e.getMessage()); - } - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java deleted file mode 100644 index 43835b7cfba..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.client.http; - -import static org.hamcrest.core.Is.is; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; - -import javax.xml.ws.Holder; - -import org.apache.log4j.Appender; -import org.apache.log4j.Logger; -import org.apache.log4j.spi.LoggingEvent; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceFactory; -import io.servicecomb.serviceregistry.client.ClientException; -import io.servicecomb.serviceregistry.client.IpPortManager; -import io.servicecomb.serviceregistry.client.http.ServiceRegistryClientImpl.ResponseWrapper; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.vertx.core.Handler; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.http.HttpClientResponse; -import io.vertx.core.http.HttpVersion; -import mockit.Deencapsulation; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestServiceRegistryClientImpl { - @Mocked - private IpPortManager ipPortManager; - - private ServiceRegistryClientImpl oClient = null; - - @Before - public void setUp() throws Exception { - oClient = new ServiceRegistryClientImpl(ipPortManager); - - new MockUp() { - @Mock - void httpDo(RequestContext requestContext, Handler responseHandler) { - } - }; - - new MockUp() { - @Mock - public void await() throws InterruptedException { - } - }; - } - - @After - public void tearDown() throws Exception { - oClient = null; - } - - @Test - public void testPrivateMehtodCreateHttpClientOptions() { - MicroserviceFactory microserviceFactory = new MicroserviceFactory(); - Microservice microservice = microserviceFactory.create("app", "ms"); - oClient.registerMicroservice(microservice); - oClient.registerMicroserviceInstance(microservice.getIntance()); - new MockUp() { - @Mock - public HttpVersion getHttpVersion() { - return HttpVersion.HTTP_2; - } - - @Mock - public boolean isSsl() { - return true; - } - }; - try { - oClient.init(); - HttpClientOptions httpClientOptions = Deencapsulation.invoke(oClient, "createHttpClientOptions"); - Assert.assertNotNull(httpClientOptions); - Assert.assertEquals(80, httpClientOptions.getDefaultPort()); - } catch (Exception e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testException() { - MicroserviceFactory microserviceFactory = new MicroserviceFactory(); - Microservice microservice = microserviceFactory.create("app", "ms"); - Assert.assertEquals(null, oClient.registerMicroservice(microservice)); - Assert.assertEquals(null, oClient.registerMicroserviceInstance(microservice.getIntance())); - oClient.init(); - Assert.assertEquals(null, - oClient.getMicroserviceId(microservice.getAppId(), - microservice.getServiceName(), - microservice.getVersion())); - Assert.assertThat(oClient.getAllMicroservices().isEmpty(), is(true)); - Assert.assertEquals(null, oClient.registerMicroservice(microservice)); - Assert.assertEquals(null, oClient.getMicroservice("microserviceId")); - Assert.assertEquals(null, oClient.getMicroserviceInstance("consumerId", "providerId")); - Assert.assertEquals(false, - oClient.unregisterMicroserviceInstance("microserviceId", "microserviceInstanceId")); - Assert.assertEquals(null, oClient.heartbeat("microserviceId", "microserviceInstanceId")); - Assert.assertEquals(null, - oClient.findServiceInstance("selfMicroserviceId", "appId", "serviceName", "versionRule")); - - Assert.assertEquals("a", new ClientException("a").getMessage()); - } - - static abstract class RegisterSchemaTester { - void run() { - Logger rootLogger = Logger.getRootLogger(); - - List events = new ArrayList<>(); - Appender appender = new MockUp() { - @Mock - public void doAppend(LoggingEvent event) { - events.add(event); - } - }.getMockInstance(); - rootLogger.addAppender(appender); - - doRun(events); - - rootLogger.removeAppender(appender); - } - - abstract void doRun(List events); - } - - @Test - public void testRegisterSchemaNoResponse() { - new RegisterSchemaTester() { - void doRun(java.util.List events) { - oClient.registerSchema("msid", "schemaId", "content"); - Assert.assertEquals("Register schema msid/schemaId failed.", events.get(0).getMessage()); - } - }.run(); - } - - @Test - public void testRegisterSchemaException() { - InterruptedException e = new InterruptedException(); - new MockUp() { - @Mock - public void await() throws InterruptedException { - throw e; - } - }; - - new RegisterSchemaTester() { - void doRun(java.util.List events) { - oClient.registerSchema("msid", "schemaId", "content"); - Assert.assertEquals( - "register schema msid/schemaId fail.", - events.get(0).getMessage()); - Assert.assertEquals(e, events.get(0).getThrowableInformation().getThrowable()); - } - }.run(); - } - - @Test - public void testRegisterSchemaErrorResponse() { - new MockUp() { - @Mock - Handler syncHandlerEx(CountDownLatch countDownLatch, Holder holder) { - return restResponse -> { - HttpClientResponse response = Mockito.mock(HttpClientResponse.class); - Mockito.when(response.statusCode()).thenReturn(400); - Mockito.when(response.statusMessage()).thenReturn("client error"); - - Buffer bodyBuffer = Buffer.buffer(); - bodyBuffer.appendString("too big"); - - ResponseWrapper responseWrapper = new ResponseWrapper(); - responseWrapper.response = response; - responseWrapper.bodyBuffer = bodyBuffer; - holder.value = responseWrapper; - }; - } - }; - new MockUp() { - @Mock - void httpDo(RequestContext requestContext, Handler responseHandler) { - responseHandler.handle(null); - } - }; - - new RegisterSchemaTester() { - void doRun(java.util.List events) { - oClient.registerSchema("msid", "schemaId", "content"); - Assert.assertEquals( - "Register schema msid/schemaId failed, statusCode: 400, statusMessage: client error, description: too big.", - events.get(0).getMessage()); - } - }.run(); - } - - @Test - public void testRegisterSchemaSuccess() { - new MockUp() { - @Mock - Handler syncHandlerEx(CountDownLatch countDownLatch, Holder holder) { - return restResponse -> { - HttpClientResponse response = Mockito.mock(HttpClientResponse.class); - Mockito.when(response.statusCode()).thenReturn(200); - - ResponseWrapper responseWrapper = new ResponseWrapper(); - responseWrapper.response = response; - holder.value = responseWrapper; - }; - } - }; - new MockUp() { - @Mock - void httpDo(RequestContext requestContext, Handler responseHandler) { - responseHandler.handle(null); - } - }; - - new RegisterSchemaTester() { - void doRun(java.util.List events) { - oClient.registerSchema("msid", "schemaId", "content"); - Assert.assertEquals( - "register schema msid/schemaId success.", - events.get(0).getMessage()); - } - }.run(); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/config/TestConfigurePropertyUtils.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/config/TestConfigurePropertyUtils.java deleted file mode 100644 index 68fe64ad5e6..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/config/TestConfigurePropertyUtils.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.config; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.configuration.Configuration; -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.config.ConfigUtil; -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.serviceregistry.api.registry.BasePath; - -public class TestConfigurePropertyUtils { - @Test - public void testGetPropertiesWithPrefix() { - Configuration configuration = ConfigUtil.createLocalConfig(); - - String prefix = "service_description.properties"; - Map expectedMap = new HashMap<>(); - expectedMap.put("key1", "value1"); - expectedMap.put("key2", "value2"); - Assert.assertEquals(expectedMap, ConfigurePropertyUtils.getPropertiesWithPrefix(configuration, prefix)); - - List paths = ConfigurePropertyUtils.getMicroservicePaths(configuration); - Assert.assertEquals(2, paths.size()); - Assert.assertEquals(paths.get(0).getPath(), "/test1/testpath"); - Assert.assertEquals(paths.get(0).getProperty().get("checksession"), false); - - System.setProperty(Const.URL_PREFIX, "/webroot"); - paths = ConfigurePropertyUtils.getMicroservicePaths(configuration); - Assert.assertEquals(2, paths.size()); - Assert.assertEquals(paths.get(0).getPath(), "/webroot/test1/testpath"); - Assert.assertEquals(paths.get(0).getProperty().get("checksession"), false); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/config/TestPropertiesLoader.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/config/TestPropertiesLoader.java deleted file mode 100644 index 47b7989d6b4..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/config/TestPropertiesLoader.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.config; - -import static io.servicecomb.foundation.common.base.ServiceCombConstants.CONFIG_SERVICE; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.configuration.Configuration; -import org.junit.Assert; -import org.junit.Test; - -import com.netflix.config.DynamicConfiguration; - -import io.servicecomb.config.archaius.sources.ConfigModel; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceFactory; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.definition.DefinitionConst; -import io.servicecomb.serviceregistry.definition.MicroserviceDefinition; -import io.servicecomb.serviceregistry.registry.ServiceRegistryFactory; - -public class TestPropertiesLoader { - private static MicroserviceFactory microserviceFactory = new MicroserviceFactory(); - - @Test - public void testMergeStrings() { - Assert.assertEquals("abc123efg", AbstractPropertiesLoader.mergeStrings("abc", "123", "efg")); - } - - @Test - public void testEmptyExtendedClass() { - Microservice microservice = microserviceFactory.create("default", "emptyExtendedClass"); - Assert.assertEquals(0, microservice.getProperties().size()); - } - - @Test - public void testInvalidExtendedClass() { - ConfigModel configModel = MicroserviceDefinition.createConfigModel("default", "invalidExtendedClass"); - @SuppressWarnings("unchecked") - Map desc = - (Map) configModel.getConfig().get(DefinitionConst.serviceDescriptionKey); - desc.put("propertyExtentedClass", "invalidClass"); - MicroserviceDefinition microserviceDefinition = new MicroserviceDefinition(Arrays.asList(configModel)); - try { - microserviceFactory.create(microserviceDefinition); - Assert.fail("Must throw exception"); - } catch (Error e) { - Assert.assertEquals(ClassNotFoundException.class, e.getCause().getClass()); - Assert.assertEquals("invalidClass", e.getCause().getMessage()); - } - } - - @Test - public void testCanNotAssignExtendedClass() { - ConfigModel configModel = MicroserviceDefinition.createConfigModel("default", "invalidExtendedClass"); - @SuppressWarnings("unchecked") - Map desc = - (Map) configModel.getConfig().get(DefinitionConst.serviceDescriptionKey); - desc.put("propertyExtentedClass", "java.lang.String"); - MicroserviceDefinition microserviceDefinition = new MicroserviceDefinition(Arrays.asList(configModel)); - try { - microserviceFactory.create(microserviceDefinition); - Assert.fail("Must throw exception"); - } catch (Error e) { - Assert.assertEquals( - "Define propertyExtendedClass java.lang.String in yaml, but not implement the interface PropertyExtended.", - e.getMessage()); - } - } - - @Test - public void testMicroservicePropertiesLoader() throws Exception { - Microservice microservice = ServiceRegistryFactory.createLocal().getMicroservice(); - Map expectedMap = new HashMap<>(); - expectedMap.put("key1", "value1"); - expectedMap.put("key2", "value2"); - expectedMap.put("ek0", "ev0"); - Assert.assertEquals(expectedMap, microservice.getProperties()); - } - - @Test - public void testInstancePropertiesLoader() { - Microservice microservice = ServiceRegistryFactory.createLocal().getMicroservice(); - MicroserviceInstance instance = microservice.getIntance(); - Map expectedMap = new HashMap<>(); - expectedMap.put("key0", "value0"); - expectedMap.put("ek0", "ev0"); - Assert.assertEquals(expectedMap, instance.getProperties()); - } - - @Test - public void testExtendedClassCompatible() { - Configuration configuration = new DynamicConfiguration(); - configuration.setProperty(CONFIG_SERVICE + AbstractPropertiesLoader.EXTENDED_CLASS, "invalidClass"); - - AbstractPropertiesLoader loader = MicroservicePropertiesLoader.INSTANCE; - try { - loader.loadProperties(configuration); - Assert.fail("Must throw exception"); - } catch (Error e) { - Assert.assertEquals(ClassNotFoundException.class, e.getCause().getClass()); - Assert.assertEquals("invalidClass", e.getCause().getMessage()); - } - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/config/TestServiceRegistryConfig.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/config/TestServiceRegistryConfig.java deleted file mode 100644 index c3d413acc2d..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/config/TestServiceRegistryConfig.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.config; - -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -import io.servicecomb.serviceregistry.ArchaiusUtils; - -public class TestServiceRegistryConfig { - @BeforeClass - public static void initClass() { - ArchaiusUtils.resetConfig(); - } - - @AfterClass - public static void teardownClass() { - ArchaiusUtils.resetConfig(); - } - - @Test - public void testServiceRegistryConfig() { - ServiceRegistryConfig oConfig = ServiceRegistryConfig.INSTANCE; - Assert.assertEquals(null, oConfig.getAccessKey()); - Assert.assertEquals(30000, oConfig.getConnectionTimeout()); - Assert.assertNotEquals(null, oConfig.getHeartbeatInterval()); - Assert.assertEquals("HTTP_1_1", oConfig.getHttpVersion().name()); - Assert.assertEquals("rest", oConfig.getTransport()); - Assert.assertNotEquals(null, oConfig.getIpPort()); - Assert.assertEquals(1, oConfig.getWorkerPoolSize()); - Assert.assertEquals(true, oConfig.isSsl()); - Assert.assertEquals(30000, oConfig.getRequestTimeout()); - Assert.assertNotEquals(null, oConfig.getResendHeartBeatTimes()); - Assert.assertEquals(false, oConfig.isPreferIpAddress()); - Assert.assertEquals(true, oConfig.isWatch()); - Assert.assertEquals(false, oConfig.isClientAuthEnabled()); - Assert.assertEquals(ServiceRegistryConfig.NO_TENANT, oConfig.getTenantName()); - Assert.assertEquals(null, oConfig.getSecretKey()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/registry/TestLocalServiceRegistry.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/registry/TestLocalServiceRegistry.java deleted file mode 100644 index f7a956b4c10..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/registry/TestLocalServiceRegistry.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.registry; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - -import com.google.common.eventbus.EventBus; - -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.task.event.ExceptionEvent; -import io.servicecomb.serviceregistry.task.event.RecoveryEvent; - -public class TestLocalServiceRegistry { - @Test - public void testCacheAvaiable() { - AbstractServiceRegistry serviceRegistry = (AbstractServiceRegistry) ServiceRegistryFactory.createLocal(); - EventBus eventBus = serviceRegistry.getEventBus(); - serviceRegistry.init(); - - Assert.assertEquals(false, serviceRegistry.cacheAvaiable); - eventBus.post(new RecoveryEvent()); - Assert.assertEquals(true, serviceRegistry.cacheAvaiable); - - eventBus.post(new ExceptionEvent(null)); - Assert.assertEquals(false, serviceRegistry.cacheAvaiable); - } - - @Test - public void testLifeCycle() { - ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal(); - serviceRegistry.init(); - - Assert.assertNull(serviceRegistry.getMicroserviceInstance().getInstanceId()); - serviceRegistry.run(); - Assert.assertNotNull(serviceRegistry.getMicroserviceInstance().getInstanceId()); - - serviceRegistry.destory(); - Assert.assertTrue(serviceRegistry.getServiceRegistryClient() - .getMicroserviceInstance("", serviceRegistry.getMicroservice().getServiceId()) - .isEmpty()); - } - - @Test - public void testUpdateProperties() { - ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal(); - serviceRegistry.init(); - serviceRegistry.run(); - - Microservice microservice = serviceRegistry.getMicroservice(); - Map properties = new HashMap<>(); - properties.put("k", "v"); - - try { - serviceRegistry.getServiceRegistryClient().updateInstanceProperties(microservice.getServiceId(), - "notExist", - properties); - Assert.fail("must throw exception"); - } catch (IllegalArgumentException e) { - Assert.assertEquals("Invalid argument. microserviceId=" + microservice.getServiceId() - + ", microserviceInstanceId=notExist.", - e.getMessage()); - } - - serviceRegistry.updateMicroserviceProperties(properties); - Assert.assertEquals(properties, microservice.getProperties()); - serviceRegistry.updateInstanceProperties(properties); - Assert.assertEquals(properties, microservice.getIntance().getProperties()); - - properties.put("k1", "v1"); - serviceRegistry.updateMicroserviceProperties(properties); - Assert.assertEquals(properties, microservice.getProperties()); - serviceRegistry.updateInstanceProperties(properties); - Assert.assertEquals(properties, microservice.getIntance().getProperties()); - } - - @Test - public void testSchema() { - ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal(); - Microservice microservice = serviceRegistry.getMicroservice(); - microservice.addSchema("s1", "s1-content"); - serviceRegistry.init(); - serviceRegistry.run(); - - try { - serviceRegistry.getServiceRegistryClient().isSchemaExist("notExist", "s1"); - Assert.fail("must throw exception"); - } catch (IllegalArgumentException e) { - Assert.assertEquals("Invalid serviceId, serviceId=notExist", e.getMessage()); - } - try { - serviceRegistry.getServiceRegistryClient().getSchema("notExist", "s1"); - Assert.fail("must throw exception"); - } catch (IllegalArgumentException e) { - Assert.assertEquals("Invalid serviceId, serviceId=notExist", e.getMessage()); - } - - Assert.assertEquals(true, - serviceRegistry.getServiceRegistryClient().isSchemaExist(microservice.getServiceId(), "s1")); - String content = serviceRegistry.getServiceRegistryClient().getSchema(microservice.getServiceId(), "s1"); - Assert.assertEquals("s1-content", content); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/registry/TestServiceRegistryFactory.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/registry/TestServiceRegistryFactory.java deleted file mode 100644 index 5d9690f059e..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/registry/TestServiceRegistryFactory.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.registry; - -import java.util.Collections; - -import org.junit.Assert; -import org.junit.Test; - -import com.google.common.eventbus.EventBus; - -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.client.LocalServiceRegistryClientImpl; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.servicecomb.serviceregistry.client.http.ServiceRegistryClientImpl; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.servicecomb.serviceregistry.definition.MicroserviceDefinition; -import mockit.Deencapsulation; - -/** - * Created by on 2017/3/31. - */ -public class TestServiceRegistryFactory { - @Test - public void testGetRemoteRegistryClient() { - EventBus eventBus = new EventBus(); - ServiceRegistryConfig serviceRegistryConfig = ServiceRegistryConfig.INSTANCE; - MicroserviceDefinition microserviceDefinition = new MicroserviceDefinition(Collections.emptyList()); - - ServiceRegistry serviceRegistry = - ServiceRegistryFactory.create(eventBus, serviceRegistryConfig, microserviceDefinition); - serviceRegistry.init(); - ServiceRegistryClient client = serviceRegistry.getServiceRegistryClient(); - Assert.assertTrue(client instanceof ServiceRegistryClientImpl); - - serviceRegistry = ServiceRegistryFactory.getOrCreate(eventBus, - serviceRegistryConfig, - microserviceDefinition); - Assert.assertTrue(serviceRegistry instanceof RemoteServiceRegistry); - Assert.assertEquals(serviceRegistry, ServiceRegistryFactory.getServiceRegistry()); - - Deencapsulation.setField(ServiceRegistryFactory.class, "serviceRegistry", null); - - System.setProperty("local.registry.file", "/tmp/test.yaml"); - serviceRegistry = ServiceRegistryFactory.create(eventBus, serviceRegistryConfig, microserviceDefinition); - serviceRegistry.init(); - client = serviceRegistry.getServiceRegistryClient(); - Assert.assertTrue(client instanceof LocalServiceRegistryClientImpl); - Assert.assertTrue(ServiceRegistryFactory.getOrCreate(eventBus, - serviceRegistryConfig, - microserviceDefinition) instanceof LocalServiceRegistry); - System.clearProperty("local.registry.file"); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestAbstractRegisterTask.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestAbstractRegisterTask.java deleted file mode 100644 index 77da96325b1..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestAbstractRegisterTask.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.google.common.eventbus.EventBus; - -import io.servicecomb.foundation.common.utils.ReflectUtils; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import mockit.Mocked; - -public class TestAbstractRegisterTask { - private EventBus eventBus; - - private Microservice microservice; - - @Before - public void setup() { - eventBus = new EventBus(); - - microservice = new Microservice(); - microservice.setAppId("app"); - microservice.setServiceName("ms"); - - microservice.setIntance(new MicroserviceInstance()); - } - - @Test - public void testHeartbeatSuccess(@Mocked ServiceRegistryClient srClient) { - MicroserviceRegisterTask registerTask = new MicroserviceRegisterTask(eventBus, srClient, microservice); - ReflectUtils.setField(registerTask, "registered", true); - - MicroserviceInstanceHeartbeatTask heartbeatTask = - new MicroserviceInstanceHeartbeatTask(eventBus, srClient, microservice); - ReflectUtils.setField(heartbeatTask, "heartbeatResult", HeartbeatResult.SUCCESS); - - Assert.assertEquals(true, registerTask.isRegistered()); - eventBus.post(heartbeatTask); - Assert.assertEquals(true, registerTask.isRegistered()); - Assert.assertEquals(eventBus, registerTask.getEventBus()); - } - - @Test - public void testHeartbeatFailed(@Mocked ServiceRegistryClient srClient) { - MicroserviceRegisterTask registerTask = new MicroserviceRegisterTask(eventBus, srClient, microservice); - ReflectUtils.setField(registerTask, "registered", true); - - MicroserviceInstanceHeartbeatTask heartbeatTask = - new MicroserviceInstanceHeartbeatTask(eventBus, srClient, microservice); - ReflectUtils.setField(heartbeatTask, "heartbeatResult", HeartbeatResult.INSTANCE_NOT_REGISTERED); - - Assert.assertEquals(true, registerTask.isRegistered()); - eventBus.post(heartbeatTask); - Assert.assertEquals(false, registerTask.isRegistered()); - } - - @Test - public void testHeartbeatOtherFailed(@Mocked ServiceRegistryClient srClient) { - MicroserviceRegisterTask registerTask = new MicroserviceRegisterTask(eventBus, srClient, microservice); - ReflectUtils.setField(registerTask, "registered", true); - - Microservice otherMicroservice = new Microservice(); - otherMicroservice.setAppId(microservice.getAppId()); - otherMicroservice.setServiceName("ms1"); - - MicroserviceInstanceHeartbeatTask heartbeatTask = - new MicroserviceInstanceHeartbeatTask(eventBus, srClient, otherMicroservice); - ReflectUtils.setField(heartbeatTask, "heartbeatResult", HeartbeatResult.INSTANCE_NOT_REGISTERED); - - Assert.assertEquals(true, registerTask.isRegistered()); - eventBus.post(heartbeatTask); - Assert.assertEquals(true, registerTask.isRegistered()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestCompositeTask.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestCompositeTask.java deleted file mode 100644 index 5e55be501d5..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestCompositeTask.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import org.junit.Assert; -import org.junit.Test; - -public class TestCompositeTask { - private int count; - - @Test - public void testRun() { - CompositeTask task = new CompositeTask(); - for (int idx = 0; idx < 3; idx++) { - task.addTask(() -> { - count++; - }); - } - - task.run(); - - Assert.assertEquals(3, count); - Assert.assertEquals(3, task.getTaskCount()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestMicroserviceInstanceHeartbeatTask.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestMicroserviceInstanceHeartbeatTask.java deleted file mode 100644 index 63356317217..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestMicroserviceInstanceHeartbeatTask.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; - -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.api.response.HeartbeatResponse; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import mockit.Expectations; -import mockit.Mocked; - -public class TestMicroserviceInstanceHeartbeatTask { - private EventBus eventBus; - - private Microservice microservice; - - private List heartbeatTaskList; - - @Before - public void setup() { - eventBus = new EventBus(); - - heartbeatTaskList = new ArrayList<>(); - eventBus.register(new Object() { - @Subscribe - public void onEvent(MicroserviceInstanceHeartbeatTask task) { - heartbeatTaskList.add(task); - } - }); - - microservice = new Microservice(); - microservice.setAppId("app"); - microservice.setServiceName("ms"); - microservice.setServiceId("serviceId"); - - microservice.setIntance(new MicroserviceInstance()); - microservice.getIntance().setInstanceId("instanceId"); - } - - @Test - public void testNotRegistered(@Mocked ServiceRegistryClient srClient, - @Mocked MicroserviceInstanceRegisterTask registerTask) { - new Expectations() { - { - } - }; - - MicroserviceInstanceHeartbeatTask task = - new MicroserviceInstanceHeartbeatTask(eventBus, srClient, microservice); - registerTask.taskStatus = TaskStatus.INIT; - eventBus.post(registerTask); - - task.run(); - Assert.assertNull(task.getHeartbeatResult()); - Assert.assertEquals(false, task.isNeedRegisterInstance()); - Assert.assertEquals(0, heartbeatTaskList.size()); - } - - @Test - public void testOtherMicroservice(@Mocked ServiceRegistryClient srClient, - @Mocked ServiceRegistryConfig serviceRegistryConfig, - @Mocked MicroserviceWatchTask watchTask) { - Microservice otherMicroservice = new Microservice(); - otherMicroservice.setServiceName("ms1"); - - new Expectations() { - { - watchTask.getMicroservice(); - result = otherMicroservice; - } - }; - - MicroserviceInstanceHeartbeatTask heartbeatTask = - new MicroserviceInstanceHeartbeatTask(eventBus, srClient, microservice); - watchTask.taskStatus = TaskStatus.READY; - eventBus.post(watchTask); - - heartbeatTask.run(); - Assert.assertNull(heartbeatTask.getHeartbeatResult()); - Assert.assertEquals(false, heartbeatTask.isNeedRegisterInstance()); - Assert.assertEquals(0, heartbeatTaskList.size()); - } - - @Test - public void testHeartbeatDisconnect(@Mocked ServiceRegistryClient srClient, - @Mocked ServiceRegistryConfig serviceRegistryConfig, - @Mocked MicroserviceWatchTask watchTask) { - new Expectations() { - { - srClient.heartbeat(anyString, anyString); - result = null; - watchTask.getMicroservice(); - result = microservice; - } - }; - - MicroserviceInstanceHeartbeatTask heartbeatTask = - new MicroserviceInstanceHeartbeatTask(eventBus, srClient, microservice); - watchTask.taskStatus = TaskStatus.READY; - eventBus.post(watchTask); - - heartbeatTask.run(); - Assert.assertEquals(HeartbeatResult.DISCONNECTED, heartbeatTask.getHeartbeatResult()); - Assert.assertEquals(false, heartbeatTask.isNeedRegisterInstance()); - Assert.assertEquals(1, heartbeatTaskList.size()); - } - - @Test - public void testHeartbeatNotRegistered(@Mocked ServiceRegistryClient srClient, - @Mocked ServiceRegistryConfig serviceRegistryConfig, - @Mocked MicroserviceWatchTask watchTask) { - HeartbeatResponse response = new HeartbeatResponse(); - response.setOk(false); - response.setMessage("FAIL"); - - new Expectations() { - { - srClient.heartbeat(anyString, anyString); - result = response; - watchTask.getMicroservice(); - result = microservice; - } - }; - - MicroserviceInstanceHeartbeatTask heartbeatTask = - new MicroserviceInstanceHeartbeatTask(eventBus, srClient, microservice); - watchTask.taskStatus = TaskStatus.READY; - eventBus.post(watchTask); - - heartbeatTask.run(); - Assert.assertEquals(HeartbeatResult.INSTANCE_NOT_REGISTERED, heartbeatTask.getHeartbeatResult()); - Assert.assertEquals(true, heartbeatTask.isNeedRegisterInstance()); - Assert.assertEquals(1, heartbeatTaskList.size()); - } - - @Test - public void testHeartbeatSuccess(@Mocked ServiceRegistryClient srClient, - @Mocked ServiceRegistryConfig serviceRegistryConfig, - @Mocked MicroserviceWatchTask watchTask) { - HeartbeatResponse response = new HeartbeatResponse(); - response.setOk(true); - response.setMessage("OK"); - - new Expectations() { - { - srClient.heartbeat(anyString, anyString); - result = response; - watchTask.getMicroservice(); - result = microservice; - } - }; - - MicroserviceInstanceHeartbeatTask heartbeatTask = - new MicroserviceInstanceHeartbeatTask(eventBus, srClient, microservice); - watchTask.taskStatus = TaskStatus.READY; - eventBus.post(watchTask); - - heartbeatTask.run(); - Assert.assertEquals(HeartbeatResult.SUCCESS, heartbeatTask.getHeartbeatResult()); - Assert.assertEquals(false, heartbeatTask.isNeedRegisterInstance()); - Assert.assertEquals(1, heartbeatTaskList.size()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestMicroserviceInstanceRegisterTask.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestMicroserviceInstanceRegisterTask.java deleted file mode 100644 index 2ceb5bca4e6..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestMicroserviceInstanceRegisterTask.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; - -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.HealthCheck; -import io.servicecomb.serviceregistry.api.registry.HealthCheckMode; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import mockit.Expectations; -import mockit.Mocked; - -public class TestMicroserviceInstanceRegisterTask { - private EventBus eventBus; - - private Microservice microservice; - - private List taskList; - - @Mocked - private ServiceRegistryConfig serviceRegistryConfig; - - @Mocked - private ServiceRegistryClient srClient; - - @Before - public void setup() { - eventBus = new EventBus(); - - taskList = new ArrayList<>(); - eventBus.register(new Object() { - @Subscribe - public void onEvent(MicroserviceInstanceRegisterTask task) { - taskList.add(task); - } - }); - - microservice = new Microservice(); - microservice.setAppId("app"); - microservice.setServiceName("ms"); - microservice.setServiceId("serviceId"); - - microservice.setIntance(new MicroserviceInstance()); - - HealthCheck healthCheck = new HealthCheck(); - healthCheck.setMode(HealthCheckMode.HEARTBEAT); - microservice.getIntance().setHealthCheck(healthCheck); - } - - @Test - public void microserviceNotRegistered() { - microservice.setServiceId(null); - - MicroserviceInstanceRegisterTask registerTask = - new MicroserviceInstanceRegisterTask(eventBus, serviceRegistryConfig, null, microservice); - registerTask.run(); - - Assert.assertEquals(false, registerTask.isRegistered()); - Assert.assertEquals(0, taskList.size()); - } - - @Test - public void registerIpSuccess() { - MicroserviceInstance instance = microservice.getIntance(); - new Expectations(RegistryUtils.class) { - { - RegistryUtils.getPublishAddress(); - result = "127.0.0.1"; - serviceRegistryConfig.isPreferIpAddress(); - result = true; - serviceRegistryConfig.getHeartbeatInterval(); - result = 10; - serviceRegistryConfig.getResendHeartBeatTimes(); - result = 20; - srClient.registerMicroserviceInstance(instance); - result = "instanceId"; - } - }; - MicroserviceInstanceRegisterTask registerTask = - new MicroserviceInstanceRegisterTask(eventBus, serviceRegistryConfig, srClient, microservice); - registerTask.taskStatus = TaskStatus.READY; - registerTask.run(); - - Assert.assertEquals(true, registerTask.isRegistered()); - Assert.assertEquals("127.0.0.1", instance.getHostName()); - Assert.assertEquals("instanceId", instance.getInstanceId()); - Assert.assertEquals(10, instance.getHealthCheck().getInterval()); - Assert.assertEquals(20, instance.getHealthCheck().getTimes()); - Assert.assertEquals(1, taskList.size()); - } - - @Test - public void registerHostSuccess() { - MicroserviceInstance instance = microservice.getIntance(); - new Expectations(RegistryUtils.class) { - { - RegistryUtils.getPublishHostName(); - result = "hostName"; - serviceRegistryConfig.isPreferIpAddress(); - result = false; - serviceRegistryConfig.getHeartbeatInterval(); - result = 10; - serviceRegistryConfig.getResendHeartBeatTimes(); - result = 20; - srClient.registerMicroserviceInstance(instance); - result = "instanceId"; - } - }; - MicroserviceInstanceRegisterTask registerTask = - new MicroserviceInstanceRegisterTask(eventBus, serviceRegistryConfig, srClient, microservice); - registerTask.taskStatus = TaskStatus.READY; - registerTask.run(); - - Assert.assertEquals(true, registerTask.isRegistered()); - Assert.assertEquals("hostName", instance.getHostName()); - Assert.assertEquals("instanceId", instance.getInstanceId()); - Assert.assertEquals(10, instance.getHealthCheck().getInterval()); - Assert.assertEquals(20, instance.getHealthCheck().getTimes()); - Assert.assertEquals(1, taskList.size()); - } - - @Test - public void registerIpFailed() { - MicroserviceInstance instance = microservice.getIntance(); - new Expectations(RegistryUtils.class) { - { - RegistryUtils.getPublishAddress(); - result = "127.0.0.1"; - serviceRegistryConfig.isPreferIpAddress(); - result = true; - serviceRegistryConfig.getHeartbeatInterval(); - result = 10; - serviceRegistryConfig.getResendHeartBeatTimes(); - result = 20; - srClient.registerMicroserviceInstance(instance); - result = null; - } - }; - MicroserviceInstanceRegisterTask registerTask = - new MicroserviceInstanceRegisterTask(eventBus, serviceRegistryConfig, srClient, microservice); - registerTask.taskStatus = TaskStatus.READY; - registerTask.run(); - - Assert.assertEquals(false, registerTask.isRegistered()); - Assert.assertEquals("127.0.0.1", instance.getHostName()); - Assert.assertEquals(10, instance.getHealthCheck().getInterval()); - Assert.assertEquals(20, instance.getHealthCheck().getTimes()); - Assert.assertEquals(1, taskList.size()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestMicroserviceRegisterTask.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestMicroserviceRegisterTask.java deleted file mode 100644 index faf2396c296..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestMicroserviceRegisterTask.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; - -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import mockit.Expectations; -import mockit.Mocked; - -public class TestMicroserviceRegisterTask { - private EventBus eventBus; - - private Microservice microservice; - - private List taskList; - - @Before - public void setup() { - eventBus = new EventBus(); - - taskList = new ArrayList<>(); - eventBus.register(new Object() { - @Subscribe - public void onEvent(MicroserviceRegisterTask task) { - taskList.add(task); - } - }); - - microservice = new Microservice(); - microservice.setAppId("app"); - microservice.setServiceName("ms"); - - microservice.setIntance(new MicroserviceInstance()); - } - - @Test - public void testNewRegisterFailed(@Mocked ServiceRegistryClient srClient) { - new Expectations() { - { - srClient.getMicroserviceId(anyString, anyString, anyString); - result = null; - srClient.registerMicroservice((Microservice) any); - result = null; - } - }; - - MicroserviceRegisterTask registerTask = new MicroserviceRegisterTask(eventBus, srClient, microservice); - registerTask.run(); - - Assert.assertEquals(false, registerTask.isRegistered()); - Assert.assertEquals(false, registerTask.isSchemaIdSetMatch()); - Assert.assertEquals(null, microservice.getServiceId()); - Assert.assertEquals(1, taskList.size()); - } - - @Test - public void testNewRegisterSuccess(@Mocked ServiceRegistryClient srClient) { - new Expectations() { - { - srClient.getMicroserviceId(anyString, anyString, anyString); - result = null; - srClient.registerMicroservice((Microservice) any); - result = "serviceId"; - } - }; - - MicroserviceRegisterTask registerTask = new MicroserviceRegisterTask(eventBus, srClient, microservice); - registerTask.run(); - - Assert.assertEquals(true, registerTask.isRegistered()); - Assert.assertEquals(true, registerTask.isSchemaIdSetMatch()); - Assert.assertEquals("serviceId", microservice.getServiceId()); - Assert.assertEquals("serviceId", microservice.getIntance().getServiceId()); - Assert.assertEquals(1, taskList.size()); - - registerTask.run(); - Assert.assertEquals(1, taskList.size()); - } - - @Test - public void testRegisterSchemaFailed(@Mocked ServiceRegistryClient srClient) { - microservice.addSchema("s1", ""); - microservice.addSchema("exist", ""); - new Expectations() { - { - srClient.getMicroserviceId(anyString, anyString, anyString); - result = null; - srClient.registerMicroservice((Microservice) any); - result = "serviceId"; - srClient.isSchemaExist("serviceId", "exist"); - result = true; - srClient.isSchemaExist(anyString, anyString); - result = false; - srClient.registerSchema(anyString, anyString, anyString); - result = false; - } - }; - - MicroserviceRegisterTask registerTask = new MicroserviceRegisterTask(eventBus, srClient, microservice); - registerTask.run(); - - Assert.assertEquals(false, registerTask.isRegistered()); - Assert.assertEquals(true, registerTask.isSchemaIdSetMatch()); - Assert.assertEquals("serviceId", microservice.getServiceId()); - Assert.assertEquals("serviceId", microservice.getIntance().getServiceId()); - Assert.assertEquals(1, taskList.size()); - } - - @Test - public void testRegisterSchemaSuccess(@Mocked ServiceRegistryClient srClient) { - microservice.addSchema("s1", ""); - new Expectations() { - { - srClient.getMicroserviceId(anyString, anyString, anyString); - result = null; - srClient.registerMicroservice((Microservice) any); - result = "serviceId"; - srClient.isSchemaExist(anyString, anyString); - result = false; - srClient.registerSchema(anyString, anyString, anyString); - result = true; - } - }; - - MicroserviceRegisterTask registerTask = new MicroserviceRegisterTask(eventBus, srClient, microservice); - registerTask.run(); - - Assert.assertEquals(true, registerTask.isRegistered()); - Assert.assertEquals(true, registerTask.isSchemaIdSetMatch()); - Assert.assertEquals("serviceId", microservice.getServiceId()); - Assert.assertEquals("serviceId", microservice.getIntance().getServiceId()); - Assert.assertEquals(1, taskList.size()); - } - - @Test - public void testAlreadyRegisteredSchemaIdSetMatch(@Mocked ServiceRegistryClient srClient) { - new Expectations() { - { - srClient.getMicroserviceId(anyString, anyString, anyString); - result = "serviceId"; - srClient.getMicroservice(anyString); - result = microservice; - } - }; - - MicroserviceRegisterTask registerTask = new MicroserviceRegisterTask(eventBus, srClient, microservice); - registerTask.run(); - - Assert.assertEquals(true, registerTask.isRegistered()); - Assert.assertEquals(true, registerTask.isSchemaIdSetMatch()); - Assert.assertEquals("serviceId", microservice.getServiceId()); - Assert.assertEquals("serviceId", microservice.getIntance().getServiceId()); - Assert.assertEquals(true, registerTask.isSchemaIdSetMatch()); - Assert.assertEquals(1, taskList.size()); - } - - @Test - public void testAlreadyRegisteredSchemaIdSetNotMatch(@Mocked ServiceRegistryClient srClient) { - Microservice otherMicroservice = new Microservice(); - otherMicroservice.setAppId(microservice.getAppId()); - otherMicroservice.setServiceName("ms1"); - otherMicroservice.addSchema("s1", ""); - - new Expectations() { - { - srClient.getMicroserviceId(anyString, anyString, anyString); - result = "serviceId"; - srClient.getMicroservice(anyString); - result = otherMicroservice; - } - }; - - MicroserviceRegisterTask registerTask = new MicroserviceRegisterTask(eventBus, srClient, microservice); - registerTask.run(); - - Assert.assertEquals(true, registerTask.isRegistered()); - Assert.assertEquals(false, registerTask.isSchemaIdSetMatch()); - Assert.assertEquals("serviceId", microservice.getServiceId()); - Assert.assertEquals("serviceId", microservice.getIntance().getServiceId()); - Assert.assertEquals(1, taskList.size()); - } - - @Test - public void testAlreadyRegisteredGetSchemaIdSetFailed(@Mocked ServiceRegistryClient srClient) { - Microservice otherMicroservice = new Microservice(); - otherMicroservice.setAppId(microservice.getAppId()); - otherMicroservice.setServiceName("ms1"); - otherMicroservice.addSchema("s1", ""); - - new Expectations() { - { - srClient.getMicroserviceId(anyString, anyString, anyString); - result = "serviceId"; - srClient.getMicroservice(anyString); - result = null; - } - }; - - MicroserviceRegisterTask registerTask = new MicroserviceRegisterTask(eventBus, srClient, microservice); - registerTask.run(); - - Assert.assertEquals(false, registerTask.isRegistered()); - Assert.assertEquals(false, registerTask.isSchemaIdSetMatch()); - Assert.assertEquals("serviceId", microservice.getServiceId()); - Assert.assertEquals(1, taskList.size()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestMicroserviceWatchTask.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestMicroserviceWatchTask.java deleted file mode 100644 index eaa4baf487d..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestMicroserviceWatchTask.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import javax.xml.ws.Holder; - -import org.junit.Assert; -import org.junit.Test; - -import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.Subscribe; - -import io.servicecomb.foundation.vertx.AsyncResultCallback; -import io.servicecomb.serviceregistry.api.MicroserviceKey; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.WatchAction; -import io.servicecomb.serviceregistry.api.response.MicroserviceInstanceChangedEvent; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.servicecomb.serviceregistry.task.event.ExceptionEvent; -import io.servicecomb.serviceregistry.task.event.RecoveryEvent; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestMicroserviceWatchTask { - EventBus eventBus = new EventBus(); - - MicroserviceWatchTask microserviceWatchTask; - - private void initWatch(ServiceRegistryConfig serviceRegistryConfig, - ServiceRegistryClient srClient, Microservice microservice) { - microserviceWatchTask = new MicroserviceWatchTask(eventBus, serviceRegistryConfig, srClient, microservice); - microserviceWatchTask.taskStatus = TaskStatus.READY; - new Expectations() { - { - serviceRegistryConfig.isWatch(); - result = true; - microservice.getServiceId(); - result = "serviceId"; - } - }; - } - - @Test - public void testWatchOpen(@Mocked ServiceRegistryConfig serviceRegistryConfig, - @Mocked ServiceRegistryClient srClient, - @Mocked Microservice microservice) { - initWatch(serviceRegistryConfig, srClient, microservice); - - new MockUp(srClient) { - @Mock - void watch(String selfMicroserviceId, AsyncResultCallback callback, - AsyncResultCallback onOpen, AsyncResultCallback onClose) { - onOpen.success(null); - } - }; - - Holder openHolder = new Holder<>(); - eventBus.register(new Object() { - @Subscribe - public void onOpen(RecoveryEvent event) { - openHolder.value = true; - } - }); - Assert.assertNull(openHolder.value); - microserviceWatchTask.run(); - Assert.assertTrue(openHolder.value); - } - - @Test - public void testWatchFailed(@Mocked ServiceRegistryConfig serviceRegistryConfig, - @Mocked ServiceRegistryClient srClient, - @Mocked Microservice microservice) { - initWatch(serviceRegistryConfig, srClient, microservice); - - new MockUp(srClient) { - @Mock - void watch(String selfMicroserviceId, AsyncResultCallback callback, - AsyncResultCallback onOpen, AsyncResultCallback onClose) { - callback.fail(new Error("test failed")); - } - }; - - Holder holder = new Holder<>(); - eventBus.register(new Object() { - @Subscribe - public void onException(ExceptionEvent event) { - holder.value = event.getThrowable(); - } - }); - Assert.assertNull(holder.value); - microserviceWatchTask.run(); - Assert.assertEquals("test failed", holder.value.getMessage()); - } - - @Test - public void testWatchInstanceChanged(@Mocked ServiceRegistryConfig serviceRegistryConfig, - @Mocked ServiceRegistryClient srClient, - @Mocked Microservice microservice) { - initWatch(serviceRegistryConfig, srClient, microservice); - - MicroserviceInstanceChangedEvent changedEvent = new MicroserviceInstanceChangedEvent(); - MicroserviceKey key = new MicroserviceKey(); - key.setAppId(microservice.getAppId()); - key.setVersion(microservice.getVersion()); - key.setServiceName(microservice.getServiceName()); - changedEvent.setKey(key); - changedEvent.setInstance(microservice.getIntance()); - - new MockUp(srClient) { - @Mock - void watch(String selfMicroserviceId, AsyncResultCallback callback, - AsyncResultCallback onOpen, AsyncResultCallback onClose) { - callback.success(changedEvent); - } - }; - - Holder holder = new Holder<>(); - eventBus.register(new Object() { - @Subscribe - public void onException(MicroserviceInstanceChangedEvent event) { - holder.value = event; - } - }); - - changedEvent.setAction(WatchAction.CREATE); - microserviceWatchTask.run(); - Assert.assertEquals(WatchAction.CREATE, holder.value.getAction()); - - changedEvent.setAction(WatchAction.DELETE); - microserviceWatchTask.run(); - Assert.assertEquals(WatchAction.DELETE, holder.value.getAction()); - - changedEvent.setAction(WatchAction.UPDATE); - microserviceWatchTask.run(); - Assert.assertEquals(WatchAction.UPDATE, holder.value.getAction()); - } - - @Test - public void testNeedToWatch(@Mocked ServiceRegistryConfig serviceRegistryConfig, - @Mocked ServiceRegistryClient srClient, - @Mocked Microservice microservice) { - EventBus eventBus = new EventBus(); - - MicroserviceWatchTask microserviceWatchTask = - new MicroserviceWatchTask(eventBus, serviceRegistryConfig, srClient, microservice); - microserviceWatchTask.taskStatus = TaskStatus.READY; - - new MockUp(srClient) { - @Mock - void watch(String selfMicroserviceId, AsyncResultCallback callback, - AsyncResultCallback onOpen, AsyncResultCallback onClose) { - throw new Error("called watch"); - } - }; - - new Expectations() { - { - serviceRegistryConfig.isWatch(); - result = false; - serviceRegistryConfig.isRegistryAutoDiscovery(); - result = false; - } - }; - // no watch - try { - microserviceWatchTask.run(); - } catch (Throwable e) { - Assert.fail("must do not watch"); - } - - new Expectations() { - { - serviceRegistryConfig.isWatch(); - result = true; - serviceRegistryConfig.isRegistryAutoDiscovery(); - result = false; - } - }; - // no watch - try { - microserviceWatchTask.run(); - } catch (Throwable e) { - // ready state, service id can not be null , will always watch - Assert.assertEquals("called watch", e.getMessage()); - } - - new Expectations() { - { - serviceRegistryConfig.isWatch(); - result = false; - serviceRegistryConfig.isRegistryAutoDiscovery(); - result = true; - } - }; - // no watch - try { - microserviceWatchTask.run(); - } catch (Throwable e) { - // ready state, service id can not be null , will always watch - Assert.assertEquals("called watch", e.getMessage()); - } - - new Expectations() { - { - serviceRegistryConfig.isWatch(); - result = false; - serviceRegistryConfig.isRegistryAutoDiscovery(); - result = true; - microservice.getServiceId(); - result = "serviceId"; - } - }; - // watch - try { - microserviceWatchTask.run(); - Assert.fail("must watch"); - } catch (Throwable e) { - Assert.assertEquals("called watch", e.getMessage()); - } - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestServiceCenterTask.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestServiceCenterTask.java deleted file mode 100644 index ab5ec0e8c36..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/task/TestServiceCenterTask.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.serviceregistry.task; - -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import com.google.common.eventbus.EventBus; - -import io.servicecomb.serviceregistry.ArchaiusUtils; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; -import io.servicecomb.serviceregistry.config.ServiceRegistryConfig; -import io.servicecomb.serviceregistry.task.event.ShutdownEvent; -import mockit.Deencapsulation; -import mockit.Mocked; - -public class TestServiceCenterTask { - private EventBus eventBus = new EventBus(); - - @Mocked - private MicroserviceServiceCenterTask microserviceServiceCenterTask; - - private ServiceCenterTask serviceCenterTask; - - @BeforeClass - public static void initClass() { - ArchaiusUtils.resetConfig(); - } - - @AfterClass - public static void teardownClass() { - ArchaiusUtils.resetConfig(); - } - - @Before - public void init() { - serviceCenterTask = - new ServiceCenterTask(eventBus, ServiceRegistryConfig.INSTANCE, microserviceServiceCenterTask); - } - - @Test - public void testLifeCycle() { - serviceCenterTask.init(); - - eventBus.post(new ShutdownEvent()); - Assert.assertFalse(Deencapsulation.getField(serviceCenterTask, "running")); - } - - @Test - public void testCalcSleepInterval(@Mocked ServiceRegistryClient srClient, - @Mocked Microservice microservice, @Mocked MicroserviceInstanceHeartbeatTask heartbeatTask, - @Mocked MicroserviceInstanceRegisterTask registerTask) { - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(1, serviceCenterTask.getInterval()); - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(2, serviceCenterTask.getInterval()); - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(3, serviceCenterTask.getInterval()); - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(10, serviceCenterTask.getInterval()); - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(20, serviceCenterTask.getInterval()); - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(30, serviceCenterTask.getInterval()); - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(40, serviceCenterTask.getInterval()); - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(50, serviceCenterTask.getInterval()); - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(60, serviceCenterTask.getInterval()); - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(60, serviceCenterTask.getInterval()); - - eventBus.post(heartbeatTask); - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(60, serviceCenterTask.getInterval()); - - // recover and exception again - registerTask.taskStatus = TaskStatus.FINISHED; - eventBus.post(registerTask); - registerTask.taskStatus = TaskStatus.INIT; - eventBus.post(registerTask); - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(1, serviceCenterTask.getInterval()); - - registerTask.taskStatus = TaskStatus.FINISHED; - eventBus.post(registerTask); - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(30, serviceCenterTask.getInterval()); - - registerTask.taskStatus = TaskStatus.INIT; - eventBus.post(registerTask); - serviceCenterTask.calcSleepInterval(); - Assert.assertEquals(1, serviceCenterTask.getInterval()); - } -} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/utils/TestTimer.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/utils/TestTimer.java deleted file mode 100644 index 32dab0223c0..00000000000 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/utils/TestTimer.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.serviceregistry.utils; - -import org.junit.Assert; -import org.junit.Test; - -/** - * Created by on 2017/3/13. - */ -public class TestTimer { - @Test(expected = TimerException.class) - public void testTimer() throws TimerException { - Timer timer = new Timer(); - Assert.assertEquals(Timer.DEFAULT_MAX_TIMEOUT, timer.getMax()); - Assert.assertEquals(Timer.DEFAULT_RETRY_TIMES, timer.getTimes()); - Assert.assertEquals(0, timer.getCurrent()); - Assert.assertEquals((timer.getCurrent() + 1) * Timer.DEFAULT_STEP_SIZE, timer.nextTimeout()); - Assert.assertEquals((timer.getCurrent() + 1) * Timer.DEFAULT_STEP_SIZE, timer.getNextTimeout()); - - Timer timer1 = new Timer(1, 2, 3, false); - Timer timer2 = new Timer(1); - timer2.reset(); - @SuppressWarnings("unused") - Timer timer3 = new Timer(1, 2); - Assert.assertNotNull(Timer.newDefaultTimer()); - Assert.assertNotNull(Timer.newForeverTimer()); - Assert.assertEquals(1, timer1.nextTimeout()); - Assert.assertEquals(1, timer1.nextTimeout()); - timer1.sleep(); - } -} diff --git a/service-registry/src/test/resources/META-INF/services/io.servicecomb.foundation.auth.AuthHeaderProvider b/service-registry/src/test/resources/META-INF/services/io.servicecomb.foundation.auth.AuthHeaderProvider deleted file mode 100644 index 858f6efc74c..00000000000 --- a/service-registry/src/test/resources/META-INF/services/io.servicecomb.foundation.auth.AuthHeaderProvider +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -io.servicecomb.serviceregistry.client.http.MockAuthHeaderProvider diff --git a/service-registry/src/test/resources/app1.yaml b/service-registry/src/test/resources/app1.yaml deleted file mode 100644 index d34a2b0a806..00000000000 --- a/service-registry/src/test/resources/app1.yaml +++ /dev/null @@ -1,3 +0,0 @@ -APPLICATION_ID: app1 -service_description: - name: ms1 \ No newline at end of file diff --git a/service-registry/src/test/resources/app2.yaml b/service-registry/src/test/resources/app2.yaml deleted file mode 100644 index df3a618c51a..00000000000 --- a/service-registry/src/test/resources/app2.yaml +++ /dev/null @@ -1,3 +0,0 @@ -APPLICATION_ID: app2 -service_description: - name: ms2 \ No newline at end of file diff --git a/service-registry/src/test/resources/config/log4j.cse.serviceregistry.client.properties b/service-registry/src/test/resources/config/log4j.cse.serviceregistry.client.properties deleted file mode 100644 index f84e0833ce5..00000000000 --- a/service-registry/src/test/resources/config/log4j.cse.serviceregistry.client.properties +++ /dev/null @@ -1,19 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=target/ -paas.logs.file=cse.log -log4j.rootLogger=INFO,paas,stdout diff --git a/service-registry/src/test/resources/log4j.properties b/service-registry/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/service-registry/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/service-registry/src/test/resources/microservice.yaml b/service-registry/src/test/resources/microservice.yaml deleted file mode 100644 index c1359d468fe..00000000000 --- a/service-registry/src/test/resources/microservice.yaml +++ /dev/null @@ -1,43 +0,0 @@ -service_description: - name: default - version: 0.0.1 - properties: - key1: value1 - key2: value2 - propertyExtentedClass: io.servicecomb.serviceregistry.MicroServicePropertyExtendedStub - paths: - - path: /test1/testpath - property: - checksession: false - - path: /test2/testpath - property: - checksession: true -instance_description: - properties: - key0: value0 - propertyExtentedClass: io.servicecomb.serviceregistry.MicroServicePropertyExtendedStub - -cse: - service: - registry: - address: http://127.0.0.1:9980 - client: - httpVersion: HTTP_1_1 - workerPoolSize: 1 - timeout: - connection: 30000 - idle: 30000 - instance: - watch: true - preferIpAddress: false - healthCheck: - interval: 1 - times: 5 - datacenter: - name: myDC - region: my-Region - availableZone: my-Zone -#ssl.keystore.path= -#ssl.keystore.pass= -#ssl.truststore.path= -#ssl.truststore.pass= diff --git a/service-registry/src/test/resources/registry.yaml b/service-registry/src/test/resources/registry.yaml deleted file mode 100644 index 70f632d70ba..00000000000 --- a/service-registry/src/test/resources/registry.yaml +++ /dev/null @@ -1,7 +0,0 @@ -springmvctest: - - id: "001" - version: "1.0" - appid: myapp - instances: - - endpoints: - - rest://127.0.0.1:8080 diff --git a/solutions/pom.xml b/solutions/pom.xml new file mode 100644 index 00000000000..4203f65309b --- /dev/null +++ b/solutions/pom.xml @@ -0,0 +1,38 @@ + + + + + + org.apache.servicecomb + java-chassis-parent + 3.4.0-SNAPSHOT + ../parents/default + + 4.0.0 + + solutions + Java Chassis::Solutions + pom + + + solution-basic + + + diff --git a/solutions/solution-basic/pom.xml b/solutions/solution-basic/pom.xml new file mode 100644 index 00000000000..1bb51a4b88c --- /dev/null +++ b/solutions/solution-basic/pom.xml @@ -0,0 +1,100 @@ + + + + + + org.apache.servicecomb + solutions + 3.4.0-SNAPSHOT + + 4.0.0 + + solution-basic + Java Chassis::Solutions::BASIC + + + + + org.apache.servicecomb + transport-rest-vertx + + + org.apache.servicecomb + transport-highway + + + org.apache.servicecomb + transport-rest-servlet + + + + + org.apache.servicecomb + provider-springmvc + + + org.apache.servicecomb + provider-pojo + + + org.apache.servicecomb + provider-jaxrs + + + + + org.apache.servicecomb + handler-fault-injection + + + org.apache.servicecomb + handler-flowcontrol-qps + + + org.apache.servicecomb + handler-governance + + + org.apache.servicecomb + handler-loadbalance + + + org.apache.servicecomb + handler-publickey-auth + + + org.apache.servicecomb + handler-router + + + + + org.apache.servicecomb + swagger-invocation-validator + + + + + org.apache.servicecomb + metrics-core + + + + diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthEndpoint.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthEndpoint.java new file mode 100644 index 00000000000..b340b663ad6 --- /dev/null +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthEndpoint.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.solution.basic.integration; + +import java.util.Map; + +import org.apache.servicecomb.foundation.metrics.health.HealthCheckResult; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +/** + * Java Chassis health check extensions. + */ +@Path("/scb/health") +public interface HealthEndpoint { + String NAME = "scb-health"; + + @Path("/") + @GET + boolean checkHealth(); + + @Path("/details") + @GET + Map checkHealthDetails(); +} diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthEndpointImpl.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthEndpointImpl.java new file mode 100644 index 00000000000..d8166999068 --- /dev/null +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthEndpointImpl.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.solution.basic.integration; + +import java.util.Map; + +import org.apache.servicecomb.foundation.metrics.health.HealthCheckResult; +import org.apache.servicecomb.foundation.metrics.health.HealthCheckerManager; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = HealthEndpoint.NAME, schemaInterface = HealthEndpoint.class) +public class HealthEndpointImpl implements HealthEndpoint { + @Override + public boolean checkHealth() { + Map results = HealthCheckerManager.getInstance().check(); + for (HealthCheckResult result : results.values()) { + if (!result.isHealthy()) { + return false; + } + } + return true; + } + + @Override + public Map checkHealthDetails() { + return HealthCheckerManager.getInstance().check(); + } +} diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthInstancePing.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthInstancePing.java new file mode 100644 index 00000000000..79db6818e61 --- /dev/null +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthInstancePing.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.solution.basic.integration; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.config.BootStrapProperties; +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.provider.consumer.InvokerUtils; +import org.apache.servicecomb.registry.discovery.InstancePing; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; +import org.apache.servicecomb.registry.discovery.TelnetInstancePing; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Lazy; +import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; + +public class HealthInstancePing implements InstancePing { + private static final Logger LOGGER = LoggerFactory.getLogger(HealthInstancePing.class); + + private SCBEngine scbEngine; + + private TelnetInstancePing telnetInstancePing; + + private Environment environment; + + @Autowired + @Lazy + public void setScbEngine(SCBEngine scbEngine) { + this.scbEngine = scbEngine; + } + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + @Lazy + public void setTelnetInstancePing(@Qualifier("scbTelnetInstancePing") TelnetInstancePing telnetInstancePing) { + this.telnetInstancePing = telnetInstancePing; + } + + @Override + public boolean ping(StatefulDiscoveryInstance instance) { + if (CollectionUtils.isEmpty(instance.getEndpoints())) { + return false; + } + + // hard coded here, not very nice + if ("local-registry".equals(instance.getRegistryName())) { + return telnetInstancePing.ping(instance); + } + + Map args = new HashMap<>(2); + args.put("instanceId", instance.getInstanceId()); + args.put("registryName", instance.getRegistryName()); + + for (String endpoint : instance.getEndpoints()) { + URI uri = URI.create(endpoint); + String transportName = uri.getScheme(); + Transport transport = scbEngine.getTransportManager().findTransport(transportName); + if (transport == null) { + continue; + } + // Use myself service name instead of the target. Because can avoid create + // MicroserviceReferenceConfig for the target. + Invocation invocation = InvokerUtils.createInvocation(BootStrapProperties.readServiceName(environment), + transportName, + ManagementEndpoint.NAME, "health", + args, boolean.class); + invocation.setEndpoint(new Endpoint(transport, endpoint, instance)); + boolean result; + try { + result = (boolean) InvokerUtils.syncInvoke(invocation); + } catch (Exception e) { + LOGGER.warn("ping instance {}/{}/{}/{} endpoint {} failed. {}", + instance.getApplication(), + instance.getServiceName(), + instance.getRegistryName(), + instance.getInstanceId(), endpoint, e.getMessage()); + continue; + } + if (result) { + return true; + } + 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/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/InstanceOpenAPIRegistry.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/InstanceOpenAPIRegistry.java new file mode 100644 index 00000000000..f8314d5afaa --- /dev/null +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/InstanceOpenAPIRegistry.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.solution.basic.integration; + +import java.net.URI; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.core.provider.OpenAPIRegistry; +import org.apache.servicecomb.core.provider.OpenAPIRegistryManager.OpenAPIChangeListener; +import org.apache.servicecomb.core.provider.consumer.InvokerUtils; +import org.apache.servicecomb.core.transport.TransportManager; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; + +import com.fasterxml.jackson.core.type.TypeReference; + +import io.swagger.v3.oas.models.OpenAPI; +import jakarta.ws.rs.core.Response.Status; + +public class InstanceOpenAPIRegistry implements OpenAPIRegistry { + private static final Logger LOGGER = LoggerFactory.getLogger(InstanceOpenAPIRegistry.class); + + private DiscoveryManager discoveryManager; + + private Environment environment; + + private TransportManager transportManager; + + @Autowired + public void setDiscoveryManager(DiscoveryManager discoveryManager) { + this.discoveryManager = discoveryManager; + } + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + public void setTransportManager(TransportManager transportManager) { + this.transportManager = transportManager; + } + + @Override + public boolean enabled() { + return environment.getProperty(OpenAPIRegistry.CONFIG_PREFIX + ".instance.enabled", boolean.class, true); + } + + @Override + public void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api) { + // do nothing + } + + @Override + @SuppressWarnings("unchecked") + public Map loadOpenAPI(String application, String serviceName) { + List discoveryInstances = + discoveryManager.getOrCreateVersionedCache(application, serviceName).data(); + + if (discoveryInstances.isEmpty()) { + throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "no instances"); + } + discoveryInstances.sort((a, b) -> VersionCompareUtil.compareVersion(b.getVersion(), a.getVersion())); + + String version = null; + for (DiscoveryInstance instance : discoveryInstances) { + if (version != null && !version.equals(instance.getVersion())) { + break; + } + version = instance.getVersion(); + for (String endpoint : instance.getEndpoints()) { + URI uri = URI.create(endpoint); + String transportName = uri.getScheme(); + Transport transport = transportManager.findTransport(transportName); + if (transport == null) { + continue; + } + // Use myself service name instead of the target. Because can avoid create + // MicroserviceReferenceConfig for the target. + Invocation invocation = InvokerUtils.createInvocation(BootStrapProperties.readServiceName(environment), + transportName, + ManagementEndpoint.NAME, "schemaContents", + new HashMap<>(), new TypeReference>() { + }.getType()); + invocation.setEndpoint(new Endpoint(transport, endpoint, discoveryInstances.get(0))); + try { + Map contents = (Map) InvokerUtils.syncInvoke(invocation); + Map result = new HashMap<>(contents.size()); + contents.forEach((k, v) -> result.put(k, SwaggerUtils.parseSwagger(v))); + return result; + } catch (InvocationException e) { + LOGGER.warn("Get schema contents {}/{}/{} from endpoint {} failed. {}", + instance.getApplication(), + instance.getServiceName(), + instance.getInstanceId(), endpoint, e.getMessage()); + } + } + } + throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "Get schema contents fail from all latest version."); + } + + @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 -9000; + } +} diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/IntegrationConfiguration.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/IntegrationConfiguration.java new file mode 100644 index 00000000000..dc80439ae81 --- /dev/null +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/IntegrationConfiguration.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.solution.basic.integration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class IntegrationConfiguration { + @Bean + public ManagementEndpointImpl managementEndpoint() { + return new ManagementEndpointImpl(); + } + + @Bean + public HealthEndpointImpl healthEndpoint() { + return new HealthEndpointImpl(); + } + + @Bean + public MetricsEndpointImpl metricsEndpoint() { + return new MetricsEndpointImpl(); + } + + @Bean + public HealthInstancePing healthInstancePing() { + return new HealthInstancePing(); + } + + @Bean + public InstanceOpenAPIRegistry instanceOpenAPIRegistry() { + return new InstanceOpenAPIRegistry(); + } +} diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpoint.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpoint.java new file mode 100644 index 00000000000..f41aa8a216f --- /dev/null +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpoint.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.solution.basic.integration; + +import java.util.Map; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; + +/** + * Java Chassis internal management apis. + */ +@Path("/scb/management") +public interface ManagementEndpoint { + String NAME = "scb-management"; + + /** + * Health of this instance. If the instanceId match this instance, and this service is ready + * to service return true. Otherwise, return false. + * + * This api is for internal instance status usage. Load balancer will call this api to check if + * the target instance is alive. + */ + @GET + @Path("/health") + boolean health(@QueryParam("instanceId") String instanceId, @QueryParam("registryName") String registryName); + + /** + * Schemas of this instance. + * + * This api is for internal schema loading usage. + */ + @POST + @Path("/schema/contents") + Map schemaContents(); +} diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpointImpl.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpointImpl.java new file mode 100644 index 00000000000..00852bde863 --- /dev/null +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpointImpl.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.solution.basic.integration; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.registry.RegistrationManager; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.springframework.beans.factory.annotation.Autowired; + +import io.swagger.v3.oas.models.OpenAPI; + +@RestSchema(schemaId = ManagementEndpoint.NAME, schemaInterface = ManagementEndpoint.class) +public class ManagementEndpointImpl implements ManagementEndpoint { + private RegistrationManager registrationManager; + + private LocalOpenAPIRegistry localOpenAPIRegistry; + + @Autowired + public void setRegistrationManager(RegistrationManager registrationManager) { + this.registrationManager = registrationManager; + } + + @Autowired + public void setLocalOpenAPIRegistry(LocalOpenAPIRegistry localOpenAPIRegistry) { + this.localOpenAPIRegistry = localOpenAPIRegistry; + } + + @Override + public boolean health(String instanceId, String registryName) { + if (StringUtils.isEmpty(instanceId) || StringUtils.isEmpty(registryName)) { + return false; + } + String mySelf = registrationManager.getInstanceId(registryName); + if (StringUtils.isEmpty(mySelf)) { + return false; + } + return mySelf.equals(instanceId); + } + + @Override + public Map schemaContents() { + Map apis = localOpenAPIRegistry.loadOpenAPI(); + Map result = new HashMap<>(apis.size()); + apis.forEach((k, v) -> result.put(k, SwaggerUtils.swaggerToString(v))); + return result; + } +} diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpoint.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpoint.java new file mode 100644 index 00000000000..4bfee7827d5 --- /dev/null +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpoint.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.solution.basic.integration; + +import java.util.Map; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +/** + * Java Chassis metrics. + */ +@Path("/scb/metrics") +public interface MetricsEndpoint { + String NAME = "scb-metrics"; + + @GET + @Path("/") + Map measure(); +} diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpointImpl.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpointImpl.java new file mode 100644 index 00000000000..e989a5b8a76 --- /dev/null +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpointImpl.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.solution.basic.integration; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.foundation.metrics.MetricsInitializer; +import org.apache.servicecomb.metrics.core.meter.vertx.EndpointMeter; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +import com.google.common.eventbus.EventBus; + +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.Meter.Id; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; + +@RestSchema(schemaId = MetricsEndpoint.NAME, schemaInterface = MetricsEndpoint.class) +public class MetricsEndpointImpl implements MetricsInitializer, MetricsEndpoint { + private MeterRegistry meterRegistry; + + @Override + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + this.meterRegistry = meterRegistry; + } + + @Override + public Map measure() { + Map measurements = new LinkedHashMap<>(); + StringBuilder sb = new StringBuilder(); + + for (Meter meter : this.meterRegistry.getMeters()) { + meter.measure().forEach(measurement -> { + String key = idToString(meter.getId(), measurement, sb); + measurements.put(key, measurement.getValue()); + }); + } + + return measurements; + } + + // format id to string: + // idName(tag1=value1,tag2=value2) + protected String idToString(Id id, Measurement measurement, StringBuilder sb) { + sb.setLength(0); + sb.append(id.getName()).append("(").append(EndpointMeter.STATISTIC).append("=") + .append(measurement.getStatistic().name()).append(","); + sb.append(StreamSupport + .stream(id + .getTags() + .spliterator(), false) + .map(this::tagToString) + .collect( + Collectors.joining(","))); + sb.append(')'); + + return sb.toString(); + } + + private String tagToString(Tag tag) { + return tag.getKey() + "=" + tag.getValue(); + } +} diff --git a/solutions/solution-basic/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/solutions/solution-basic/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..351463c398d --- /dev/null +++ b/solutions/solution-basic/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.solution.basic.integration.IntegrationConfiguration diff --git a/solutions/solution-basic/src/main/resources/microservice.yaml b/solutions/solution-basic/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..cead0718220 --- /dev/null +++ b/solutions/solution-basic/src/main/resources/microservice.yaml @@ -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. +## --------------------------------------------------------------------------- + +# order of this configure file +servicecomb-config-order: -100 + +servicecomb: +# metrics and access log + accesslog: + enabled: true + pattern: "|%SCB-traceId|%h|%r|%s|%D" + + metrics: + window_time: 300000 + invocation: + latencyDistribution: 0,10,50,100,1000 + Consumer.invocation.slow: + enabled: true + msTime: 1000 + Provider.invocation.slow: + enabled: true + msTime: 1000 + publisher.defaultLog: + enabled: true + endpoints.client.detail.enabled: true diff --git a/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestHealthEndpointImpl.java b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestHealthEndpointImpl.java new file mode 100644 index 00000000000..d0515a73105 --- /dev/null +++ b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestHealthEndpointImpl.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.solution.basic.integration; + +import java.util.Map; + +import org.apache.servicecomb.foundation.metrics.health.HealthCheckResult; +import org.apache.servicecomb.foundation.metrics.health.HealthChecker; +import org.apache.servicecomb.foundation.metrics.health.HealthCheckerManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class TestHealthEndpointImpl { + private final HealthChecker good = new HealthChecker() { + @Override + public String getName() { + return "test"; + } + + @Override + public HealthCheckResult check() { + return new HealthCheckResult(true, "info", "extra data"); + } + }; + + private final HealthChecker bad = new HealthChecker() { + @Override + public String getName() { + return "test2"; + } + + @Override + public HealthCheckResult check() { + return new HealthCheckResult(false, "info2", "extra data 2"); + } + }; + + + @BeforeEach + public void reset() { + HealthCheckerManager.getInstance().unregister(good.getName()); + HealthCheckerManager.getInstance().unregister(bad.getName()); + } + + @Test + public void checkHealthGood() { + HealthCheckerManager.getInstance().register(good); + HealthEndpointImpl publisher = new HealthEndpointImpl(); + Assertions.assertTrue(publisher.checkHealth()); + } + + @Test + public void checkHealthBad() { + HealthCheckerManager.getInstance().register(good); + HealthCheckerManager.getInstance().register(bad); + HealthEndpointImpl publisher = new HealthEndpointImpl(); + Assertions.assertFalse(publisher.checkHealth()); + } + + @Test + public void checkHealthDetails() { + HealthCheckerManager.getInstance().register(good); + HealthCheckerManager.getInstance().register(bad); + HealthEndpointImpl publisher = new HealthEndpointImpl(); + Map content = publisher.checkHealthDetails(); + Assertions.assertTrue(content.get("test").isHealthy()); + Assertions.assertEquals("info", content.get("test").getInformation()); + Assertions.assertEquals("extra data", content.get("test").getExtraData()); + } +} diff --git a/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestManagementEndpointImpl.java b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestManagementEndpointImpl.java new file mode 100644 index 00000000000..02e06025192 --- /dev/null +++ b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestManagementEndpointImpl.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.solution.basic.integration; + +public class TestManagementEndpointImpl { + +} diff --git a/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestMetricsEndpointImpl.java b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestMetricsEndpointImpl.java new file mode 100644 index 00000000000..cb175d9b7d8 --- /dev/null +++ b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestMetricsEndpointImpl.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.solution.basic.integration; + +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.Map; + +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +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 io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; + +public class TestMetricsEndpointImpl { + MetricsEndpointImpl publisher = new MetricsEndpointImpl(); + + 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); + } + + @Test + public void measure_globalRegistryNull() { + MeterRegistry registry = new SimpleMeterRegistry(); + publisher.init(registry, eventBus, new MetricsBootstrapConfig(environment)); + + Map result = publisher.measure(); + + Assertions.assertEquals(0, result.size()); + } + + @Test + public void measure_normal() { + MeterRegistry registry = new SimpleMeterRegistry(); + registry.timer("name", "t1", "v1", "t2", "v2"); + + publisher.init(registry, eventBus, new MetricsBootstrapConfig(environment)); + Map result = publisher.measure(); + + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(0, result.get("name(statistic=COUNT,t1=v1,t2=v2)"), 0); + Assertions.assertEquals(0, result.get("name(statistic=TOTAL_TIME,t1=v1,t2=v2)"), 0); + Assertions.assertEquals(0, result.get("name(statistic=MAX,t1=v1,t2=v2)"), 0); + } +} diff --git a/spring-boot-starter/pom.xml b/spring-boot-starter/pom.xml deleted file mode 100644 index cc85a0c2edc..00000000000 --- a/spring-boot-starter/pom.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - 4.0.0 - - io.servicecomb - java-chassis-parent - 0.3.0-SNAPSHOT - ../parent - - sprint-boot-starter-parent - pom - - spring-boot-starter-configuration - spring-boot-starter-provider - spring-boot-starter-transport - spring-boot-starter-discovery - spring-cloud-zuul-zipkin - spring-boot-starter-servicecomb - - - diff --git a/spring-boot-starter/spring-boot-starter-configuration/pom.xml b/spring-boot-starter/spring-boot-starter-configuration/pom.xml deleted file mode 100644 index d18a7d43f84..00000000000 --- a/spring-boot-starter/spring-boot-starter-configuration/pom.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - 4.0.0 - - io.servicecomb - sprint-boot-starter-parent - 0.3.0-SNAPSHOT - - - spring-boot-starter-configuration - - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.cloud - spring-cloud-commons - true - - - org.springframework.cloud - spring-cloud-context - true - - - javax.servlet - javax.servlet-api - compile - - - commons-configuration - commons-configuration - - - com.netflix.archaius - archaius-core - - - diff --git a/spring-boot-starter/spring-boot-starter-configuration/src/main/java/io/servicecomb/springboot/starter/configuration/ConfigurableEnvironmentConfiguration.java b/spring-boot-starter/spring-boot-starter-configuration/src/main/java/io/servicecomb/springboot/starter/configuration/ConfigurableEnvironmentConfiguration.java deleted file mode 100644 index be544dc2520..00000000000 --- a/spring-boot-starter/spring-boot-starter-configuration/src/main/java/io/servicecomb/springboot/starter/configuration/ConfigurableEnvironmentConfiguration.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.springboot.starter.configuration; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.springframework.core.env.CompositePropertySource; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.EnumerablePropertySource; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertySource; -import org.springframework.core.env.StandardEnvironment; - -/** - * - */ -public class ConfigurableEnvironmentConfiguration extends AbstractConfiguration { - - private final ConfigurableEnvironment environment; - - public ConfigurableEnvironmentConfiguration(ConfigurableEnvironment environment) { - this.environment = environment; - } - - @Override - protected void addPropertyDirect(String key, Object value) { - - } - - @Override - public boolean isEmpty() { - return !getKeys().hasNext(); - } - - @Override - public boolean containsKey(String key) { - return this.environment.containsProperty(key); - } - - @Override - public Object getProperty(String key) { - return this.environment.getProperty(key); - } - - @Override - public Iterator getKeys() { - List result = new ArrayList<>(); - for (Map.Entry> entry : getPropertySources().entrySet()) { - PropertySource source = entry.getValue(); - if (source instanceof EnumerablePropertySource) { - EnumerablePropertySource enumerable = (EnumerablePropertySource) source; - for (String name : enumerable.getPropertyNames()) { - result.add(name); - } - } - } - return result.iterator(); - } - - private Map> getPropertySources() { - Map> map = new LinkedHashMap<>(); - MutablePropertySources sources = (this.environment != null ? this.environment - .getPropertySources() : new StandardEnvironment().getPropertySources()); - for (PropertySource source : sources) { - extract("", map, source); - } - return map; - } - - private void extract(String root, Map> map, - PropertySource source) { - if (source instanceof CompositePropertySource) { - for (PropertySource nest : ((CompositePropertySource) source) - .getPropertySources()) { - extract(source.getName() + ":", map, nest); - } - } else { - map.put(root + source.getName(), source); - } - } -} diff --git a/spring-boot-starter/spring-boot-starter-configuration/src/main/java/io/servicecomb/springboot/starter/configuration/CseAutoConfiguration.java b/spring-boot-starter/spring-boot-starter-configuration/src/main/java/io/servicecomb/springboot/starter/configuration/CseAutoConfiguration.java deleted file mode 100644 index 0cd9d767d93..00000000000 --- a/spring-boot-starter/spring-boot-starter-configuration/src/main/java/io/servicecomb/springboot/starter/configuration/CseAutoConfiguration.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.configuration; - -import static com.netflix.config.ConfigurationManager.APPLICATION_PROPERTIES; -import static com.netflix.config.ConfigurationManager.DISABLE_DEFAULT_ENV_CONFIG; -import static com.netflix.config.ConfigurationManager.DISABLE_DEFAULT_SYS_CONFIG; -import static com.netflix.config.ConfigurationManager.ENV_CONFIG_NAME; -import static com.netflix.config.ConfigurationManager.SYS_CONFIG_NAME; -import static com.netflix.config.ConfigurationManager.URL_CONFIG_NAME; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -import javax.annotation.PreDestroy; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.commons.configuration.ConfigurationBuilder; -import org.apache.commons.configuration.EnvironmentConfiguration; -import org.apache.commons.configuration.SystemConfiguration; -import org.apache.commons.configuration.event.ConfigurationEvent; -import org.apache.commons.configuration.event.ConfigurationListener; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint; -import org.springframework.boot.actuate.endpoint.Endpoint; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.cloud.context.environment.EnvironmentChangeEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.Environment; -import org.springframework.util.ReflectionUtils; - -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DeploymentContext; -import com.netflix.config.DynamicProperty; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicURLConfiguration; - -/** - * - */ -@Configuration -@ConditionalOnClass({ConcurrentCompositeConfiguration.class, - ConfigurationBuilder.class}) -public class CseAutoConfiguration { - - private static final AtomicBoolean INITIALIZED = new AtomicBoolean(false); - - @Autowired - private ConfigurableEnvironment env; - - @Autowired(required = false) - private List externalConfigurations = new ArrayList<>(); - - @PreDestroy - public void close() { - setStatic(ConfigurationManager.class, "instance", null); - setStatic(ConfigurationManager.class, "customConfigurationInstalled", false); - setStatic(DynamicPropertyFactory.class, "config", null); - setStatic(DynamicPropertyFactory.class, "initializedWithDefaultConfig", false); - setStatic(DynamicProperty.class, "dynamicPropertySupportImpl", null); - INITIALIZED.compareAndSet(true, false); - } - - @Bean - public ConfigurableEnvironmentConfiguration configurableEnvironmentConfiguration() { - ConfigurableEnvironmentConfiguration envConfig = new ConfigurableEnvironmentConfiguration( - this.env); - configureArchaius(envConfig); - return envConfig; - } - - @Configuration - @ConditionalOnClass(Endpoint.class) - - protected static class ArchaiusEndpointConfiguration { - @Bean - @ConditionalOnEnabledEndpoint("archaius") - protected CseEndpoint archaiusEndpoint() { - return new CseEndpoint(); - } - } - - @Configuration - @ConditionalOnProperty(value = "archaius.propagate.environmentChangedEvent", matchIfMissing = true) - @ConditionalOnClass(EnvironmentChangeEvent.class) - protected static class PropagateEventsConfiguration - implements ApplicationListener { - @Autowired - private Environment env; - - @Override - public void onApplicationEvent(EnvironmentChangeEvent event) { - AbstractConfiguration manager = ConfigurationManager.getConfigInstance(); - for (String key : event.getKeys()) { - for (ConfigurationListener listener : manager - .getConfigurationListeners()) { - Object source = event.getSource(); - // TODO: Handle add vs set vs delete? - int type = AbstractConfiguration.EVENT_SET_PROPERTY; - String value = this.env.getProperty(key); - boolean beforeUpdate = false; - listener.configurationChanged(new ConfigurationEvent(source, type, - key, value, beforeUpdate)); - } - } - } - } - - protected void configureArchaius(ConfigurableEnvironmentConfiguration envConfig) { - if (INITIALIZED.compareAndSet(false, true)) { - String appName = this.env.getProperty("spring.application.name"); - if (appName == null) { - appName = "application"; - //log.warn("No spring.application.name found, defaulting to 'application'"); - } - System.setProperty(DeploymentContext.ContextKey.appId.getKey(), appName); - - ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration(); - - // support to add other Configurations (Jdbc, DynamoDb, Zookeeper, jclouds, - // etc...) - if (this.externalConfigurations != null) { - for (AbstractConfiguration externalConfig : this.externalConfigurations) { - config.addConfiguration(externalConfig); - } - } - config.addConfiguration(envConfig, - ConfigurableEnvironmentConfiguration.class.getSimpleName()); - - // below come from ConfigurationManager.createDefaultConfigInstance() - DynamicURLConfiguration defaultURLConfig = new DynamicURLConfiguration(); - try { - config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME); - } catch (Throwable ex) { - //log.error("Cannot create config from " + defaultURLConfig, ex); - } - - // TODO: sys/env above urls? - if (!Boolean.getBoolean(DISABLE_DEFAULT_SYS_CONFIG)) { - SystemConfiguration sysConfig = new SystemConfiguration(); - config.addConfiguration(sysConfig, SYS_CONFIG_NAME); - } - if (!Boolean.getBoolean(DISABLE_DEFAULT_ENV_CONFIG)) { - EnvironmentConfiguration environmentConfiguration = new EnvironmentConfiguration(); - config.addConfiguration(environmentConfiguration, ENV_CONFIG_NAME); - } - - ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration(); - config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES); - config.setContainerConfigurationIndex( - config.getIndexOfConfiguration(appOverrideConfig)); - - addArchaiusConfiguration(config); - } - // else { - // // TODO: reinstall ConfigurationManager - // //log.warn( - // // "Netflix ConfigurationManager has already been installed, unable to re-install"); - // } - } - - private void addArchaiusConfiguration(ConcurrentCompositeConfiguration config) { - if (ConfigurationManager.isConfigurationInstalled()) { - AbstractConfiguration installedConfiguration = ConfigurationManager - .getConfigInstance(); - if (installedConfiguration instanceof ConcurrentCompositeConfiguration) { - ConcurrentCompositeConfiguration configInstance = - (ConcurrentCompositeConfiguration) installedConfiguration; - configInstance.addConfiguration(config); - } else { - installedConfiguration.append(config); - // if (!(installedConfiguration instanceof AggregatedConfiguration)) { - // //log.warn( - // // "Appending a configuration to an existing non-aggregated - // //installed configuration will have no effect"); - // } - } - } else { - ConfigurationManager.install(config); - } - } - - private static void setStatic(Class type, String name, Object value) { - // Hack a private static field - Field field = ReflectionUtils.findField(type, name); - ReflectionUtils.makeAccessible(field); - ReflectionUtils.setField(field, null, value); - } -} diff --git a/spring-boot-starter/spring-boot-starter-configuration/src/main/java/io/servicecomb/springboot/starter/configuration/CseDelegatingProxyUtils.java b/spring-boot-starter/spring-boot-starter-configuration/src/main/java/io/servicecomb/springboot/starter/configuration/CseDelegatingProxyUtils.java deleted file mode 100644 index f665c08a2de..00000000000 --- a/spring-boot-starter/spring-boot-starter-configuration/src/main/java/io/servicecomb/springboot/starter/configuration/CseDelegatingProxyUtils.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.configuration; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ConfigurableApplicationContext; - -import com.netflix.config.ConfigurationManager; - -/** - * - */ -public final class CseDelegatingProxyUtils { - - private static final String APPLICATION_CONTEXT = ApplicationContext.class.getName(); - - private CseDelegatingProxyUtils() { - } - - public static T getNamedInstance(Class type, String name) { - ApplicationContext context = (ApplicationContext) ConfigurationManager - .getConfigInstance() - .getProperty(APPLICATION_CONTEXT); - return context != null && context.containsBean(name) ? context - .getBean(name, type) : null; - } - - public static T getInstanceWithPrefix(Class type, String prefix) { - String name = prefix + type.getSimpleName(); - return getNamedInstance(type, name); - } - - public static void addApplicationContext(ConfigurableApplicationContext context) { - AbstractConfiguration config = ConfigurationManager.getConfigInstance(); - config.clearProperty(APPLICATION_CONTEXT); - config.setProperty(APPLICATION_CONTEXT, context); - } -} diff --git a/spring-boot-starter/spring-boot-starter-configuration/src/main/java/io/servicecomb/springboot/starter/configuration/CseEndpoint.java b/spring-boot-starter/spring-boot-starter-configuration/src/main/java/io/servicecomb/springboot/starter/configuration/CseEndpoint.java deleted file mode 100644 index 94c917623ec..00000000000 --- a/spring-boot-starter/spring-boot-starter-configuration/src/main/java/io/servicecomb/springboot/starter/configuration/CseEndpoint.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.configuration; - -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.EnvironmentConfiguration; -import org.apache.commons.configuration.SystemConfiguration; -import org.springframework.boot.actuate.endpoint.AbstractEndpoint; - -import com.netflix.config.ConcurrentCompositeConfiguration; -import com.netflix.config.ConfigurationManager; - -/** - * - */ -public class CseEndpoint extends AbstractEndpoint> { - - public CseEndpoint() { - super("archaius"); - } - - @Override - public Map invoke() { - Map map = new LinkedHashMap(); - AbstractConfiguration config = ConfigurationManager.getConfigInstance(); - if (config instanceof ConcurrentCompositeConfiguration) { - ConcurrentCompositeConfiguration composite = (ConcurrentCompositeConfiguration) config; - for (Configuration item : composite.getConfigurations()) { - append(map, item); - } - } else { - append(map, config); - } - return map; - } - - private void append(Map map, Configuration config) { - if (config instanceof ConfigurableEnvironmentConfiguration) { - return; - } - if (config instanceof SystemConfiguration) { - return; - } - if (config instanceof EnvironmentConfiguration) { - return; - } - for (Iterator iter = config.getKeys(); iter.hasNext();) { - String key = iter.next(); - map.put(key, config.getProperty(key)); - } - } -} diff --git a/spring-boot-starter/spring-boot-starter-configuration/src/main/resources/META-INF/spring.factories b/spring-boot-starter/spring-boot-starter-configuration/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 206c00e83ab..00000000000 --- a/spring-boot-starter/spring-boot-starter-configuration/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.servicecomb.springboot.starter.configuration.CseAutoConfiguration \ No newline at end of file diff --git a/spring-boot-starter/spring-boot-starter-configuration/src/test/java/io/servicecomb/springboot/starter/configuration/TestConfigurableEnvironmentConfiguration.java b/spring-boot-starter/spring-boot-starter-configuration/src/test/java/io/servicecomb/springboot/starter/configuration/TestConfigurableEnvironmentConfiguration.java deleted file mode 100644 index 4ca591c824f..00000000000 --- a/spring-boot-starter/spring-boot-starter-configuration/src/test/java/io/servicecomb/springboot/starter/configuration/TestConfigurableEnvironmentConfiguration.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.configuration; - - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; -import org.springframework.core.env.ConfigurableEnvironment; - -/** - * - */ -public class TestConfigurableEnvironmentConfiguration { - - private ConfigurableEnvironmentConfiguration configurableEnvironmentConfiguration; - - @Before - public void setUp() throws Exception { - configurableEnvironmentConfiguration = new ConfigurableEnvironmentConfiguration(null); - } - - @After - public void tearDown() throws Exception { - configurableEnvironmentConfiguration = null; - } - - @Test - public void testIsEmpty() { - Assert.assertFalse(configurableEnvironmentConfiguration.isEmpty()); - } - - @Test - public void testContainsKey() { - configurableEnvironmentConfiguration = - new ConfigurableEnvironmentConfiguration(Mockito.mock(ConfigurableEnvironment.class)); - Assert.assertFalse(configurableEnvironmentConfiguration.containsKey("path.separator")); - } - - @Test - public void testGetProperty() { - configurableEnvironmentConfiguration = - new ConfigurableEnvironmentConfiguration(Mockito.mock(ConfigurableEnvironment.class)); - Assert.assertEquals(null, configurableEnvironmentConfiguration.getProperty("path.separator")); - } -} diff --git a/spring-boot-starter/spring-boot-starter-configuration/src/test/java/io/servicecomb/springboot/starter/configuration/TestCseAutoConfiguration.java b/spring-boot-starter/spring-boot-starter-configuration/src/test/java/io/servicecomb/springboot/starter/configuration/TestCseAutoConfiguration.java deleted file mode 100644 index 2ea209c0d98..00000000000 --- a/spring-boot-starter/spring-boot-starter-configuration/src/test/java/io/servicecomb/springboot/starter/configuration/TestCseAutoConfiguration.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.configuration; - - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.springframework.core.env.ConfigurableEnvironment; - -import mockit.Injectable; -import mockit.Tested; - -/** - * - */ -public class TestCseAutoConfiguration { - - @Injectable - private ConfigurableEnvironment env; - - @Tested - private CseAutoConfiguration cseAutoConfiguration; - - @Before - public void setUp() throws Exception { - cseAutoConfiguration = new CseAutoConfiguration(); - } - - @After - public void tearDown() throws Exception { - cseAutoConfiguration.close(); - cseAutoConfiguration = null; - } - - @Test - public void testConfigurableEnvironmentConfiguration() { - Assert.assertTrue(cseAutoConfiguration.configurableEnvironmentConfiguration().isEmpty()); - } -} diff --git a/spring-boot-starter/spring-boot-starter-configuration/src/test/java/io/servicecomb/springboot/starter/configuration/TestCseDelegatingProxyUtils.java b/spring-boot-starter/spring-boot-starter-configuration/src/test/java/io/servicecomb/springboot/starter/configuration/TestCseDelegatingProxyUtils.java deleted file mode 100644 index 3f93d467402..00000000000 --- a/spring-boot-starter/spring-boot-starter-configuration/src/test/java/io/servicecomb/springboot/starter/configuration/TestCseDelegatingProxyUtils.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.configuration; - -import org.apache.commons.configuration.AbstractConfiguration; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ConfigurableApplicationContext; - -import com.netflix.config.ConfigurationManager; - -/** - * - */ -public class TestCseDelegatingProxyUtils { - - @AfterClass - public static void tearDown() { - AbstractConfiguration config = ConfigurationManager.getConfigInstance(); - config.clearProperty(ApplicationContext.class.getName()); - } - - @Test - public void testGetNamedInstance() { - CseDelegatingProxyUtils.addApplicationContext(Mockito.mock(ConfigurableApplicationContext.class)); - Assert.assertNull(CseDelegatingProxyUtils.getNamedInstance(this.getClass(), "TestCseDelegatingProxyUtils")); - } - - @Test - public void testGetInstanceWithPrefix() { - Assert.assertNull( - CseDelegatingProxyUtils.getInstanceWithPrefix(this.getClass(), "TestCseDelegatingProxyUtils")); - } -} diff --git a/spring-boot-starter/spring-boot-starter-configuration/src/test/java/io/servicecomb/springboot/starter/configuration/TestCseEndpoint.java b/spring-boot-starter/spring-boot-starter-configuration/src/test/java/io/servicecomb/springboot/starter/configuration/TestCseEndpoint.java deleted file mode 100644 index af0476ebd04..00000000000 --- a/spring-boot-starter/spring-boot-starter-configuration/src/test/java/io/servicecomb/springboot/starter/configuration/TestCseEndpoint.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.configuration; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -/** - * - */ -public class TestCseEndpoint { - - private CseEndpoint cseEndpoint; - - @Before - public void setUp() throws Exception { - cseEndpoint = new CseEndpoint(); - } - - @After - public void tearDown() throws Exception { - cseEndpoint = null; - } - - @Test - public void testInvoke() { - Assert.assertTrue(cseEndpoint.invoke().isEmpty()); - } -} diff --git a/spring-boot-starter/spring-boot-starter-discovery/pom.xml b/spring-boot-starter/spring-boot-starter-discovery/pom.xml deleted file mode 100644 index 5395b8cddad..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/pom.xml +++ /dev/null @@ -1,113 +0,0 @@ - - - - 4.0.0 - - io.servicecomb - sprint-boot-starter-parent - 0.3.0-SNAPSHOT - - spring-boot-starter-discovery - - - - org.apache.tomcat.embed - tomcat-embed-logging-juli - - - org.springframework.boot - spring-boot-starter - - - ch.qos.logback - logback-classic - - - org.slf4j - log4j-over-slf4j - - - - - org.springframework.cloud - spring-cloud-commons - - - org.springframework.cloud - spring-cloud-context - - - org.springframework.cloud - spring-cloud-netflix-core - - - org.springframework.cloud - spring-cloud-starter-zuul - - - org.springframework.cloud - spring-cloud-starter-ribbon - - - javax.ws.rs - jsr311-api - - - - - org.springframework.cloud - spring-cloud-starter-hystrix - - - io.servicecomb - common-rest - - - io.servicecomb - java-chassis-core - - - io.servicecomb - provider-pojo - - - io.servicecomb - provider-rest-common - - - io.servicecomb - provider-jaxrs - - - io.servicecomb - provider-springmvc - - - io.servicecomb - transport-rest-client - - - io.servicecomb - transport-rest-servlet - - - io.servicecomb - handler-loadbalance - - - diff --git a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseDiscoveryClient.java b/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseDiscoveryClient.java deleted file mode 100644 index 8ab4ec388ec..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseDiscoveryClient.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.discovery; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import org.springframework.cloud.client.DefaultServiceInstance; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.discovery.DiscoveryClient; - -import io.servicecomb.core.provider.consumer.ConsumerProviderManager; -import io.servicecomb.core.provider.consumer.ReferenceConfig; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.api.registry.Microservice; -import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; -import io.servicecomb.serviceregistry.client.ServiceRegistryClient; - -public class CseDiscoveryClient implements DiscoveryClient { - - @Inject - private ConsumerProviderManager consumerProviderManager; - - @Override - public String description() { - return "Spring Cloud CSE Discovery Client"; - } - - @Override - public List getInstances(final String serviceId) { - List instances = new ArrayList(); - ServiceRegistryClient client = RegistryUtils.getServiceRegistryClient(); - String appId = RegistryUtils.getAppId(); - ReferenceConfig referenceConfig = consumerProviderManager.getReferenceConfig(serviceId); - String versionRule = referenceConfig.getMicroserviceVersionRule(); - String cseServiceID = client.getMicroserviceId(appId, serviceId, versionRule); - List cseServices = client.getMicroserviceInstance(cseServiceID, cseServiceID); - if (null != cseServices && !cseServices.isEmpty()) { - for (MicroserviceInstance instance : cseServices) { - List eps = instance.getEndpoints(); - for (String ep : eps) { - URIEndpointObject uri = new URIEndpointObject(ep); - instances.add(new DefaultServiceInstance(instance.getServiceId(), uri.getHostOrIp(), - uri.getPort(), false)); - } - } - } - return instances; - } - - @Override - public ServiceInstance getLocalServiceInstance() { - return null; - } - - @Override - public List getServices() { - ServiceRegistryClient client = RegistryUtils.getServiceRegistryClient(); - List services = client.getAllMicroservices(); - List serviceIDList = new ArrayList<>(); - if (null != services && !services.isEmpty()) { - for (Microservice service : services) { - serviceIDList.add(service.getServiceName()); - } - } - return serviceIDList; - } -} diff --git a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseDiscoveryClientConfiguration.java b/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseDiscoveryClientConfiguration.java deleted file mode 100644 index b60a15bddb0..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseDiscoveryClientConfiguration.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.discovery; - -import org.springframework.boot.autoconfigure.AutoConfigureBefore; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration; -import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; - -import io.servicecomb.core.provider.consumer.ConsumerProviderManager; - -@AutoConfigureBefore(NoopDiscoveryClientAutoConfiguration.class) -@Configuration -public class CseDiscoveryClientConfiguration { - - @Bean - public CseDiscoveryProperties cseDiscoveryProperties() { - return new CseDiscoveryProperties(); - } - - @Bean - @ConditionalOnBean(ZuulProperties.class) - public CseRoutesProperties cseRoutesProperties(ConsumerProviderManager manager) { - return new CseRoutesProperties(manager); - } - - @Bean - @Primary - public DiscoveryClient cseDiscoveryClient() { - return new CseDiscoveryClient(); - } -} diff --git a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseDiscoveryProperties.java b/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseDiscoveryProperties.java deleted file mode 100644 index 60847bd7e1d..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseDiscoveryProperties.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.discovery; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -@ConfigurationProperties("spring.cloud.cse") -public class CseDiscoveryProperties { - - /** Hostname to use when accessing server */ - private String host; - - /** Port to register the service under (defaults to listening port) */ - private String port; - - public CseDiscoveryProperties() { - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public String getPort() { - return port; - } - - public void setPort(String port) { - this.port = port; - } -} diff --git a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseRoutesProperties.java b/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseRoutesProperties.java deleted file mode 100644 index 85dc9dfd4b8..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseRoutesProperties.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.discovery; - -import org.springframework.beans.factory.annotation.Autowired; - -import io.servicecomb.core.provider.consumer.ConsumerProviderManager; -import io.servicecomb.core.provider.consumer.ReferenceConfig; -import io.servicecomb.serviceregistry.RegistryUtils; - -public final class CseRoutesProperties { - - private final ConsumerProviderManager consumerProviderManager; - - @Autowired - CseRoutesProperties(ConsumerProviderManager consumerProviderManager) { - this.consumerProviderManager = consumerProviderManager; - } - - String getVersionRule(String serviceName) { - ReferenceConfig referenceConfig = consumerProviderManager.getReferenceConfig(serviceName); - return referenceConfig.getMicroserviceVersionRule(); - } - - String getAppID() { - return RegistryUtils.getAppId(); - } -} diff --git a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseServerListCacheWrapper.java b/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseServerListCacheWrapper.java deleted file mode 100644 index 59e48abe3c9..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseServerListCacheWrapper.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.discovery; - -import com.netflix.loadbalancer.Server; - -import io.servicecomb.core.Transport; -import io.servicecomb.loadbalance.ServerListCache; -import io.servicecomb.serviceregistry.cache.CacheEndpoint; - -public class CseServerListCacheWrapper extends ServerListCache { - - public CseServerListCacheWrapper(String appId, String microserviceName, String microserviceVersionRule, - String transportName) { - super(appId, microserviceName, microserviceVersionRule, transportName); - } - - @Override - protected Server createEndpoint(Transport transport, CacheEndpoint cacheEndpoint) { - return new CseServerWrapper(transport, cacheEndpoint); - } -} diff --git a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseServerWrapper.java b/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseServerWrapper.java deleted file mode 100644 index 5abd0aef00f..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/CseServerWrapper.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.discovery; - -import io.servicecomb.core.Transport; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.loadbalance.CseServer; -import io.servicecomb.serviceregistry.cache.CacheEndpoint; - -public class CseServerWrapper extends CseServer { - - public CseServerWrapper(Transport transport, CacheEndpoint cacheEndpoint) { - super(transport, cacheEndpoint); - } - - // used in LoadBalancerContext - public String getHost() { - URIEndpointObject host = (URIEndpointObject) getEndpoint().getAddress(); - return host.getHostOrIp(); - } - - public int getPort() { - URIEndpointObject host = (URIEndpointObject) getEndpoint().getAddress(); - return host.getPort(); - } -} diff --git a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/RibbonCseAutoConfiguration.java b/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/RibbonCseAutoConfiguration.java deleted file mode 100644 index 93ba4983452..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/RibbonCseAutoConfiguration.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.discovery; - -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; -import org.springframework.cloud.netflix.ribbon.RibbonClients; -import org.springframework.cloud.netflix.ribbon.SpringClientFactory; -import org.springframework.context.annotation.Configuration; - -import ribbon.io.servicecomb.springboot.starter.discovery.CseRibbonClientConfiguration; - -@Configuration -@EnableConfigurationProperties -@ConditionalOnBean(SpringClientFactory.class) -@AutoConfigureAfter(RibbonAutoConfiguration.class) -@RibbonClients(defaultConfiguration = CseRibbonClientConfiguration.class) -public class RibbonCseAutoConfiguration { - -} diff --git a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/ServiceCombDiscoveryException.java b/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/ServiceCombDiscoveryException.java deleted file mode 100644 index 1517fcb20b8..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/ServiceCombDiscoveryException.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.springboot.starter.discovery; - -public class ServiceCombDiscoveryException extends RuntimeException { - private static final long serialVersionUID = 3806741463767943277L; - - public ServiceCombDiscoveryException(String message) { - super(message); - } -} diff --git a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/ServiceCombServerList.java b/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/ServiceCombServerList.java deleted file mode 100644 index e61598ffbc5..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/ServiceCombServerList.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.discovery; - -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.client.config.IClientConfig; -import com.netflix.loadbalancer.AbstractServerList; -import com.netflix.loadbalancer.Server; - -import io.servicecomb.loadbalance.ServerListCache; - -public class ServiceCombServerList extends AbstractServerList { - - private static final Logger logger = LoggerFactory.getLogger(ServiceCombServerList.class); - - private final CseRoutesProperties config; - - private ServerListCache serverListCache; - - private String serviceId; - - public ServiceCombServerList(CseRoutesProperties config) { - this.config = config; - } - - @Override - public List getInitialListOfServers() { - return servers(); - } - - @Override - public List getUpdatedListOfServers() { - return servers(); - } - - private List servers() { - if (serverListCache == null) { - throw new ServiceCombDiscoveryException("Service list is not initialized"); - } - - logger.info("Looking for service with app id: {}, service id: {}, version rule: {}", - config.getAppID(), - serviceId, - config.getVersionRule(serviceId)); - - List endpoints = serverListCache.getLatestEndpoints(); - - logger.info("Found service endpoints {}", endpoints); - return endpoints; - } - - @Override - public void initWithNiwsConfig(IClientConfig iClientConfig) { - serviceId = iClientConfig.getClientName(); - - serverListCache = new CseServerListCacheWrapper( - config.getAppID(), - serviceId, - config.getVersionRule(serviceId), - "rest"); - } -} diff --git a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/package-info.java b/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/package-info.java deleted file mode 100644 index d5e954d8788..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/io/servicecomb/springboot/starter/discovery/package-info.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.discovery; diff --git a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/ribbon/io/servicecomb/springboot/starter/discovery/CseRibbonClientConfiguration.java b/spring-boot-starter/spring-boot-starter-discovery/src/main/java/ribbon/io/servicecomb/springboot/starter/discovery/CseRibbonClientConfiguration.java deleted file mode 100644 index 41b5b5ef388..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/src/main/java/ribbon/io/servicecomb/springboot/starter/discovery/CseRibbonClientConfiguration.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ribbon.io.servicecomb.springboot.starter.discovery; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.cloud.netflix.zuul.filters.ZuulProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import com.netflix.client.config.IClientConfig; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerList; - -import io.servicecomb.springboot.starter.discovery.CseRoutesProperties; -import io.servicecomb.springboot.starter.discovery.ServiceCombServerList; - -/** - * Custom {@link org.springframework.cloud.netflix.ribbon.RibbonClient} configuration must not be - * scanned by spring. - * - * @see - * Customizing the Ribbon Client - */ -@Configuration -@ConditionalOnBean(ZuulProperties.class) -public class CseRibbonClientConfiguration { - - @Bean - ServerList ribbonServerList( - IClientConfig config, - CseRoutesProperties cseRoutesProperties) { - - ServiceCombServerList serverList = new ServiceCombServerList(cseRoutesProperties); - serverList.initWithNiwsConfig(config); - return serverList; - } -} diff --git a/spring-boot-starter/spring-boot-starter-discovery/src/main/resources/META-INF/spring.factories b/spring-boot-starter/spring-boot-starter-discovery/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 8f119a0158e..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,3 +0,0 @@ -io.servicecomb.springboot.starter.provider.EnableServiceComb=\ - io.servicecomb.springboot.starter.discovery.CseDiscoveryClientConfiguration,\ - io.servicecomb.springboot.starter.discovery.RibbonCseAutoConfiguration diff --git a/spring-boot-starter/spring-boot-starter-discovery/src/test/java/io/servicecomb/springboot/starter/discovery/ServiceCombServerListTest.java b/spring-boot-starter/spring-boot-starter-discovery/src/test/java/io/servicecomb/springboot/starter/discovery/ServiceCombServerListTest.java deleted file mode 100644 index eb5fadde08c..00000000000 --- a/spring-boot-starter/spring-boot-starter-discovery/src/test/java/io/servicecomb/springboot/starter/discovery/ServiceCombServerListTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.springboot.starter.discovery; - -import static com.seanyinx.github.unit.scaffolding.AssertUtils.expectFailing; -import static io.servicecomb.core.Const.DEFAULT_VERSION_RULE; -import static org.hamcrest.core.Is.is; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.seanyinx.github.unit.scaffolding.Randomness; - -import io.servicecomb.core.provider.consumer.ConsumerProviderManager; -import io.servicecomb.core.provider.consumer.ReferenceConfig; - -public class ServiceCombServerListTest { - - private final ReferenceConfig referenceConfig = mock(ReferenceConfig.class); - - private final ConsumerProviderManager manager = mock(ConsumerProviderManager.class); - - private final CseRoutesProperties properties = new CseRoutesProperties(manager); - - private final ServiceCombServerList serverList = new ServiceCombServerList(properties); - - private String serviceId = Randomness.uniquify("serviceId"); - - @Before - public void setUp() throws Exception { - when(manager.getReferenceConfig(serviceId)).thenReturn(referenceConfig); - when(referenceConfig.getMicroserviceVersionRule()).thenReturn(DEFAULT_VERSION_RULE); - } - - @Test - public void blowsUpWhenServerListNotInitialized() { - try { - serverList.getInitialListOfServers(); - expectFailing(ServiceCombDiscoveryException.class); - } catch (ServiceCombDiscoveryException e) { - Assert.assertThat(e.getMessage(), is("Service list is not initialized")); - } - } -} diff --git a/spring-boot-starter/spring-boot-starter-provider/pom.xml b/spring-boot-starter/spring-boot-starter-provider/pom.xml deleted file mode 100644 index 1c248eb3b83..00000000000 --- a/spring-boot-starter/spring-boot-starter-provider/pom.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - sprint-boot-starter-parent - io.servicecomb - 0.3.0-SNAPSHOT - - 4.0.0 - - spring-boot-starter-provider - - - - io.servicecomb - spring-boot-starter-servicecomb - - - io.servicecomb - transport-rest-vertx - - - - \ No newline at end of file diff --git a/spring-boot-starter/spring-boot-starter-provider/src/main/resources/application.properties b/spring-boot-starter/spring-boot-starter-provider/src/main/resources/application.properties deleted file mode 100644 index fb99724c679..00000000000 --- a/spring-boot-starter/spring-boot-starter-provider/src/main/resources/application.properties +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -spring.main.web-environment=false \ No newline at end of file diff --git a/spring-boot-starter/spring-boot-starter-servicecomb/pom.xml b/spring-boot-starter/spring-boot-starter-servicecomb/pom.xml deleted file mode 100644 index 3df21746a9e..00000000000 --- a/spring-boot-starter/spring-boot-starter-servicecomb/pom.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - 4.0.0 - - io.servicecomb - sprint-boot-starter-parent - 0.3.0-SNAPSHOT - - spring-boot-starter-servicecomb - - - - org.apache.tomcat.embed - tomcat-embed-logging-juli - - - org.springframework.boot - spring-boot-starter - - - ch.qos.logback - logback-classic - - - org.slf4j - log4j-over-slf4j - - - - - io.servicecomb - provider-springmvc - - - io.servicecomb - provider-jaxrs - - - io.servicecomb - provider-pojo - - - io.servicecomb - handler-bizkeeper - - - io.servicecomb - handler-loadbalance - - - io.servicecomb - transport-highway - - - io.servicecomb - handler-flowcontrol-qps - - - diff --git a/spring-boot-starter/spring-boot-starter-servicecomb/src/main/java/io/servicecomb/springboot/starter/provider/EnableServiceComb.java b/spring-boot-starter/spring-boot-starter-servicecomb/src/main/java/io/servicecomb/springboot/starter/provider/EnableServiceComb.java deleted file mode 100644 index c076f9e66f9..00000000000 --- a/spring-boot-starter/spring-boot-starter-servicecomb/src/main/java/io/servicecomb/springboot/starter/provider/EnableServiceComb.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.provider; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.context.annotation.Import; - -/** - * This annotation enables auto-configuration of Spring beans to integrate with ServiceComb modules. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Import(ServiceCombSpringConfiguration.class) -public @interface EnableServiceComb { - -} diff --git a/spring-boot-starter/spring-boot-starter-servicecomb/src/main/java/io/servicecomb/springboot/starter/provider/ServiceCombSpringConfiguration.java b/spring-boot-starter/spring-boot-starter-servicecomb/src/main/java/io/servicecomb/springboot/starter/provider/ServiceCombSpringConfiguration.java deleted file mode 100644 index 958cd8ec13c..00000000000 --- a/spring-boot-starter/spring-boot-starter-servicecomb/src/main/java/io/servicecomb/springboot/starter/provider/ServiceCombSpringConfiguration.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.springboot.starter.provider; - -import javax.inject.Inject; - -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.ImportResource; - -import io.servicecomb.core.CseApplicationListener; -import io.servicecomb.foundation.common.utils.BeanUtils; - -@Configuration -@ImportResource(BeanUtils.DEFAULT_BEAN_RESOURCE) -class ServiceCombSpringConfiguration { - @Inject - public void setCseApplicationListener(CseApplicationListener cseApplicationListener) { - cseApplicationListener.setInitEventClass(ApplicationReadyEvent.class); - } -} diff --git a/spring-boot-starter/spring-boot-starter-servicecomb/src/main/java/io/servicecomb/springboot/starter/provider/package-info.java b/spring-boot-starter/spring-boot-starter-servicecomb/src/main/java/io/servicecomb/springboot/starter/provider/package-info.java deleted file mode 100644 index 138acc497a0..00000000000 --- a/spring-boot-starter/spring-boot-starter-servicecomb/src/main/java/io/servicecomb/springboot/starter/provider/package-info.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * - */ -/** - * - * - */ -package io.servicecomb.springboot.starter.provider; diff --git a/spring-boot-starter/spring-boot-starter-transport/pom.xml b/spring-boot-starter/spring-boot-starter-transport/pom.xml deleted file mode 100644 index 0d26a0282df..00000000000 --- a/spring-boot-starter/spring-boot-starter-transport/pom.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - 4.0.0 - - io.servicecomb - sprint-boot-starter-parent - 0.3.0-SNAPSHOT - - - spring-boot-starter-transport - - - - io.servicecomb - spring-boot-starter-servicecomb - - - io.servicecomb - transport-rest-servlet - - - javax.servlet - javax.servlet-api - compile - - - diff --git a/spring-boot-starter/spring-boot-starter-transport/src/main/java/io/servicecomb/springboot/starter/transport/RestServletInitializer.java b/spring-boot-starter/spring-boot-starter-transport/src/main/java/io/servicecomb/springboot/starter/transport/RestServletInitializer.java deleted file mode 100644 index bba393c6162..00000000000 --- a/spring-boot-starter/spring-boot-starter-transport/src/main/java/io/servicecomb/springboot/starter/transport/RestServletInitializer.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.springboot.starter.transport; - -import java.io.IOException; -import java.net.ServerSocket; - -import javax.servlet.ServletContext; -import javax.servlet.ServletException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.context.embedded.AbstractConfigurableEmbeddedServletContainer; -import org.springframework.boot.web.servlet.ServletContextInitializer; -import org.springframework.stereotype.Component; - -import io.servicecomb.transport.rest.servlet.RestServletInjector; -import io.servicecomb.transport.rest.servlet.ServletUtils; - -@Component -// extends from AbstractConfigurableEmbeddedServletContainer, only want to get embed web container's port and address -// when org.springframework.web.context.ServletContextAware invoked, ServletContext already rejected to addServlet -// should implements org.springframework.boot.web.servlet.ServletContextInitializer to inject servlet -public class RestServletInitializer extends AbstractConfigurableEmbeddedServletContainer - implements ServletContextInitializer { - private static final Logger LOGGER = LoggerFactory.getLogger(RestServletInitializer.class); - - @Override - public void onStartup(ServletContext servletContext) throws ServletException { - if (getPort() == 0) { - LOGGER.warn( - "spring boot embed web container listen port is 0, serviceComb will not use container's port to handler RESTful request."); - return; - } - - // web container did not did listen now. - // so mock to listen, and then close. - try (ServerSocket ss = new ServerSocket(getPort(), 0, getAddress())) { - RestServletInjector.defaultInject(servletContext); - ServletUtils.saveUrlPrefix(servletContext); - } catch (IOException e) { - throw new ServletException(e); - } - } -} diff --git a/spring-boot-starter/spring-boot-starter-transport/src/main/java/io/servicecomb/springboot/starter/transport/package-info.java b/spring-boot-starter/spring-boot-starter-transport/src/main/java/io/servicecomb/springboot/starter/transport/package-info.java deleted file mode 100644 index 30c5494bb06..00000000000 --- a/spring-boot-starter/spring-boot-starter-transport/src/main/java/io/servicecomb/springboot/starter/transport/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * - */ -/** - * - * - */ -package io.servicecomb.springboot.starter.transport; diff --git a/spring-boot-starter/spring-cloud-zuul-zipkin/pom.xml b/spring-boot-starter/spring-cloud-zuul-zipkin/pom.xml deleted file mode 100644 index 8b4d5f51fd3..00000000000 --- a/spring-boot-starter/spring-cloud-zuul-zipkin/pom.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - sprint-boot-starter-parent - io.servicecomb - 0.3.0-SNAPSHOT - - 4.0.0 - - spring-cloud-zuul-zipkin - - - - - org.springframework.cloud - spring-cloud-starter-zuul - - - io.servicecomb - handler-tracing-zipkin - - - io.zipkin.brave - brave-instrumentation-servlet - - - \ No newline at end of file diff --git a/spring-boot-starter/spring-cloud-zuul-zipkin/src/main/java/io/servicecomb/spring/cloud/zuul/tracing/SpringTracingConfiguration.java b/spring-boot-starter/spring-cloud-zuul-zipkin/src/main/java/io/servicecomb/spring/cloud/zuul/tracing/SpringTracingConfiguration.java deleted file mode 100644 index 5cb376086e4..00000000000 --- a/spring-boot-starter/spring-cloud-zuul-zipkin/src/main/java/io/servicecomb/spring/cloud/zuul/tracing/SpringTracingConfiguration.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.spring.cloud.zuul.tracing; - -import static javax.servlet.DispatcherType.ASYNC; -import static javax.servlet.DispatcherType.ERROR; -import static javax.servlet.DispatcherType.FORWARD; -import static javax.servlet.DispatcherType.INCLUDE; -import static javax.servlet.DispatcherType.REQUEST; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; - -import com.netflix.zuul.context.RequestContext; - -import brave.http.HttpClientAdapter; -import brave.http.HttpClientHandler; -import brave.http.HttpTracing; -import brave.servlet.TracingFilter; - -@Configuration -public class SpringTracingConfiguration { - - @Bean - FilterRegistrationBean traceWebFilter(HttpTracing httpTracing) { - FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(TracingFilter.create(httpTracing)); - filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, REQUEST); - filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); - return filterRegistrationBean; - } - - @SuppressWarnings("unchecked") - @Bean - HttpClientHandler httpClientHandler(HttpTracing httpTracing) { - return HttpClientHandler.create(httpTracing, new ZuulHttpClientAdapter()); - } - - @Bean - TracePreZuulFilter tracePreZuulFilter( - HttpTracing tracing, - HttpClientHandler handler) { - - return new TracePreZuulFilter(tracing, handler); - } - - @Bean - TracePostZuulFilter tracePostZuulFilter( - HttpTracing tracing, - HttpClientHandler handler) { - - return new TracePostZuulFilter(tracing, handler); - } - - private static class ZuulHttpClientAdapter extends HttpClientAdapter { - - @Nullable - @Override - public String method(@Nonnull RequestContext request) { - return request.getRequest().getMethod(); - } - - @Nullable - @Override - public String url(@Nonnull RequestContext request) { - return request.getRequest().getRequestURI(); - } - - @Nullable - @Override - public String requestHeader(@Nonnull RequestContext request, @Nonnull String name) { - return request.getRequest().getHeader(name); - } - - @Nullable - @Override - public Integer statusCode(@Nonnull HttpServletResponse response) { - return response.getStatus() == 0 ? 500 : response.getStatus(); - } - } -} diff --git a/spring-boot-starter/spring-cloud-zuul-zipkin/src/main/java/io/servicecomb/spring/cloud/zuul/tracing/TracePostZuulFilter.java b/spring-boot-starter/spring-cloud-zuul-zipkin/src/main/java/io/servicecomb/spring/cloud/zuul/tracing/TracePostZuulFilter.java deleted file mode 100644 index e2f933c71bf..00000000000 --- a/spring-boot-starter/spring-cloud-zuul-zipkin/src/main/java/io/servicecomb/spring/cloud/zuul/tracing/TracePostZuulFilter.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.spring.cloud.zuul.tracing; - -import java.lang.invoke.MethodHandles; - -import javax.servlet.http.HttpServletResponse; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.zuul.ZuulFilter; -import com.netflix.zuul.context.RequestContext; - -import brave.Tracer; -import brave.Tracer.SpanInScope; -import brave.http.HttpClientHandler; -import brave.http.HttpTracing; - -class TracePostZuulFilter extends ZuulFilter { - - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - private final Tracer tracer; - - private final HttpClientHandler clientHandler; - - TracePostZuulFilter(HttpTracing tracer, HttpClientHandler clientHandler) { - this.tracer = tracer.tracing().tracer(); - this.clientHandler = clientHandler; - } - - @Override - public boolean shouldFilter() { - return tracer.currentSpan() != null; - } - - @Override - public Object run() { - RequestContext context = RequestContext.getCurrentContext(); - ((SpanInScope) context.getRequest().getAttribute(SpanInScope.class.getName())).close(); - - clientHandler.handleReceive(context.getResponse(), null, tracer.currentSpan()); - log.debug("Closed span {} for {}", tracer.currentSpan(), context.getRequest().getMethod()); - - return null; - } - - @Override - public String filterType() { - return "post"; - } - - @Override - public int filterOrder() { - return 0; - } -} diff --git a/spring-boot-starter/spring-cloud-zuul-zipkin/src/main/java/io/servicecomb/spring/cloud/zuul/tracing/TracePreZuulFilter.java b/spring-boot-starter/spring-cloud-zuul-zipkin/src/main/java/io/servicecomb/spring/cloud/zuul/tracing/TracePreZuulFilter.java deleted file mode 100644 index 59ffdb37f34..00000000000 --- a/spring-boot-starter/spring-cloud-zuul-zipkin/src/main/java/io/servicecomb/spring/cloud/zuul/tracing/TracePreZuulFilter.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.spring.cloud.zuul.tracing; - -import static io.servicecomb.core.Const.CSE_CONTEXT; - -import java.lang.invoke.MethodHandles; - -import javax.servlet.http.HttpServletResponse; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.netflix.zuul.ExecutionStatus; -import com.netflix.zuul.ZuulFilter; -import com.netflix.zuul.ZuulFilterResult; -import com.netflix.zuul.context.RequestContext; - -import brave.Span; -import brave.Tracer; -import brave.Tracer.SpanInScope; -import brave.http.HttpClientHandler; -import brave.http.HttpTracing; -import brave.propagation.TraceContext.Injector; -import io.servicecomb.foundation.common.utils.JsonUtils; - -class TracePreZuulFilter extends ZuulFilter { - - private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - private final Tracer tracer; - - private final HttpClientHandler clientHandler; - - private final Injector injector; - - TracePreZuulFilter( - HttpTracing tracing, - HttpClientHandler clientHandler) { - - this.tracer = tracing.tracing().tracer(); - this.injector = tracing.tracing().propagation().injector( - (requestContext, name, value) -> requestContext.getZuulRequestHeaders().put(name, value)); - this.clientHandler = clientHandler; - } - - @Override - public boolean shouldFilter() { - return true; - } - - @Override - public Object run() { - return null; - } - - @Override - public ZuulFilterResult runFilter() { - RequestContext ctx = RequestContext.getCurrentContext(); - - Span span = clientHandler.handleSend(injector, ctx); - saveHeadersAsInvocationContext(ctx, span); - - SpanInScope scope = tracer.withSpanInScope(span); - log.debug("Generated tracing span {} for {}", span, ctx.getRequest().getMethod()); - - ctx.getRequest().setAttribute(SpanInScope.class.getName(), scope); - - ZuulFilterResult result = super.runFilter(); - log.debug("Result of Zuul filter is [{}]", result.getStatus()); - - if (ExecutionStatus.SUCCESS != result.getStatus()) { - log.debug("The result of Zuul filter execution was not successful thus will close the current span {}", span); - clientHandler.handleReceive(ctx.getResponse(), result.getException(), span); - scope.close(); - } - return result; - } - - private void saveHeadersAsInvocationContext(RequestContext ctx, Span span) { - try { - ctx.addZuulRequestHeader(CSE_CONTEXT, JsonUtils.writeValueAsString(ctx.getZuulRequestHeaders())); - } catch (JsonProcessingException e) { - clientHandler.handleReceive(ctx.getResponse(), e, span); - throw new IllegalStateException("Unable to write request headers as json to " + CSE_CONTEXT, e); - } - } - - - @Override - public String filterType() { - return "pre"; - } - - @Override - public int filterOrder() { - return Integer.MAX_VALUE; - } -} diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml new file mode 100644 index 00000000000..b96de480b73 --- /dev/null +++ b/spring-boot/pom.xml @@ -0,0 +1,38 @@ + + + + + + org.apache.servicecomb + java-chassis-parent + 3.4.0-SNAPSHOT + ../parents/default + + + 4.0.0 + pom + java-chassis-spring-boot + Java Chassis::Spring Boot + + + spring-boot-starters + + + diff --git a/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/pom.xml b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/pom.xml new file mode 100644 index 00000000000..c3fa579baee --- /dev/null +++ b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/pom.xml @@ -0,0 +1,69 @@ + + + + + + java-chassis-spring-boot-starters + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + java-chassis-spring-boot-starter-servlet + Java Chassis::Spring Boot Starter::Servlet + + + + org.springframework.boot + spring-boot-starter-web + + + org.apache.servicecomb + transport-rest-servlet + + + org.apache.servicecomb + provider-springmvc + + + org.apache.servicecomb + provider-jaxrs + + + org.apache.servicecomb + provider-pojo + + + org.apache.servicecomb + handler-loadbalance + + + jakarta.servlet + jakarta.servlet-api + compile + + + org.apache.servicecomb + foundation-test-scaffolding + test + + + + diff --git a/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/java/org/apache/servicecomb/springboot/starter/servlet/RestServletInitializer.java b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/java/org/apache/servicecomb/springboot/starter/servlet/RestServletInitializer.java new file mode 100644 index 00000000000..ee247758fd4 --- /dev/null +++ b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/java/org/apache/servicecomb/springboot/starter/servlet/RestServletInitializer.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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.starter.servlet; + +import java.io.IOException; +import java.net.ServerSocket; + +import org.apache.servicecomb.transport.rest.servlet.ServletUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.server.AbstractConfigurableWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.core.env.Environment; + +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; + +public class RestServletInitializer + implements WebServerFactoryCustomizer, ServletContextInitializer { + private static final Logger LOGGER = LoggerFactory.getLogger(RestServletInitializer.class); + + private AbstractConfigurableWebServerFactory factory = null; + + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public void customize(AbstractConfigurableWebServerFactory factory) { + this.factory = factory; + } + + @Override + @SuppressWarnings("try") + public void onStartup(ServletContext servletContext) throws ServletException { + if (this.factory == null) { + // when running in external tomcat, WebServerFactoryCustomizer will not be available, but now tomcat + // is already listening and we can call ServletUtils.init directly. + ServletUtils.init(servletContext, environment); + return; + } + + if (factory.getPort() == 0) { + LOGGER.warn( + "spring boot embedded web container listen port is 0, ServiceComb will not use container's port to handler REST request."); + return; + } + + // when running in embedded tomcat, web container did not listen now. Call ServletUtils.init needs server is ready, + // so mock to listen, and then close. + try (ServerSocket ignored = new ServerSocket(factory.getPort(), 0, factory.getAddress())) { + ServletUtils.init(servletContext, environment); + } catch (IOException e) { + throw new ServletException(e); + } + } +} diff --git a/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/java/org/apache/servicecomb/springboot/starter/servlet/SpringBootStarterServletConfiguration.java b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/java/org/apache/servicecomb/springboot/starter/servlet/SpringBootStarterServletConfiguration.java new file mode 100644 index 00000000000..5d17aefb60b --- /dev/null +++ b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/java/org/apache/servicecomb/springboot/starter/servlet/SpringBootStarterServletConfiguration.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.springboot.starter.servlet; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SpringBootStarterServletConfiguration { + @Bean + public RestServletInitializer restServletInitializer() { + return new RestServletInitializer(); + } +} diff --git a/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/java/org/apache/servicecomb/springboot/starter/servlet/package-info.java b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/java/org/apache/servicecomb/springboot/starter/servlet/package-info.java new file mode 100644 index 00000000000..fbf06e33413 --- /dev/null +++ b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/java/org/apache/servicecomb/springboot/starter/servlet/package-info.java @@ -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. + */ + +package org.apache.servicecomb.springboot.starter.servlet; diff --git a/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..217c48ac511 --- /dev/null +++ b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/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.springboot.starter.servlet.SpringBootStarterServletConfiguration diff --git a/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-standalone/pom.xml b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-standalone/pom.xml new file mode 100644 index 00000000000..cd8d007109f --- /dev/null +++ b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-standalone/pom.xml @@ -0,0 +1,66 @@ + + + + + + java-chassis-spring-boot-starters + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + java-chassis-spring-boot-starter-standalone + Java Chassis::Spring Boot Starter::Standalone + + + + org.apache.servicecomb + transport-rest-vertx + + + org.apache.servicecomb + provider-springmvc + + + org.apache.servicecomb + provider-jaxrs + + + org.apache.servicecomb + provider-pojo + + + org.apache.servicecomb + handler-loadbalance + + + + org.hibernate.validator + hibernate-validator + + + + org.apache.servicecomb + foundation-test-scaffolding + test + + + + diff --git a/spring-boot/spring-boot-starters/pom.xml b/spring-boot/spring-boot-starters/pom.xml new file mode 100644 index 00000000000..8c3eecd55ed --- /dev/null +++ b/spring-boot/spring-boot-starters/pom.xml @@ -0,0 +1,39 @@ + + + + + + java-chassis-spring-boot + org.apache.servicecomb + 3.4.0-SNAPSHOT + + + 4.0.0 + + java-chassis-spring-boot-starters + Java Chassis::Spring Boot Starter::Parent + pom + + + java-chassis-spring-boot-starter-servlet + java-chassis-spring-boot-starter-standalone + + + diff --git a/swagger/pom.xml b/swagger/pom.xml index 9746b760e50..5009d6dbc8f 100644 --- a/swagger/pom.xml +++ b/swagger/pom.xml @@ -1,11 +1,12 @@ - + 4.0.0 - io.servicecomb + org.apache.servicecomb java-chassis-parent - 0.3.0-SNAPSHOT - ../parent + 3.4.0-SNAPSHOT + ../parents/default swagger + Java Chassis::Swagger pom - swagger-generator - swagger-invocation + swagger-generator + swagger-invocation diff --git a/swagger/swagger-generator/generator-core/pom.xml b/swagger/swagger-generator/generator-core/pom.xml index 82029698145..768f6ecd054 100644 --- a/swagger/swagger-generator/generator-core/pom.xml +++ b/swagger/swagger-generator/generator-core/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - swagger-generator - 0.3.0-SNAPSHOT - - swagger-generator-core + 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 + swagger-generator + 3.4.0-SNAPSHOT + + swagger-generator-core + Java Chassis::Swagger::Generator::Core + + + + jakarta.validation + jakarta.validation-api + + + io.swagger.core.v3 + swagger-core-jakarta + + + org.apache.servicecomb + foundation-config + + + jakarta.servlet + jakarta.servlet-api + + + org.reactivestreams + reactive-streams + - - - io.swagger - swagger-core - - - io.servicecomb - foundation-common - - - io.servicecomb - common-javassist - - - javax.servlet - javax.servlet-api - + + org.mockito + mockito-inline + test + + + org.junit.jupiter + junit-jupiter-api + provided + - - junit - junit - provided - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.servicecomb + foundation-test-scaffolding + + + org.springframework + spring-web + test + + diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/AbstractConverter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/AbstractConverter.java deleted file mode 100644 index 3154ee69494..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/AbstractConverter.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter; - -import java.util.Map; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.swagger.generator.core.SwaggerConst; -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.swagger.models.Swagger; - -public abstract class AbstractConverter implements Converter { - - protected abstract Map findVendorExtensions(Object def); - - protected abstract JavaType doConvert(ClassLoader classLoader, String packageName, Swagger swagger, Object def); - - @Override - public JavaType convert(ClassLoader classLoader, String packageName, Swagger swagger, Object def) { - Map vendorExtensions = findVendorExtensions(def); - JavaType javaType = getJavaTypeByVendorExtensions(classLoader, vendorExtensions); - if (javaType != null) { - return javaType; - } - - return doConvert(classLoader, packageName, swagger, def); - } - - private JavaType getJavaTypeByVendorExtensions(ClassLoader classLoader, - Map vendorExtensions) { - Class cls = - ClassUtils.getClassByVendorExtensions(classLoader, vendorExtensions, SwaggerConst.EXT_JAVA_CLASS); - if (cls == null) { - return null; - } - - return TypeFactory.defaultInstance().constructType(cls); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/Converter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/Converter.java deleted file mode 100644 index eff05488a80..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/Converter.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter; - -import com.fasterxml.jackson.databind.JavaType; - -import io.swagger.models.Swagger; - -public interface Converter { - // def可能是property或model - // def不可能为null - JavaType convert(ClassLoader classLoader, String packageName, Swagger swagger, Object def); -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/ConverterMgr.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/ConverterMgr.java deleted file mode 100644 index ee85511d1ca..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/ConverterMgr.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.SimpleType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.swagger.converter.model.ArrayModelConverter; -import io.servicecomb.swagger.converter.model.ModelImplConverter; -import io.servicecomb.swagger.converter.model.RefModelConverter; -import io.servicecomb.swagger.converter.parameter.AbstractSerializableParameterConverter; -import io.servicecomb.swagger.converter.parameter.BodyParameterConverter; -import io.servicecomb.swagger.converter.property.ArrayPropertyConverter; -import io.servicecomb.swagger.converter.property.MapPropertyConverter; -import io.servicecomb.swagger.converter.property.RefPropertyConverter; -import io.servicecomb.swagger.converter.property.StringPropertyConverter; -import io.servicecomb.swagger.extend.property.ByteProperty; -import io.servicecomb.swagger.extend.property.ShortProperty; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.swagger.models.ArrayModel; -import io.swagger.models.Model; -import io.swagger.models.ModelImpl; -import io.swagger.models.RefModel; -import io.swagger.models.Swagger; -import io.swagger.models.parameters.BodyParameter; -import io.swagger.models.parameters.CookieParameter; -import io.swagger.models.parameters.FormParameter; -import io.swagger.models.parameters.HeaderParameter; -import io.swagger.models.parameters.PathParameter; -import io.swagger.models.parameters.QueryParameter; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.BooleanProperty; -import io.swagger.models.properties.ByteArrayProperty; -import io.swagger.models.properties.DateProperty; -import io.swagger.models.properties.DateTimeProperty; -import io.swagger.models.properties.DecimalProperty; -import io.swagger.models.properties.DoubleProperty; -import io.swagger.models.properties.FloatProperty; -import io.swagger.models.properties.IntegerProperty; -import io.swagger.models.properties.LongProperty; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.Property; -import io.swagger.models.properties.RefProperty; -import io.swagger.models.properties.StringProperty; - -public final class ConverterMgr { - private static final JavaType VOID_JAVA_TYPE = TypeFactory.defaultInstance().constructType(void.class); - - private static final Map, JavaType> PROPERTY_MAP = new HashMap<>(); - - // key为swagger中标准数据类型的type.format - // value为对应的java class - private static final Map TYPE_FORMAT_MAP = new HashMap<>(); - - private static Map, Converter> converterMap = new HashMap<>(); - - static { - initPropertyMap(); - initTypeFormatMap(); - initConverters(); - } - - public static String genTypeFormatKey(String type, String format) { - return type + ":" + String.valueOf(format); - } - - private ConverterMgr() { - - } - - private static void initTypeFormatMap() { - try { - for (Entry, JavaType> entry : PROPERTY_MAP.entrySet()) { - Property property = entry.getKey().newInstance(); - String key = genTypeFormatKey(property.getType(), property.getFormat()); - TYPE_FORMAT_MAP.put(key, entry.getValue()); - } - } catch (Throwable e) { - throw new Error(e); - } - } - - private static void initPropertyMap() { - PROPERTY_MAP.put(BooleanProperty.class, SimpleType.constructUnsafe(Boolean.class)); - - PROPERTY_MAP.put(FloatProperty.class, SimpleType.constructUnsafe(Float.class)); - PROPERTY_MAP.put(DoubleProperty.class, SimpleType.constructUnsafe(Double.class)); - PROPERTY_MAP.put(DecimalProperty.class, SimpleType.constructUnsafe(BigDecimal.class)); - - PROPERTY_MAP.put(ByteProperty.class, SimpleType.constructUnsafe(Byte.class)); - PROPERTY_MAP.put(ShortProperty.class, SimpleType.constructUnsafe(Short.class)); - PROPERTY_MAP.put(IntegerProperty.class, SimpleType.constructUnsafe(Integer.class)); - PROPERTY_MAP.put(LongProperty.class, SimpleType.constructUnsafe(Long.class)); - - // stringProperty包含了enum的场景,并不一定是转化为string - // 但是,如果统一走StringPropertyConverter则可以处理enum的场景 - PROPERTY_MAP.put(StringProperty.class, SimpleType.constructUnsafe(String.class)); - - PROPERTY_MAP.put(DateProperty.class, SimpleType.constructUnsafe(LocalDate.class)); - PROPERTY_MAP.put(DateTimeProperty.class, SimpleType.constructUnsafe(Date.class)); - - PROPERTY_MAP.put(ByteArrayProperty.class, SimpleType.constructUnsafe(byte[].class)); - } - - private static void initConverters() { - // inner converters - for (Class propertyCls : PROPERTY_MAP.keySet()) { - addInnerConverter(propertyCls); - } - - converterMap.put(RefProperty.class, new RefPropertyConverter()); - converterMap.put(ArrayProperty.class, new ArrayPropertyConverter()); - converterMap.put(MapProperty.class, new MapPropertyConverter()); - converterMap.put(StringProperty.class, new StringPropertyConverter()); - - converterMap.put(ModelImpl.class, new ModelImplConverter()); - converterMap.put(RefModel.class, new RefModelConverter()); - converterMap.put(ArrayModel.class, new ArrayModelConverter()); - - converterMap.put(BodyParameter.class, new BodyParameterConverter()); - - AbstractSerializableParameterConverter converter = new AbstractSerializableParameterConverter(); - converterMap.put(QueryParameter.class, converter); - converterMap.put(PathParameter.class, converter); - converterMap.put(HeaderParameter.class, converter); - converterMap.put(FormParameter.class, converter); - converterMap.put(CookieParameter.class, converter); - } - - private static void addInnerConverter(Class propertyCls) { - JavaType javaType = PROPERTY_MAP.get(propertyCls); - if (javaType == null) { - throw new Error("not support inner property class: " + propertyCls.getName()); - } - - converterMap.put(propertyCls, (classLoader, packageName, swagger, def) -> javaType); - } - - public static void addConverter(Class cls, Converter converter) { - converterMap.put(cls, converter); - } - - public static JavaType findJavaType(String type, String format) { - String key = genTypeFormatKey(type, format); - return TYPE_FORMAT_MAP.get(key); - } - - public static JavaType findJavaType(SwaggerGenerator generator, Object def) { - return findJavaType(generator.getClassLoader(), - generator.ensureGetPackageName(), - generator.getSwagger(), - def); - } - - // def为null是void的场景 - // def可能是model、property、parameter - public static JavaType findJavaType(ClassLoader classLoader, String packageName, Swagger swagger, Object def) { - if (def == null) { - return VOID_JAVA_TYPE; - } - Converter converter = converterMap.get(def.getClass()); - if (converter == null) { - throw new Error("not support def type: " + def.getClass()); - } - - return converter.convert(classLoader, packageName, swagger, def); - } - - public static JavaType findByRef(ClassLoader classLoader, String packageName, Swagger swagger, String refName) { - Model ref = swagger.getDefinitions().get(refName); - if (ModelImpl.class.isInstance(ref) && ((ModelImpl) ref).getName() == null) { - ((ModelImpl) ref).setName(refName); - } - return findJavaType(classLoader, packageName, swagger, ref); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/model/AbstractModelConverter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/model/AbstractModelConverter.java deleted file mode 100644 index 5a6bf2bf430..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/model/AbstractModelConverter.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter.model; - -import java.util.Map; - -import io.servicecomb.swagger.converter.AbstractConverter; -import io.swagger.models.Model; - -public abstract class AbstractModelConverter extends AbstractConverter { - protected Map findVendorExtensions(Object def) { - return ((Model) def).getVendorExtensions(); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/model/ArrayModelConverter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/model/ArrayModelConverter.java deleted file mode 100644 index da0a1c4d0e5..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/model/ArrayModelConverter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter.model; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.swagger.converter.property.ArrayPropertyConverter; -import io.swagger.models.ArrayModel; -import io.swagger.models.Swagger; - -public class ArrayModelConverter extends AbstractModelConverter { - @Override - public JavaType doConvert(ClassLoader classLoader, String packageName, Swagger swagger, Object model) { - ArrayModel arrayModel = (ArrayModel) model; - - if (arrayModel.getItems() != null) { - return ArrayPropertyConverter.findJavaType(classLoader, - packageName, - swagger, - arrayModel.getItems(), - false); - } - - // don't know when will this happen. - throw new Error("not support null array model items."); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/model/ModelImplConverter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/model/ModelImplConverter.java deleted file mode 100644 index a90e920198f..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/model/ModelImplConverter.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter.model; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.swagger.converter.ConverterMgr; -import io.servicecomb.swagger.converter.property.MapPropertyConverter; -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.swagger.models.ModelImpl; -import io.swagger.models.Swagger; - -public class ModelImplConverter extends AbstractModelConverter { - @Override - public JavaType doConvert(ClassLoader classLoader, String packageName, Swagger swagger, Object model) { - ModelImpl modelImpl = (ModelImpl) model; - - JavaType javaType = ConverterMgr.findJavaType(modelImpl.getType(), modelImpl.getFormat()); - if (javaType != null) { - return javaType; - } - - if (modelImpl.getReference() != null) { - return ConverterMgr.findByRef(classLoader, packageName, swagger, modelImpl.getReference()); - } - - if (modelImpl.getAdditionalProperties() != null) { - return MapPropertyConverter.findJavaType(classLoader, - packageName, - swagger, - modelImpl.getAdditionalProperties()); - } - - // 根据name、property动态生成class - if (packageName == null) { - throw new Error("packageName should not be null"); - } - String clsName = packageName + "." + modelImpl.getName(); - Class cls = - ClassUtils.getOrCreateClass(classLoader, packageName, swagger, modelImpl.getProperties(), clsName); - return TypeFactory.defaultInstance().constructType(cls); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/model/RefModelConverter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/model/RefModelConverter.java deleted file mode 100644 index e3c5c5bab43..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/model/RefModelConverter.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter.model; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.swagger.converter.ConverterMgr; -import io.swagger.models.RefModel; -import io.swagger.models.Swagger; - -public class RefModelConverter extends AbstractModelConverter { - @Override - public JavaType doConvert(ClassLoader classLoader, String packageName, Swagger swagger, Object model) { - RefModel refModel = (RefModel) model; - - return ConverterMgr.findByRef(classLoader, packageName, swagger, refModel.getSimpleRef()); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/parameter/AbstractSerializableParameterConverter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/parameter/AbstractSerializableParameterConverter.java deleted file mode 100644 index 26910f43258..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/parameter/AbstractSerializableParameterConverter.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter.parameter; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.swagger.converter.Converter; -import io.servicecomb.swagger.converter.ConverterMgr; -import io.servicecomb.swagger.converter.property.ArrayPropertyConverter; -import io.servicecomb.swagger.converter.property.StringPropertyConverter; -import io.swagger.models.Swagger; -import io.swagger.models.parameters.AbstractSerializableParameter; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.StringProperty; - -public class AbstractSerializableParameterConverter implements Converter { - - @Override - public JavaType convert(ClassLoader classLoader, String packageName, Swagger swagger, Object def) { - AbstractSerializableParameter param = (AbstractSerializableParameter) def; - - switch (param.getType()) { - case ArrayProperty.TYPE: - return ArrayPropertyConverter.findJavaType(classLoader, - packageName, - swagger, - param.getItems(), - param.isUniqueItems()); - case StringProperty.TYPE: - return StringPropertyConverter.findJavaType(classLoader, - packageName, - swagger, - param.getType(), - param.getFormat(), - param.getEnum()); - default: - return ConverterMgr.findJavaType(param.getType(), param.getFormat()); - } - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/parameter/BodyParameterConverter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/parameter/BodyParameterConverter.java deleted file mode 100644 index 67387d12623..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/parameter/BodyParameterConverter.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter.parameter; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.swagger.converter.Converter; -import io.servicecomb.swagger.converter.ConverterMgr; -import io.swagger.models.Swagger; -import io.swagger.models.parameters.BodyParameter; - -public class BodyParameterConverter implements Converter { - - @Override - public JavaType convert(ClassLoader classLoader, String packageName, Swagger swagger, Object def) { - BodyParameter param = (BodyParameter) def; - return ConverterMgr.findJavaType(classLoader, packageName, swagger, param.getSchema()); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/AbstractPropertyConverter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/AbstractPropertyConverter.java deleted file mode 100644 index db811a30d59..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/AbstractPropertyConverter.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter.property; - -import java.util.Map; - -import io.servicecomb.swagger.converter.AbstractConverter; -import io.swagger.models.properties.Property; - -public abstract class AbstractPropertyConverter extends AbstractConverter { - protected Map findVendorExtensions(Object def) { - return ((Property) def).getVendorExtensions(); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/ArrayPropertyConverter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/ArrayPropertyConverter.java deleted file mode 100644 index 121a3f962c2..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/ArrayPropertyConverter.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter.property; - -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.swagger.converter.ConverterMgr; -import io.swagger.models.Swagger; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.Property; - -public class ArrayPropertyConverter extends AbstractPropertyConverter { - public static JavaType findJavaType(ClassLoader classLoader, String packageName, Swagger swagger, - Property itemProperty, - Boolean uniqueItems) { - JavaType itemJavaType = ConverterMgr.findJavaType(classLoader, packageName, swagger, itemProperty); - - @SuppressWarnings("rawtypes") - Class collectionClass = List.class; - if (Boolean.TRUE.equals(uniqueItems)) { - collectionClass = Set.class; - } - return TypeFactory.defaultInstance().constructCollectionType(collectionClass, itemJavaType); - } - - @Override - public JavaType doConvert(ClassLoader classLoader, String packageName, Swagger swagger, Object property) { - ArrayProperty arrayProperty = (ArrayProperty) property; - - return findJavaType(classLoader, - packageName, - swagger, - arrayProperty.getItems(), - arrayProperty.getUniqueItems()); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/MapPropertyConverter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/MapPropertyConverter.java deleted file mode 100644 index 9f3b2b0ee7b..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/MapPropertyConverter.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter.property; - -import java.util.Map; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.swagger.converter.ConverterMgr; -import io.swagger.models.Swagger; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.Property; - -public class MapPropertyConverter extends AbstractPropertyConverter { - @Override - public JavaType doConvert(ClassLoader classLoader, String packageName, Swagger swagger, Object property) { - MapProperty mapProperty = (MapProperty) property; - Property valueProperty = mapProperty.getAdditionalProperties(); - return findJavaType(classLoader, packageName, swagger, valueProperty); - } - - public static JavaType findJavaType(ClassLoader classLoader, String packageName, Swagger swagger, - Property valueProperty) { - JavaType valueJavaType = ConverterMgr.findJavaType(classLoader, packageName, swagger, valueProperty); - - return TypeFactory.defaultInstance().constructMapType(Map.class, - TypeFactory.defaultInstance().constructType(String.class), - valueJavaType); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/RefPropertyConverter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/RefPropertyConverter.java deleted file mode 100644 index 5bc0d9c65ca..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/RefPropertyConverter.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter.property; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.swagger.converter.ConverterMgr; -import io.swagger.models.Swagger; -import io.swagger.models.properties.RefProperty; - -public class RefPropertyConverter extends AbstractPropertyConverter { - @Override - public JavaType doConvert(ClassLoader classLoader, String packageName, Swagger swagger, Object property) { - RefProperty refProperty = (RefProperty) property; - - return ConverterMgr.findByRef(classLoader, packageName, swagger, refProperty.getSimpleRef()); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/StringPropertyConverter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/StringPropertyConverter.java deleted file mode 100644 index ebf6eb17319..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/converter/property/StringPropertyConverter.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.converter.property; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.common.javassist.JavassistUtils; -import io.servicecomb.swagger.converter.ConverterMgr; -import io.swagger.models.Swagger; -import io.swagger.models.properties.StringProperty; - -public class StringPropertyConverter extends AbstractPropertyConverter { - // 用于生成唯一的enum名称 - // key为enum names, value为enum cls javaType - private static Map enumMap = new HashMap<>(); - - private static final Object LOCK = new Object(); - - // 转换并创建enum是小概率事件,没必要double check - private static JavaType getOrCreateEnumByNames(String packageName, List enums) { - String strEnums = enums.toString(); - - synchronized (LOCK) { - JavaType javaType = enumMap.get(strEnums); - if (javaType != null) { - return javaType; - } - - String enumClsName = packageName + ".Enum" + enumMap.size(); - @SuppressWarnings("rawtypes") - Class enumCls = JavassistUtils.createEnum(enumClsName, enums); - javaType = TypeFactory.defaultInstance().constructType(enumCls); - enumMap.put(strEnums, javaType); - - return javaType; - } - } - - public static JavaType findJavaType(ClassLoader classLoader, String packageName, Swagger swagger, String type, - String format, List enums) { - if (!isEnum(enums)) { - return ConverterMgr.findJavaType(type, format); - } - - // enum,且需要动态生成class - return getOrCreateEnumByNames(packageName, enums); - } - - public static boolean isEnum(StringProperty stringProperty) { - return isEnum(stringProperty.getEnum()); - } - - public static boolean isEnum(List enums) { - return enums != null && !enums.isEmpty(); - } - - @Override - public JavaType doConvert(ClassLoader classLoader, String packageName, Swagger swagger, Object property) { - StringProperty stringProperty = (StringProperty) property; - - List enums = stringProperty.getEnum(); - return findJavaType(classLoader, - packageName, - swagger, - stringProperty.getType(), - stringProperty.getFormat(), - enums); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/ExtendConst.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/ExtendConst.java deleted file mode 100644 index bb8149a1972..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/ExtendConst.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.extend; - -public final class ExtendConst { - private ExtendConst() { - - } - - public static final String EXT_JAVA_INTF = "x-java-interface"; - - public static final String EXT_JAVA_CLASS = "x-java-class"; -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/ModelResolverExt.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/ModelResolverExt.java deleted file mode 100644 index 89e34815081..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/ModelResolverExt.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.extend; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Modifier; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import org.springframework.util.StringUtils; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.foundation.common.exceptions.ServiceCombException; -import io.servicecomb.swagger.converter.property.StringPropertyConverter; -import io.servicecomb.swagger.extend.property.ByteProperty; -import io.servicecomb.swagger.extend.property.ShortProperty; -import io.swagger.converter.ModelConverter; -import io.swagger.converter.ModelConverterContext; -import io.swagger.jackson.ModelResolver; -import io.swagger.models.Model; -import io.swagger.models.ModelImpl; -import io.swagger.models.properties.ByteArrayProperty; -import io.swagger.models.properties.Property; -import io.swagger.models.properties.StringProperty; -import io.swagger.util.Json; - -public class ModelResolverExt extends ModelResolver { - private interface PropertyCreator { - Property createProperty(); - } - - private Map, PropertyCreator> creatorMap = new HashMap<>(); - - public ModelResolverExt() { - super(Json.mapper()); - - addCreator(() -> { - return new ByteProperty(); - }, Byte.class, byte.class); - - addCreator(() -> { - return new ShortProperty(); - }, Short.class, short.class); - - addCreator(() -> { - return new ByteArrayProperty(); - }, Byte[].class, byte[].class); - } - - private void addCreator(PropertyCreator creator, Class... clsArr) { - for (Class cls : clsArr) { - creatorMap.put(cls, creator); - } - } - - private void setType(JavaType type, Map vendorExtensions) { - vendorExtensions.put(ExtendConst.EXT_JAVA_CLASS, type.getRawClass().getName()); - } - - private void checkType(JavaType type) { - // 原子类型/string在java中是abstract的 - if (type.getRawClass().isPrimitive() - || byte[].class.equals(type.getRawClass()) - || Byte[].class.equals(type.getRawClass()) - || String.class.equals(type.getRawClass())) { - return; - } - - String msg = "Must be a concrete type."; - if (type.getRawClass().equals(Object.class)) { - throw new ServiceCombException(type.getRawClass().getName() + " not support. " + msg); - } - - if (type.isMapLikeType()) { - if (!String.class.equals(type.getKeyType().getRawClass())) { - // swagger中map的key只允许为string - throw new Error("Key of map must be string."); - } - } - - if (type.isContainerType()) { - checkType(type.getContentType()); - return; - } - - if (type.getRawClass().isInterface()) { - throw new ServiceCombException(type.getTypeName() + " is interface. " + msg); - } - - if (Modifier.isAbstract(type.getRawClass().getModifiers())) { - throw new ServiceCombException(type.getTypeName() + " is abstract class. " + msg); - } - } - - @Override - public Model resolve(JavaType type, ModelConverterContext context, Iterator next) { - Model model = super.resolve(type, context, next); - if (model == null) { - return null; - } - - checkType(type); - - // 只有声明model的地方才需要标注类型 - if (ModelImpl.class.isInstance(model) && !StringUtils.isEmpty(((ModelImpl) model).getName())) { - setType(type, model.getVendorExtensions()); - } - return model; - } - - @Override - public Property resolveProperty(JavaType propType, ModelConverterContext context, Annotation[] annotations, - Iterator next) { - checkType(propType); - - PropertyCreator creator = creatorMap.get(propType.getRawClass()); - if (creator != null) { - return creator.createProperty(); - } - - Property property = super.resolveProperty(propType, context, annotations, next); - if (StringProperty.class.isInstance(property)) { - if (StringPropertyConverter.isEnum((StringProperty) property)) { - setType(propType, property.getVendorExtensions()); - } - } - return property; - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/annotations/ResponseHeaders.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/annotations/ResponseHeaders.java deleted file mode 100644 index 664ab998390..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/annotations/ResponseHeaders.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.extend.annotations; - -import static java.lang.annotation.ElementType.METHOD; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import io.swagger.annotations.ResponseHeader; - -@Target({METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface ResponseHeaders { - ResponseHeader[] value(); -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/parameter/ContextParameter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/parameter/ContextParameter.java deleted file mode 100644 index 1a5b33aa74f..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/parameter/ContextParameter.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.extend.parameter; - -import io.swagger.models.parameters.Parameter; - -/** - * http request这种不在swagger中体现出来的参数的包装 - */ -public interface ContextParameter extends Parameter { -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/parameter/HttpRequestParameter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/parameter/HttpRequestParameter.java deleted file mode 100644 index 5674dea0395..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/parameter/HttpRequestParameter.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.extend.parameter; - -import io.swagger.models.parameters.AbstractParameter; - -public class HttpRequestParameter extends AbstractParameter implements ContextParameter { -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/property/AbstractBaseIntegerProperty.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/property/AbstractBaseIntegerProperty.java deleted file mode 100644 index d0d9cab86ae..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/property/AbstractBaseIntegerProperty.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.extend.property; - -import java.util.ArrayList; -import java.util.List; - -import io.swagger.models.Xml; -import io.swagger.models.properties.BaseIntegerProperty; - -/** - * copy from io.swagger.models.properties.IntegerProperty - * to avoid so many repeated code. - */ -public abstract class AbstractBaseIntegerProperty extends BaseIntegerProperty { - protected T defaultValue; - - protected List enumNames; - - public AbstractBaseIntegerProperty(String format) { - super(format); - } - - public AbstractBaseIntegerProperty addEnum(T value) { - if (this.enumNames == null) { - this.enumNames = new ArrayList(); - } - if (!enumNames.contains(value)) { - enumNames.add(value); - } - return this; - } - - public AbstractBaseIntegerProperty replaceEnum(List value) { - this.enumNames = value; - return this; - } - - public AbstractBaseIntegerProperty xml(Xml xml) { - this.setXml(xml); - return this; - } - - public AbstractBaseIntegerProperty readOnly() { - this.setReadOnly(Boolean.TRUE); - return this; - } - - protected abstract T parseNumber(String strValue); - - public AbstractBaseIntegerProperty assignDefault(String defaultValue) { - if (defaultValue != null) { - try { - this.defaultValue = parseNumber(defaultValue); - } catch (NumberFormatException e) { - // continue; - } - } - return this; - } - - public AbstractBaseIntegerProperty assignDefault(T defaultValue) { - this.setDefault(defaultValue); - return this; - } - - public AbstractBaseIntegerProperty vendorExtension(String key, Object obj) { - this.setVendorExtension(key, obj); - return this; - } - - public T getDefault() { - return defaultValue; - } - - public void setDefault(String defaultValue) { - this.assignDefault(defaultValue); - } - - public void setDefault(T defaultValue) { - this.defaultValue = defaultValue; - } - - public List getEnum() { - return enumNames; - } - - public void setEnum(List enums) { - this.enumNames = enums; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((defaultValue == null) ? 0 : defaultValue.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (!super.equals(obj)) { - return false; - } - if (!(obj instanceof AbstractBaseIntegerProperty)) { - return false; - } - @SuppressWarnings("unchecked") - AbstractBaseIntegerProperty other = (AbstractBaseIntegerProperty) obj; - if (defaultValue == null) { - if (other.defaultValue != null) { - return false; - } - } else if (!defaultValue.equals(other.defaultValue)) { - return false; - } - return true; - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/property/ByteProperty.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/property/ByteProperty.java deleted file mode 100644 index 21630b561af..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/property/ByteProperty.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.extend.property; - -public class ByteProperty extends AbstractBaseIntegerProperty { - public static final String FORMAT = "int8"; - - public ByteProperty() { - super(FORMAT); - } - - public static boolean isType(String type, String format) { - return TYPE.equals(type) && FORMAT.equals(format); - } - - @Override - protected Byte parseNumber(String strValue) { - return Byte.parseByte(strValue); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/property/ShortProperty.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/property/ShortProperty.java deleted file mode 100644 index 5a5f9fe73c7..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/extend/property/ShortProperty.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.extend.property; - -public class ShortProperty extends AbstractBaseIntegerProperty { - public static final String FORMAT = "int16"; - - public ShortProperty() { - super(FORMAT); - } - - public static boolean isType(String type, String format) { - return TYPE.equals(type) && FORMAT.equals(format); - } - - @Override - protected Short parseNumber(String strValue) { - return Short.parseShort(strValue); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/AbstractSwaggerGeneratorContext.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/AbstractSwaggerGeneratorContext.java deleted file mode 100644 index 381af6f9477..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/AbstractSwaggerGeneratorContext.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; - -import io.servicecomb.foundation.common.RegisterManager; -import io.servicecomb.foundation.common.utils.SPIServiceUtils; -import io.servicecomb.swagger.extend.annotations.ResponseHeaders; -import io.servicecomb.swagger.generator.core.AnnotationProcessorManager.AnnotationType; -import io.servicecomb.swagger.generator.core.processor.annotation.ApiImplicitParamClassProcessor; -import io.servicecomb.swagger.generator.core.processor.annotation.ApiImplicitParamMethodProcessor; -import io.servicecomb.swagger.generator.core.processor.annotation.ApiImplicitParamsClassProcessor; -import io.servicecomb.swagger.generator.core.processor.annotation.ApiImplicitParamsMethodProcessor; -import io.servicecomb.swagger.generator.core.processor.annotation.ApiOperationProcessor; -import io.servicecomb.swagger.generator.core.processor.annotation.ApiResponseClassProcessor; -import io.servicecomb.swagger.generator.core.processor.annotation.ApiResponseMethodProcessor; -import io.servicecomb.swagger.generator.core.processor.annotation.ApiResponsesClassProcessor; -import io.servicecomb.swagger.generator.core.processor.annotation.ApiResponsesMethodProcessor; -import io.servicecomb.swagger.generator.core.processor.annotation.ResponseHeaderProcessor; -import io.servicecomb.swagger.generator.core.processor.annotation.ResponseHeadersProcessor; -import io.servicecomb.swagger.generator.core.processor.annotation.SwaggerDefinitionProcessor; -import io.servicecomb.swagger.generator.core.processor.response.DefaultResponseTypeProcessor; -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.annotations.SwaggerDefinition; - -/** - * 根据class反向生成swagger的上下文对象 - */ -public abstract class AbstractSwaggerGeneratorContext implements SwaggerGeneratorContext { - protected AnnotationProcessorManager classAnnotationMgr = - new AnnotationProcessorManager<>(AnnotationType.CLASS); - - protected AnnotationProcessorManager methodAnnotationMgr = - new AnnotationProcessorManager<>(AnnotationType.METHOD); - - protected AnnotationProcessorManager parameterAnnotationMgr = - new AnnotationProcessorManager<>(AnnotationType.PARAMETER); - - // 管理方法入参的processor,用于支撑httpRequest、Context之类的特殊处理 - // key为class - protected RegisterManager parameterTypeProcessorMgr = - new RegisterManager<>("parameter type processor mgr"); - - protected DefaultParameterProcessor defaultParameterProcessor; - - protected RegisterManager responseTypeProcessorMgr = - new RegisterManager<>("response type processor mgr"); - - protected ResponseTypeProcessor defaultResponseTypeProcessor = new DefaultResponseTypeProcessor(); - - public AbstractSwaggerGeneratorContext() { - initClassAnnotationMgr(); - initMethodAnnotationMgr(); - initParameterAnnotationMgr(); - - initParameterTypeProcessorMgr(); - - initDefaultParameterProcessor(); - - initResponseTypeProcessorMgr(); - } - - protected void initClassAnnotationMgr() { - classAnnotationMgr.register(SwaggerDefinition.class, new SwaggerDefinitionProcessor()); - - classAnnotationMgr.register(ApiImplicitParams.class, new ApiImplicitParamsClassProcessor()); - classAnnotationMgr.register(ApiImplicitParam.class, new ApiImplicitParamClassProcessor()); - - classAnnotationMgr.register(ApiResponses.class, new ApiResponsesClassProcessor()); - classAnnotationMgr.register(ApiResponse.class, new ApiResponseClassProcessor()); - } - - protected void initMethodAnnotationMgr() { - methodAnnotationMgr.register(ApiOperation.class, new ApiOperationProcessor()); - - methodAnnotationMgr.register(ApiImplicitParams.class, new ApiImplicitParamsMethodProcessor()); - methodAnnotationMgr.register(ApiImplicitParam.class, new ApiImplicitParamMethodProcessor()); - - methodAnnotationMgr.register(ApiResponses.class, new ApiResponsesMethodProcessor()); - methodAnnotationMgr.register(ApiResponse.class, new ApiResponseMethodProcessor()); - - methodAnnotationMgr.register(ResponseHeaders.class, new ResponseHeadersProcessor()); - methodAnnotationMgr.register(ResponseHeader.class, new ResponseHeaderProcessor()); - } - - protected void initParameterAnnotationMgr() { - } - - protected void initParameterTypeProcessorMgr() { - SPIServiceUtils.getAllService(CommonParameterTypeProcessor.class).forEach(p -> { - parameterTypeProcessorMgr.register(p.getParameterType(), p); - }); - } - - protected void initDefaultParameterProcessor() { - } - - protected void initResponseTypeProcessorMgr() { - - } - - public void setDefaultParamProcessor(DefaultParameterProcessor defaultParamProcessor) { - this.defaultParameterProcessor = defaultParamProcessor; - } - - public ClassAnnotationProcessor findClassAnnotationProcessor(Class annotationType) { - return classAnnotationMgr.findProcessor(annotationType); - } - - public MethodAnnotationProcessor findMethodAnnotationProcessor(Class annotationType) { - return methodAnnotationMgr.findProcessor(annotationType); - } - - public ParameterAnnotationProcessor findParameterAnnotationProcessor(Class annotationType) { - return parameterAnnotationMgr.findProcessor(annotationType); - } - - public ParameterTypeProcessor findParameterTypeProcessor(Type type) { - return parameterTypeProcessorMgr.findValue(type); - } - - public DefaultParameterProcessor getDefaultParamProcessor() { - return defaultParameterProcessor; - } - - @Override - public ResponseTypeProcessor findResponseTypeProcessor(Type responseType) { - ResponseTypeProcessor processor = responseTypeProcessorMgr.findValue(responseType); - if (processor == null) { - processor = defaultResponseTypeProcessor; - } - - return processor; - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/AnnotationProcessorManager.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/AnnotationProcessorManager.java deleted file mode 100644 index 72359a098ba..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/AnnotationProcessorManager.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import java.lang.annotation.Annotation; - -import io.servicecomb.foundation.common.RegisterManager; - -public class AnnotationProcessorManager extends RegisterManager { - enum AnnotationType { - CLASS, - METHOD, - PARAMETER - } - - public AnnotationProcessorManager(AnnotationType annotationType) { - super(annotationType + " annotation processor mgr"); - } - - public void register(Class annotationCls, T processor) { - register(annotationCls.getName(), processor); - } - - public T findProcessor(Class annotationCls) { - return this.findValue(annotationCls.getName()); - } - - public T findProcessor(String annotationName) { - return this.findValue(annotationName); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/ClassAnnotationProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/ClassAnnotationProcessor.java deleted file mode 100644 index d02dcd84150..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/ClassAnnotationProcessor.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -public interface ClassAnnotationProcessor { - void process(Object annotation, SwaggerGenerator swaggerGenerator); -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/CommonParameterTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/CommonParameterTypeProcessor.java deleted file mode 100644 index c3a0c1ca393..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/CommonParameterTypeProcessor.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -/** - * 通过spi自动注入到所有的GeneratorContext实例中去 - */ -public interface CommonParameterTypeProcessor extends ParameterTypeProcessor { - Class getParameterType(); -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/CompositeSwaggerGeneratorContext.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/CompositeSwaggerGeneratorContext.java deleted file mode 100644 index ae6960486fe..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/CompositeSwaggerGeneratorContext.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import io.servicecomb.foundation.common.utils.SPIServiceUtils; - -@Component -public class CompositeSwaggerGeneratorContext { - private static final Logger LOGGER = LoggerFactory.getLogger(CompositeSwaggerGeneratorContext.class); - - private List contextList; - - public CompositeSwaggerGeneratorContext() { - contextList = SPIServiceUtils.getAllService(SwaggerGeneratorContext.class); - - contextList.sort((context1, context2) -> { - return context1.getOrder() - context2.getOrder(); - }); - - for (SwaggerGeneratorContext context : contextList) { - LOGGER.info("Found swagger generator context: {}", context.getClass().getName()); - } - } - - public List getContextList() { - return contextList; - } - - public SwaggerGeneratorContext selectContext(Class cls) { - for (SwaggerGeneratorContext context : contextList) { - if (context.canProcess(cls)) { - return context; - } - } - - throw new Error("impossible, must be bug."); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/DefaultParameterProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/DefaultParameterProcessor.java deleted file mode 100644 index 548a577df25..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/DefaultParameterProcessor.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -/** - * method参数没有RESTful相关标注时的处理器 - * jaxrs中,处理为body参数 - * springmvc中处理为requestParam参数 - */ -public interface DefaultParameterProcessor { - void process(OperationGenerator operationGenerator, int paramIndex); -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/MethodAnnotationProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/MethodAnnotationProcessor.java deleted file mode 100644 index c27552674e4..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/MethodAnnotationProcessor.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -public interface MethodAnnotationProcessor { - void process(Object annotation, OperationGenerator operationGenerator); -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/OperationGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/OperationGenerator.java deleted file mode 100644 index 9bdc50c89eb..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/OperationGenerator.java +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; - -import org.springframework.util.StringUtils; - -import io.servicecomb.swagger.extend.parameter.ContextParameter; -import io.servicecomb.swagger.generator.core.utils.ParamUtils; -import io.swagger.models.HttpMethod; -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.util.ReflectionUtils; - -public class OperationGenerator { - protected SwaggerGenerator swaggerGenerator; - - protected Swagger swagger; - - protected Operation operation; - - // 根据方法上独立的ResponseHeader(s)标注生成的数据 - // 如果Response中不存在对应的header,则会将这些header补充进去 - protected Map responseHeaderMap = new HashMap<>(); - - // provider的方法 - protected Method providerMethod; - - // 方法annotation所有的参数 - private List methodAnnotationParameters = new ArrayList<>(); - - // provider所有的参数 - // 如果相同的参数名在annotationParameters中已经存在 - // 则从annotationParameters移除,将之转移到providerParameters中来,覆盖在同名位置 - private List providerParameters = new ArrayList<>(); - - // 生成的契约参数 - private List swaggerParameters = new ArrayList<>(); - - protected SwaggerGeneratorContext context; - - protected String path; - - protected String httpMethod; - - public OperationGenerator(SwaggerGenerator swaggerGenerator, Method providerMethod) { - this.swaggerGenerator = swaggerGenerator; - this.swagger = swaggerGenerator.swagger; - this.operation = new Operation(); - this.providerMethod = providerMethod; - this.context = swaggerGenerator.context; - - if (swagger.getParameters() != null) { - methodAnnotationParameters.addAll(swagger.getParameters().values()); - } - } - - public void addResponseHeader(String name, Property header) { - responseHeaderMap.put(name, header); - } - - public List getSwaggerParameters() { - return swaggerParameters; - } - - public SwaggerGeneratorContext getContext() { - return context; - } - - public SwaggerGenerator getSwaggerGenerator() { - return swaggerGenerator; - } - - public Swagger getSwagger() { - return swagger; - } - - public Operation getOperation() { - return operation; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - if (!path.startsWith("/")) { - path = "/" + path; - } - this.path = path; - } - - public String getHttpMethod() { - return httpMethod; - } - - public void setHttpMethod(String httpMethod) { - if (StringUtils.isEmpty(httpMethod)) { - return; - } - - this.httpMethod = httpMethod.toLowerCase(Locale.US); - } - - public void addMethodAnnotationParameter(Parameter parameter) { - methodAnnotationParameters.add(parameter); - } - - public void addProviderParameter(Parameter parameter) { - providerParameters.add(parameter); - } - - public List getProviderParameters() { - return providerParameters; - } - - public void generate() { - scanMethodAnnotation(); - scanMethodParameters(); - scanResponse(); - - checkBodyParameter(); - copyToSwaggerParameters(); - operation.setParameters(swaggerParameters); - - correctOperation(); - } - - protected void copyToSwaggerParameters() { - for (Parameter parameter : providerParameters) { - if (ContextParameter.class.isInstance(parameter)) { - continue; - } - - int annotationIdx = ParamUtils.findParameterByName(parameter.getName(), methodAnnotationParameters); - if (annotationIdx != -1) { - Parameter annotationParameter = methodAnnotationParameters.remove(annotationIdx); - swaggerParameters.add(annotationParameter); - continue; - } - - swaggerParameters.add(parameter); - } - - swaggerParameters.addAll(methodAnnotationParameters); - } - - protected int countRealBodyParameter(List parameters) { - int count = 0; - for (Parameter p : parameters) { - if (ParamUtils.isRealBodyParameter(p)) { - count++; - } - } - - return count; - } - - protected void checkBodyParameter() { - // annotationParameters中不能有多个body - int annotationBodyCount = countRealBodyParameter(methodAnnotationParameters); - if (annotationBodyCount > 1) { - throw new Error(String.format("too many (%d) body parameter in %s:%s annotation", - annotationBodyCount, - providerMethod.getDeclaringClass().getName(), - providerMethod.getName())); - } - - // providerParameters中不能有多个body - int parameterBodyCount = countRealBodyParameter(providerParameters); - if (parameterBodyCount > 1) { - throw new Error(String.format("too many (%d) body parameter in %s:%s parameters", - parameterBodyCount, - providerMethod.getDeclaringClass().getName(), - providerMethod.getName())); - } - - // annotationParameters和providerParameters不能同时出现body - if (annotationBodyCount + parameterBodyCount >= 2) { - throw new Error(String.format("not allow both defined body parameter in %s:%s annotation and parameters", - providerMethod.getDeclaringClass().getName(), - providerMethod.getName())); - } - } - - protected void scanMethodAnnotation() { - for (Annotation annotation : providerMethod.getAnnotations()) { - MethodAnnotationProcessor processor = context.findMethodAnnotationProcessor(annotation.annotationType()); - if (processor == null) { - continue; - } - processor.process(annotation, this); - } - } - - /** - * - * 根据method上的数据,综合生成契约参数 - */ - protected void scanMethodParameters() { - Annotation[][] allAnnotations = providerMethod.getParameterAnnotations(); - Type[] parameterTypes = providerMethod.getGenericParameterTypes(); - for (int paramIdx = 0; paramIdx < parameterTypes.length; paramIdx++) { - Type type = parameterTypes[paramIdx]; - // 是否需要根据参数类型处理,目标场景:httpReqest之类 - if (processByParameterType(type, paramIdx)) { - continue; - } - - int swaggerParamCount = providerParameters.size(); - - // 根据annotation处理 - Annotation[] paramAnnotations = allAnnotations[paramIdx]; - processByParameterAnnotation(paramAnnotations, paramIdx); - - if (swaggerParamCount == providerParameters.size()) { - // 没有用于描述契约的标注,根据函数原型来反射生成 - context.getDefaultParamProcessor().process(this, paramIdx); - } - } - } - - protected void processByParameterAnnotation(Annotation[] paramAnnotations, int paramIdx) { - for (Annotation annotation : paramAnnotations) { - ParameterAnnotationProcessor processor = - context.findParameterAnnotationProcessor(annotation.annotationType()); - if (processor != null) { - processor.process(annotation, this, paramIdx); - } - } - } - - protected boolean processByParameterType(Type parameterType, int paramIdx) { - ParameterTypeProcessor processor = context.findParameterTypeProcessor(parameterType); - if (processor != null) { - processor.process(this, paramIdx); - return true; - } - - return false; - } - - public void correctOperation() { - if (StringUtils.isEmpty(operation.getOperationId())) { - operation.setOperationId(providerMethod.getName()); - } - - context.postProcessOperation(this); - - correctResponse(); - } - - private void correctResponse() { - for (Entry responseEntry : operation.getResponses().entrySet()) { - Response response = responseEntry.getValue(); - if (StringUtils.isEmpty(response.getDescription())) { - response.setDescription("response of " + responseEntry.getKey()); - } - - for (Entry entry : responseHeaderMap.entrySet()) { - if (response.getHeaders() != null && response.getHeaders().containsKey(entry.getKey())) { - continue; - } - - response.addHeader(entry.getKey(), entry.getValue()); - } - } - } - - public void scanResponse() { - if (operation.getResponses() != null) { - Response successResponse = operation.getResponses().get(SwaggerConst.SUCCESS_KEY); - if (successResponse != null) { - if (successResponse.getSchema() == null) { - // 标注已经定义了response,但是是void,这可能是在标注上未定义 - // 根据函数原型来处理response - Property property = createResponseProperty(); - successResponse.setSchema(property); - } - return; - } - } - - Property property = createResponseProperty(); - Response response = new Response(); - response.setSchema(property); - operation.addResponse(SwaggerConst.SUCCESS_KEY, response); - } - - protected Property createResponseProperty() { - Type responseType = providerMethod.getReturnType(); - if (ReflectionUtils.isVoid(responseType)) { - return null; - } - - ResponseTypeProcessor processor = context.findResponseTypeProcessor(responseType); - return processor.process(this); - } - - public Method getProviderMethod() { - return providerMethod; - } - - protected void addOperationToSwagger() { - if (StringUtils.isEmpty(httpMethod)) { - return; - } - - Path pathObj = swagger.getPath(path); - if (pathObj == null) { - pathObj = new Path(); - swagger.path(path, pathObj); - } - - HttpMethod hm = HttpMethod.valueOf(httpMethod.toUpperCase(Locale.US)); - if (pathObj.getOperationMap().get(hm) != null) { - throw new Error(String.format("Only allowed one default path. %s:%s", - swaggerGenerator.getCls().getName(), - providerMethod.getName())); - } - pathObj.set(httpMethod, operation); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/ParameterAnnotationProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/ParameterAnnotationProcessor.java deleted file mode 100644 index f5863bcf9b8..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/ParameterAnnotationProcessor.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -public interface ParameterAnnotationProcessor { - // 在ApiImplicitParam场景中paramIdx为-1 - void process(Object annotation, OperationGenerator operationGenerator, int paramIdx); -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/ParameterTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/ParameterTypeProcessor.java deleted file mode 100644 index f7157eb405c..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/ParameterTypeProcessor.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -public interface ParameterTypeProcessor { - void process(OperationGenerator operationGenerator, int paramIdx); -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/ResponseTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/ResponseTypeProcessor.java deleted file mode 100644 index fc2ba60fba2..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/ResponseTypeProcessor.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core; - -import io.swagger.models.properties.Property; - -public interface ResponseTypeProcessor { - // void的场景,不会到processor中来处理 - Property process(OperationGenerator operationGenerator); -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/SwaggerConst.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/SwaggerConst.java deleted file mode 100644 index 2c610e92bf0..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/SwaggerConst.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import javax.ws.rs.core.Response; - -public final class SwaggerConst { - private SwaggerConst() { - - } - - public static final String SUCCESS_KEY = String.valueOf(Response.Status.OK.getStatusCode()); - - public static final String EXT_JAVA_INTF = "x-java-interface"; - - public static final String EXT_JAVA_CLASS = "x-java-class"; - - public static final String EXT_RAW_JSON_TYPE = "x-raw-json"; -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/SwaggerGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/SwaggerGenerator.java deleted file mode 100644 index a44b87ceea8..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/SwaggerGenerator.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import javax.ws.rs.core.MediaType; - -import org.springframework.util.StringUtils; - -import io.swagger.annotations.ApiOperation; -import io.swagger.models.Info; -import io.swagger.models.Operation; -import io.swagger.models.Path; -import io.swagger.models.Swagger; -import io.swagger.models.parameters.Parameter; - -/** - * 根据class提取swagger信息 - * 支持以下场景的使用: - * 1.pojo + swagger annotation - * 2.pojo + swagger annotation + jaxrs annotation - * 3.pojo + swagger annotation + springmvc annotation - * 场景1,使用默认规则处理rest入参,比如将所有参数包装为一个class,统一放在body中 - * jaxrs/spring mvc的场景,需要配合各自的插件jar包 - * 非场景1,如果标注不完整,仍然会尝试使用场景1的规则产生默认数据 - */ -public class SwaggerGenerator { - protected SwaggerGeneratorContext context; - - // 需要生成class时,使用这个值 - protected String packageName; - - // 动态生成的class,加载在该classLoader中 - // 如果为null,表示加载在线程相关的classLoader中 - protected ClassLoader classLoader; - - protected Class cls; - - protected Swagger swagger; - - private Map operationGeneratorMap = new HashMap<>(); - - private String httpMethod; - - public SwaggerGenerator(SwaggerGeneratorContext context, Class cls) { - this.swagger = new Swagger(); - this.context = context; - this.cls = cls; - this.classLoader = Thread.currentThread().getContextClassLoader(); - this.packageName = "gen.swagger"; - } - - public SwaggerGeneratorContext getContext() { - return context; - } - - public void setPackageName(String packageName) { - this.packageName = packageName; - } - - public String ensureGetPackageName() { - // 不自动指定package,还是事先规划会比较合适 - if (packageName == null) { - throw new Error("package name must not be null."); - } - - return packageName; - } - - public ClassLoader getClassLoader() { - return classLoader; - } - - public void setClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } - - public Swagger getSwagger() { - return swagger; - } - - public Class getCls() { - return cls; - } - - public String getHttpMethod() { - return httpMethod; - } - - public void setHttpMethod(String httpMethod) { - this.httpMethod = httpMethod.toLowerCase(Locale.US); - } - - public Swagger generate() { - for (Annotation annotation : cls.getAnnotations()) { - ClassAnnotationProcessor processor = context.findClassAnnotationProcessor(annotation.annotationType()); - if (processor == null) { - continue; - } - processor.process(annotation, this); - } - - scanMethods(); - addOperationsToSwagger(); - - correctSwagger(); - - return swagger; - } - - /** - * 查找必填但是没值的字段,将之设置为默认值 - * 如果无法构造默认值,则抛出异常 - */ - protected void correctSwagger() { - if (StringUtils.isEmpty(swagger.getSwagger())) { - swagger.setSwagger("2.0"); - } - - correctBasePath(); - correctInfo(); - correctProduces(); - correctConsumes(); - } - - private void correctProduces() { - List produces = swagger.getProduces(); - if (produces == null || produces.isEmpty()) { - produces = Arrays.asList(MediaType.APPLICATION_JSON); - swagger.setProduces(produces); - } - } - - private void correctConsumes() { - List consumes = swagger.getConsumes(); - if (consumes == null || consumes.isEmpty()) { - consumes = Arrays.asList(MediaType.APPLICATION_JSON); - swagger.setConsumes(consumes); - } - } - - protected void correctBasePath() { - String basePath = swagger.getBasePath(); - if (StringUtils.isEmpty(basePath)) { - basePath = "/" + cls.getSimpleName(); - } - if (!basePath.startsWith("/")) { - basePath = "/" + basePath; - } - swagger.setBasePath(basePath); - } - - private void correctInfo() { - Info info = swagger.getInfo(); - if (info == null) { - info = new Info(); - swagger.setInfo(info); - } - - if (StringUtils.isEmpty(info.getTitle())) { - info.setTitle("swagger definition for " + cls.getName()); - } - if (StringUtils.isEmpty(info.getVersion())) { - info.setVersion("1.0.0"); - } - - setJavaInterface(info, cls); - } - - protected void setJavaInterface(Info info, Class cls) { - if (cls.isInterface()) { - info.setVendorExtension(SwaggerConst.EXT_JAVA_INTF, cls.getName()); - return; - } - - // Class[] interfaces = cls.getInterfaces(); - // if (interfaces.length == 1) { - // info.setVendorExtension(SwaggerConst.EXT_JAVA_INTF, interfaces[0].getName()); - // return; - // } - - String intfName = ensureGetPackageName() + "." + cls.getSimpleName() + "Intf"; - info.setVendorExtension(SwaggerConst.EXT_JAVA_INTF, intfName); - } - - protected boolean isSkipMethod(Method method) { - if (method.getDeclaringClass().getName().equals(Object.class.getName())) { - return true; - } - - ApiOperation apiOperation = method.getAnnotation(ApiOperation.class); - if (apiOperation != null) { - return apiOperation.hidden(); - } - - return !context.canProcess(method); - } - - protected void scanMethods() { - // 有时方法顺序不同,很不利于测试,所以先排序 - List methods = Arrays.asList(cls.getMethods()); - methods.sort((m1, m2) -> { - return m1.getName().compareTo(m2.getName()); - }); - for (Method method : methods) { - if (isSkipMethod(method)) { - continue; - } - - OperationGenerator operationGenerator = new OperationGenerator(this, method); - operationGenerator.setHttpMethod(httpMethod); - try { - operationGenerator.generate(); - } catch (Throwable e) { - String msg = - String.format("generate operation swagger failed, %s:%s", this.cls.getName(), method.getName()); - throw new Error(msg, e); - } - - String operationId = operationGenerator.getOperation().getOperationId(); - if (operationGeneratorMap.containsKey(operationId)) { - throw new Error(String.format("OperationId must be unique. %s:%s", cls.getName(), method.getName())); - } - operationGeneratorMap.put(operationId, operationGenerator); - } - } - - protected void addOperationsToSwagger() { - for (OperationGenerator operationGenerator : operationGeneratorMap.values()) { - operationGenerator.addOperationToSwagger(); - } - } - - public void setBasePath(String basePath) { - swagger.setBasePath(basePath); - } - - public Map getOperationGeneratorMap() { - return operationGeneratorMap; - } - - public List findProviderParameter(String methodName) { - OperationGenerator operationGenerator = operationGeneratorMap.get(methodName); - if (operationGenerator == null) { - throw new Error("method not found, name=" + methodName); - } - - return operationGenerator.getProviderParameters(); - } - - public List findSwaggerParameter(String methodName) { - for (Path path : swagger.getPaths().values()) { - for (Operation operation : path.getOperations()) { - if (methodName.equals(operation.getOperationId())) { - return operation.getParameters(); - } - } - } - - throw new Error("method not found, name=" + methodName); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/SwaggerGeneratorContext.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/SwaggerGeneratorContext.java deleted file mode 100644 index 04a10950a1d..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/SwaggerGeneratorContext.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Type; - -public interface SwaggerGeneratorContext { - // 用于排序,规避cse中定义的restSchema无法识别是jaxrs还是springmvc模式的问题 - // 实际使用时springmvc的context优先级最高 - int getOrder(); - - boolean canProcess(Class cls); - - boolean canProcess(Method method); - - ClassAnnotationProcessor findClassAnnotationProcessor(Class annotationType); - - MethodAnnotationProcessor findMethodAnnotationProcessor(Class annotationType); - - ParameterAnnotationProcessor findParameterAnnotationProcessor(Class annotationType); - - // 管理方法入参的processor,用于支撑httpRequest、Context之类的特殊处理 - // key为class - ParameterTypeProcessor findParameterTypeProcessor(Type parameterType); - - // 经过所有步骤后,仍然无法被处理的参数的默认处理器 - DefaultParameterProcessor getDefaultParamProcessor(); - - ResponseTypeProcessor findResponseTypeProcessor(Type responseType); - - void postProcessOperation(OperationGenerator operationGenerator); -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/package-info.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/package-info.java deleted file mode 100644 index 219a3115cab..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * 根据annotation及接口反射,反向生成swagger - * 本package中的功能会被cse依赖,但是不会依赖cse的概念 - */ -package io.servicecomb.swagger.generator.core; diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/AnnotationUtils.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/AnnotationUtils.java deleted file mode 100644 index a98decbb067..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/AnnotationUtils.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.processor.annotation; - -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; - -import io.servicecomb.swagger.generator.core.processor.annotation.models.ResponseConfig; -import io.servicecomb.swagger.generator.core.processor.annotation.models.ResponseConfigBase; -import io.servicecomb.swagger.generator.core.processor.annotation.models.ResponseHeaderConfig; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ResponseHeader; -import io.swagger.converter.ModelConverters; -import io.swagger.models.Model; -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.CookieParameter; -import io.swagger.models.parameters.FormParameter; -import io.swagger.models.parameters.HeaderParameter; -import io.swagger.models.parameters.Parameter; -import io.swagger.models.parameters.PathParameter; -import io.swagger.models.parameters.QueryParameter; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.Property; -import io.swagger.util.ParameterProcessor; -import io.swagger.util.ReflectionUtils; - -public final class AnnotationUtils { - private AnnotationUtils() { - - } - - public static void appendDefinition(Swagger swagger, Map newDefinitions) { - if (newDefinitions.isEmpty()) { - return; - } - - Map definitions = swagger.getDefinitions(); - if (definitions == null) { - definitions = new LinkedHashMap<>(); - swagger.setDefinitions(definitions); - } - - definitions.putAll(newDefinitions); - } - - private static ResponseConfig convert(ApiOperation apiOperation) { - ResponseConfig responseConfig = new ResponseConfig(); - responseConfig.setCode(apiOperation.code()); - responseConfig.setResponseClass(apiOperation.response()); - responseConfig.setResponseContainer(apiOperation.responseContainer()); - responseConfig.setResponseReference(apiOperation.responseReference()); - responseConfig.setResponseHeaders(apiOperation.responseHeaders()); - return responseConfig; - } - - private static ResponseConfig convert(ApiResponse apiResponse) { - ResponseConfig responseConfig = new ResponseConfig(); - responseConfig.setCode(apiResponse.code()); - responseConfig.setDescription(apiResponse.message()); - responseConfig.setResponseClass(apiResponse.response()); - responseConfig.setResponseContainer(apiResponse.responseContainer()); - responseConfig.setResponseReference(apiResponse.reference()); - responseConfig.setResponseHeaders(apiResponse.responseHeaders()); - return responseConfig; - } - - public static ResponseHeaderConfig convert(ResponseHeader responseHeader) { - if (StringUtils.isEmpty(responseHeader.name())) { - return null; - } - - ResponseHeaderConfig config = new ResponseHeaderConfig(); - config.setName(responseHeader.name()); - config.setDescription(responseHeader.description()); - config.setResponseClass(responseHeader.response()); - config.setResponseContainer(responseHeader.responseContainer()); - return config; - } - - public static void addResponse(Swagger swagger, Operation operation, ApiOperation apiOperation) { - ResponseConfig responseConfig = convert(apiOperation); - generateResponse(swagger, responseConfig); - operation.response(responseConfig.getCode(), responseConfig.getResponse()); - } - - public static void addResponse(Swagger swagger, ApiResponse apiResponse) { - ResponseConfig responseConfig = convert(apiResponse); - generateResponse(swagger, responseConfig); - swagger.response(String.valueOf(responseConfig.getCode()), responseConfig.getResponse()); - } - - public static void addResponse(Swagger swagger, Operation operation, ApiResponse apiResponse) { - ResponseConfig responseConfig = convert(apiResponse); - generateResponse(swagger, responseConfig); - operation.response(responseConfig.getCode(), responseConfig.getResponse()); - } - - private static void generateResponse(Swagger swagger, ResponseConfig responseConfig) { - Response response = new Response(); - - Property property = generateResponseProperty(swagger, responseConfig); - response.setSchema(property); - - if (responseConfig.getResponseHeaders() != null) { - Map headers = generateResponseHeader(swagger, responseConfig.getResponseHeaders()); - response.setHeaders(headers); - } - - responseConfig.setResponse(response); - } - - private static Map generateResponseHeader(Swagger swagger, - List responseHeaders) { - Map headers = new HashMap<>(); - for (ResponseHeaderConfig config : responseHeaders) { - Property property = generateResponseHeaderProperty(swagger, config); - headers.put(config.getName(), property); - } - return headers; - } - - public static Property generateResponseHeaderProperty(Swagger swagger, ResponseHeaderConfig config) throws Error { - Property property = generateResponseProperty(swagger, config); - if (property == null) { - throw new Error("invalid responseHeader, " + config); - } - return property; - } - - public static Property generateResponseProperty(Swagger swagger, ResponseConfigBase config) throws Error { - Class responseClass = config.getResponseClass(); - if (responseClass == null || ReflectionUtils.isVoid(responseClass)) { - return null; - } - - if (!ClassUtils.isPrimitiveOrWrapper(responseClass)) { - Map newDefinitions = ModelConverters.getInstance().readAll(responseClass); - appendDefinition(swagger, newDefinitions); - } - - Property property = ModelConverters.getInstance().readAsProperty(responseClass); - // responseContainer只可能是:"List", "Set" or "Map" - // 根据swagger定义这里是区分大小写的, 虽然不明白为何这样做,不过还是不要改标准了 - switch (config.getResponseContainer()) { - case "List": - property = new ArrayProperty(property); - break; - case "Set": - property = new ArrayProperty(property); - ((ArrayProperty) property).setUniqueItems(true); - break; - case "Map": - property = new MapProperty(property); - break; - case "": - // 不必处理 - break; - default: - throw new Error("not support responseContainer " + config.getResponseContainer()); - } - return property; - } - - public static Parameter createParameter(Swagger swagger, ApiImplicitParam paramAnnotation) { - Parameter parameter = createParameterInstance(paramAnnotation); - Type dataType = ReflectionUtils.typeFromString(paramAnnotation.dataType()); - parameter = ParameterProcessor.applyAnnotations(swagger, - parameter, - dataType, - Arrays.asList(paramAnnotation)); - return parameter; - } - - private static Parameter createParameterInstance(ApiImplicitParam paramAnnotation) { - switch (paramAnnotation.paramType()) { - case "path": - return new PathParameter(); - case "query": - return new QueryParameter(); - case "body": - return new BodyParameter(); - case "header": - return new HeaderParameter(); - case "form": - return new FormParameter(); - case "cookie": - return new CookieParameter(); - default: - throw new Error("not support paramType " + paramAnnotation.paramType()); - } - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiImplicitParamClassProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiImplicitParamClassProcessor.java deleted file mode 100644 index 0a57c81ac96..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiImplicitParamClassProcessor.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.processor.annotation; - -import io.servicecomb.swagger.generator.core.ClassAnnotationProcessor; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.models.parameters.Parameter; - -public class ApiImplicitParamClassProcessor implements ClassAnnotationProcessor { - @Override - public void process(Object annotation, SwaggerGenerator swaggerGenerator) { - ApiImplicitParam paramAnnotation = (ApiImplicitParam) annotation; - - Parameter parameter = AnnotationUtils.createParameter(swaggerGenerator.getSwagger(), paramAnnotation); - swaggerGenerator.getSwagger().addParameter(parameter.getName(), parameter); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiImplicitParamMethodProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiImplicitParamMethodProcessor.java deleted file mode 100644 index 0352dee5d08..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiImplicitParamMethodProcessor.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.processor.annotation; - -import io.servicecomb.swagger.generator.core.MethodAnnotationProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.models.parameters.Parameter; - -public class ApiImplicitParamMethodProcessor implements MethodAnnotationProcessor { - @Override - public void process(Object annotation, OperationGenerator operationGenerator) { - ApiImplicitParam paramAnnotation = (ApiImplicitParam) annotation; - - Parameter parameter = AnnotationUtils.createParameter(operationGenerator.getSwagger(), paramAnnotation); - operationGenerator.addMethodAnnotationParameter(parameter); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiImplicitParamsClassProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiImplicitParamsClassProcessor.java deleted file mode 100644 index 0a50495167a..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiImplicitParamsClassProcessor.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.processor.annotation; - -import io.servicecomb.swagger.generator.core.ClassAnnotationProcessor; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; - -public class ApiImplicitParamsClassProcessor implements ClassAnnotationProcessor { - @Override - public void process(Object annotation, SwaggerGenerator swaggerGenerator) { - ApiImplicitParams apiImplicitParamsAnnotation = (ApiImplicitParams) annotation; - - ClassAnnotationProcessor processor = - swaggerGenerator.getContext().findClassAnnotationProcessor(ApiImplicitParam.class); - for (ApiImplicitParam paramAnnotation : apiImplicitParamsAnnotation.value()) { - processor.process(paramAnnotation, swaggerGenerator); - } - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiImplicitParamsMethodProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiImplicitParamsMethodProcessor.java deleted file mode 100644 index c1147585ff7..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiImplicitParamsMethodProcessor.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.processor.annotation; - -import io.servicecomb.swagger.generator.core.MethodAnnotationProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; - -public class ApiImplicitParamsMethodProcessor implements MethodAnnotationProcessor { - @Override - public void process(Object annotation, OperationGenerator operationGenerator) { - ApiImplicitParams apiImplicitParamsAnnotation = (ApiImplicitParams) annotation; - - MethodAnnotationProcessor processor = - operationGenerator.getContext().findMethodAnnotationProcessor(ApiImplicitParam.class); - for (ApiImplicitParam paramAnnotation : apiImplicitParamsAnnotation.value()) { - processor.process(paramAnnotation, operationGenerator); - } - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiOperationProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiOperationProcessor.java deleted file mode 100644 index 09a756ee655..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiOperationProcessor.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.processor.annotation; - -import org.springframework.util.StringUtils; - -import io.servicecomb.swagger.generator.core.MethodAnnotationProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.swagger.annotations.ApiOperation; -import io.swagger.models.Operation; -import io.swagger.models.Scheme; -import io.swagger.util.BaseReaderUtils; - -public class ApiOperationProcessor implements MethodAnnotationProcessor { - @Override - public void process(Object annotation, OperationGenerator operationGenerator) { - ApiOperation apiOperationAnnotation = (ApiOperation) annotation; - Operation operation = operationGenerator.getOperation(); - - operationGenerator.setHttpMethod(apiOperationAnnotation.httpMethod()); - - if (!StringUtils.isEmpty(apiOperationAnnotation.value())) { - operation.setSummary(apiOperationAnnotation.value()); - } - - if (!StringUtils.isEmpty(apiOperationAnnotation.notes())) { - operation.setDescription(apiOperationAnnotation.notes()); - } - - operation.setOperationId(apiOperationAnnotation.nickname()); - operation.getVendorExtensions().putAll(BaseReaderUtils.parseExtensions(apiOperationAnnotation.extensions())); - - convertTags(apiOperationAnnotation.tags(), operation); - convertProduces(apiOperationAnnotation.produces(), operation); - convertConsumes(apiOperationAnnotation.consumes(), operation); - convertProtocols(apiOperationAnnotation.protocols(), operation); - AnnotationUtils.addResponse(operationGenerator.getSwagger(), - operation, - apiOperationAnnotation); - - // responseReference未解析 - // hidden未解析 - // authorizations未解析 - } - - // protocols以分号为创建,比如:http, https, ws, wss - private void convertProtocols(String protocols, Operation operation) { - if (protocols == null) { - return; - } - - for (String protocol : protocols.split(",")) { - if (StringUtils.isEmpty(protocol)) { - continue; - } - - operation.addScheme(Scheme.forValue(protocol)); - } - } - - // consumes以分号为创建,比如:application/json, application/xml - private void convertConsumes(String consumes, Operation operation) { - if (StringUtils.isEmpty(consumes)) { - return; - } - - for (String consume : consumes.split(",")) { - if (StringUtils.isEmpty(consume)) { - continue; - } - - operation.addConsumes(consume); - } - } - - // produces以分号为创建,比如:application/json, application/xml - private void convertProduces(String produces, Operation operation) { - if (StringUtils.isEmpty(produces)) { - return; - } - - for (String produce : produces.split(",")) { - if (StringUtils.isEmpty(produce)) { - continue; - } - - operation.addProduces(produce); - } - } - - private void convertTags(String[] tags, Operation operation) { - if (tags == null || tags.length == 0) { - return; - } - - for (String tag : tags) { - if (StringUtils.isEmpty(tag)) { - continue; - } - - operation.addTag(tag); - } - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiResponseClassProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiResponseClassProcessor.java deleted file mode 100644 index 8b224f2fbaf..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiResponseClassProcessor.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.processor.annotation; - -import io.servicecomb.swagger.generator.core.ClassAnnotationProcessor; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.swagger.annotations.ApiResponse; - -public class ApiResponseClassProcessor implements ClassAnnotationProcessor { - @Override - public void process(Object annotation, SwaggerGenerator swaggerGenerator) { - // swagger号称不允许独立使用这个标注,不过支持独立使用,也没什么后果 - ApiResponse apiResponse = (ApiResponse) annotation; - - AnnotationUtils.addResponse(swaggerGenerator.getSwagger(), apiResponse); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiResponseMethodProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiResponseMethodProcessor.java deleted file mode 100644 index a67b1cd0e27..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiResponseMethodProcessor.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.processor.annotation; - -import io.servicecomb.swagger.generator.core.MethodAnnotationProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.swagger.annotations.ApiResponse; - -public class ApiResponseMethodProcessor implements MethodAnnotationProcessor { - - @Override - public void process(Object annotation, OperationGenerator operationGenerator) { - // swagger号称不允许独立使用这个标注,不过支持独立使用,也没什么后果 - ApiResponse apiResponse = (ApiResponse) annotation; - - AnnotationUtils.addResponse(operationGenerator.getSwagger(), - operationGenerator.getOperation(), - apiResponse); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiResponsesClassProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiResponsesClassProcessor.java deleted file mode 100644 index ad0d05ac387..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiResponsesClassProcessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.processor.annotation; - -import io.servicecomb.swagger.generator.core.ClassAnnotationProcessor; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; - -public class ApiResponsesClassProcessor implements ClassAnnotationProcessor { - @Override - public void process(Object annotation, SwaggerGenerator swaggerGenerator) { - ApiResponses apiResponses = (ApiResponses) annotation; - - ClassAnnotationProcessor processor = - swaggerGenerator.getContext().findClassAnnotationProcessor(ApiResponse.class); - for (ApiResponse apiResponse : apiResponses.value()) { - processor.process(apiResponse, swaggerGenerator); - } - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiResponsesMethodProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiResponsesMethodProcessor.java deleted file mode 100644 index 815aa5ebc4c..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ApiResponsesMethodProcessor.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.processor.annotation; - -import io.servicecomb.swagger.generator.core.MethodAnnotationProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; - -public class ApiResponsesMethodProcessor implements MethodAnnotationProcessor { - - @Override - public void process(Object annotation, OperationGenerator operationGenerator) { - ApiResponses apiResponses = (ApiResponses) annotation; - - MethodAnnotationProcessor processor = - operationGenerator.getContext().findMethodAnnotationProcessor(ApiResponse.class); - for (ApiResponse apiResponse : apiResponses.value()) { - processor.process(apiResponse, operationGenerator); - } - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ResponseHeaderProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ResponseHeaderProcessor.java deleted file mode 100644 index de99bbdf55e..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ResponseHeaderProcessor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.processor.annotation; - -import io.servicecomb.swagger.generator.core.MethodAnnotationProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.core.processor.annotation.models.ResponseHeaderConfig; -import io.swagger.annotations.ResponseHeader; -import io.swagger.models.properties.Property; - -public class ResponseHeaderProcessor implements MethodAnnotationProcessor { - - @Override - public void process(Object annotation, OperationGenerator operationGenerator) { - ResponseHeader responseHeader = (ResponseHeader) annotation; - - ResponseHeaderConfig config = AnnotationUtils.convert(responseHeader); - if (config != null) { - Property property = - AnnotationUtils.generateResponseHeaderProperty(operationGenerator.getSwagger(), config); - operationGenerator.addResponseHeader(config.getName(), property); - } - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ResponseHeadersProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ResponseHeadersProcessor.java deleted file mode 100644 index 01e610c7e0f..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/ResponseHeadersProcessor.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.processor.annotation; - -import io.servicecomb.swagger.extend.annotations.ResponseHeaders; -import io.servicecomb.swagger.generator.core.MethodAnnotationProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.swagger.annotations.ResponseHeader; - -public class ResponseHeadersProcessor implements MethodAnnotationProcessor { - - @Override - public void process(Object annotation, OperationGenerator operationGenerator) { - ResponseHeaders responseHeaders = (ResponseHeaders) annotation; - - MethodAnnotationProcessor processor = - operationGenerator.getContext().findMethodAnnotationProcessor(ResponseHeader.class); - for (ResponseHeader responseHeader : responseHeaders.value()) { - processor.process(responseHeader, operationGenerator); - } - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/SwaggerDefinitionProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/SwaggerDefinitionProcessor.java deleted file mode 100644 index 1bf29b25d3f..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/SwaggerDefinitionProcessor.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.processor.annotation; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.springframework.util.StringUtils; - -import io.servicecomb.swagger.generator.core.ClassAnnotationProcessor; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.swagger.annotations.SwaggerDefinition; -import io.swagger.models.Contact; -import io.swagger.models.ExternalDocs; -import io.swagger.models.Info; -import io.swagger.models.License; -import io.swagger.models.Scheme; -import io.swagger.models.Swagger; -import io.swagger.models.Tag; -import io.swagger.util.BaseReaderUtils; - -public class SwaggerDefinitionProcessor implements ClassAnnotationProcessor { - @Override - public void process(Object annotation, SwaggerGenerator swaggerGenerator) { - SwaggerDefinition definitionAnnotation = (SwaggerDefinition) annotation; - Swagger swagger = swaggerGenerator.getSwagger(); - - swaggerGenerator.setBasePath(definitionAnnotation.basePath()); - swagger.setHost(definitionAnnotation.host()); - - convertConsumes(definitionAnnotation, swagger); - convertProduces(definitionAnnotation, swagger); - convertSchemes(definitionAnnotation, swagger); - convertTags(definitionAnnotation, swagger); - convertInfo(definitionAnnotation.info(), swagger); - swagger.setExternalDocs(convertExternalDocs(definitionAnnotation.externalDocs())); - } - - private void convertInfo(io.swagger.annotations.Info infoAnnotation, Swagger swagger) { - if (infoAnnotation == null) { - return; - } - - Info info = new Info(); - - info.setTitle(infoAnnotation.title()); - info.setVersion(infoAnnotation.version()); - info.setDescription(infoAnnotation.description()); - info.setTermsOfService(infoAnnotation.termsOfService()); - info.setContact(convertContact(infoAnnotation.contact())); - info.setLicense(convertLicense(infoAnnotation.license())); - info.getVendorExtensions().putAll(BaseReaderUtils.parseExtensions(infoAnnotation.extensions())); - - swagger.setInfo(info); - } - - private License convertLicense(io.swagger.annotations.License licenseAnnotation) { - License license = new License(); - - license.setName(licenseAnnotation.name()); - license.setUrl(licenseAnnotation.url()); - - return license; - } - - private Contact convertContact(io.swagger.annotations.Contact contactAnnotation) { - Contact contact = new Contact(); - - contact.setName(contactAnnotation.name()); - contact.setUrl(contactAnnotation.url()); - contact.setEmail(contactAnnotation.email()); - - return contact; - } - - private void convertTags(SwaggerDefinition definitionAnnotation, Swagger swagger) { - if (definitionAnnotation.tags() == null) { - return; - } - - Stream stream = - Arrays.asList(definitionAnnotation.tags()).stream(); - List tags = stream.map(this::convertTag).collect(Collectors.toList()); - swagger.setTags(tags); - } - - private Tag convertTag(io.swagger.annotations.Tag tagAnnotation) { - Tag tag = new Tag(); - tag.setName(tagAnnotation.name()); - tag.setDescription(tagAnnotation.description()); - tag.setExternalDocs(convertExternalDocs(tagAnnotation.externalDocs())); - tag.getVendorExtensions().putAll(BaseReaderUtils.parseExtensions(tagAnnotation.extensions())); - return tag; - } - - private ExternalDocs convertExternalDocs(io.swagger.annotations.ExternalDocs annotationExternalDocs) { - ExternalDocs externalDocs = new ExternalDocs(); - externalDocs.setUrl(annotationExternalDocs.url()); - externalDocs.setDescription(annotationExternalDocs.value()); - - return externalDocs; - } - - private void convertSchemes(SwaggerDefinition definitionAnnotation, Swagger swagger) { - if (definitionAnnotation.schemes() == null) { - return; - } - - Stream stream = - Arrays.asList(definitionAnnotation.schemes()).stream(); - List schemes = stream.map(this::convertScheme).collect(Collectors.toList()); - swagger.setSchemes(schemes); - } - - private Scheme convertScheme(io.swagger.annotations.SwaggerDefinition.Scheme annotationScheme) { - return Scheme.forValue(annotationScheme.name()); - } - - private void convertProduces(SwaggerDefinition definitionAnnotation, Swagger swagger) { - String[] produces = definitionAnnotation.produces(); - if (produces == null) { - return; - } - for (String produce : produces) { - if (!StringUtils.isEmpty(produce)) { - swagger.addProduces(produce); - } - } - } - - private void convertConsumes(SwaggerDefinition definitionAnnotation, Swagger swagger) { - String[] consumes = definitionAnnotation.consumes(); - if (consumes == null) { - return; - } - for (String consume : consumes) { - if (!StringUtils.isEmpty(consume)) { - swagger.addConsumes(consume); - } - } - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/models/ResponseConfig.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/models/ResponseConfig.java deleted file mode 100644 index 27a8fd83887..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/models/ResponseConfig.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.processor.annotation.models; - -import java.util.ArrayList; -import java.util.List; - -import io.servicecomb.swagger.generator.core.processor.annotation.AnnotationUtils; -import io.swagger.annotations.ResponseHeader; -import io.swagger.models.Response; - -/** - * Response的数据源太多,单单是标注都有N个 - * 所以将数据提取出来,统一处理 - */ -public class ResponseConfig extends ResponseConfigBase { - private int code; - - private List responseHeaders; - - // 根据本config生成的response - private Response response; - - public int getCode() { - return code; - } - - public void setCode(int code) { - this.code = code; - } - - public List getResponseHeaders() { - return responseHeaders; - } - - public void setResponseHeaders(List responseHeaders) { - this.responseHeaders = responseHeaders; - } - - public void setResponseHeaders(ResponseHeader[] responseHeaders) { - this.responseHeaders = new ArrayList<>(); - for (ResponseHeader header : responseHeaders) { - ResponseHeaderConfig config = AnnotationUtils.convert(header); - if (config != null) { - this.responseHeaders.add(config); - } - } - - if (this.responseHeaders.isEmpty()) { - this.responseHeaders = null; - } - } - - public Response getResponse() { - return response; - } - - public void setResponse(Response response) { - this.response = response; - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/models/ResponseConfigBase.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/models/ResponseConfigBase.java deleted file mode 100644 index fc4b4fb10b3..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/models/ResponseConfigBase.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.processor.annotation.models; - -public class ResponseConfigBase { - private String description; - - private String responseReference; - - private Class responseClass; - - private String responseContainer; - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getResponseReference() { - return responseReference; - } - - public void setResponseReference(String responseReference) { - this.responseReference = responseReference; - } - - public Class getResponseClass() { - return responseClass; - } - - public void setResponseClass(Class responseClass) { - this.responseClass = responseClass; - } - - public String getResponseContainer() { - return responseContainer; - } - - public void setResponseContainer(String responseContainer) { - this.responseContainer = responseContainer; - } - - @Override - public String toString() { - return "ResponseConfigBase [description=" + description + ", responseReference=" + responseReference - + ", responseClass=" + responseClass + ", responseContainer=" + responseContainer + "]"; - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/models/ResponseHeaderConfig.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/models/ResponseHeaderConfig.java deleted file mode 100644 index 1fad68efdd9..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/models/ResponseHeaderConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.processor.annotation.models; - -public class ResponseHeaderConfig extends ResponseConfigBase { - private String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public String toString() { - return "ResponseHeaderConfig [name=" + name + ", " + super.toString() + "]"; - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/package-info.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/package-info.java deleted file mode 100644 index 3e2f9308f9b..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/annotation/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * swagger竟然没提供从annotation生成swagger对象的功能,只好自己做了 - */ -package io.servicecomb.swagger.generator.core.processor.annotation; diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/parameter/AbstractParameterProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/parameter/AbstractParameterProcessor.java deleted file mode 100644 index e4717a3fa02..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/parameter/AbstractParameterProcessor.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.processor.parameter; - -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.core.ParameterAnnotationProcessor; -import io.servicecomb.swagger.generator.core.utils.ParamUtils; -import io.swagger.models.parameters.AbstractSerializableParameter; - -public abstract class AbstractParameterProcessor> - implements ParameterAnnotationProcessor { - @Override - public void process(Object annotation, OperationGenerator operationGenerator, int paramIdx) { - T parameter = createParameter(); - - fillParameter(annotation, operationGenerator, paramIdx, parameter); - - operationGenerator.addProviderParameter(parameter); - } - - protected void fillParameter(Object annotation, OperationGenerator operationGenerator, int paramIdx, - T parameter) { - setParameterName(annotation, operationGenerator, paramIdx, parameter); - setParameterType(operationGenerator, paramIdx, parameter); - } - - protected void setParameterType(OperationGenerator operationGenerator, int paramIdx, - T parameter) { - ParamUtils.setParameterType(operationGenerator.getSwagger(), - operationGenerator.getProviderMethod(), - paramIdx, - parameter); - } - - protected void setParameterName(Object annotation, OperationGenerator operationGenerator, int paramIdx, - T parameter) { - String paramName = getAnnotationParameterName(annotation); - paramName = ParamUtils.getParameterName(paramName, operationGenerator.getProviderMethod(), paramIdx); - parameter.setName(paramName); - } - - protected abstract T createParameter(); - - protected abstract String getAnnotationParameterName(Object annotation); -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/parametertype/HttpServletRequestProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/parametertype/HttpServletRequestProcessor.java deleted file mode 100644 index b692e356698..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/parametertype/HttpServletRequestProcessor.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.processor.parametertype; - -import javax.servlet.http.HttpServletRequest; - -import io.servicecomb.swagger.extend.parameter.HttpRequestParameter; -import io.servicecomb.swagger.generator.core.CommonParameterTypeProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; - -public class HttpServletRequestProcessor implements CommonParameterTypeProcessor { - @Override - public Class getParameterType() { - return HttpServletRequest.class; - } - - @Override - public void process(OperationGenerator operationGenerator, int paramIdx) { - HttpRequestParameter parameter = new HttpRequestParameter(); - operationGenerator.addProviderParameter(parameter); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/response/DefaultResponseTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/response/DefaultResponseTypeProcessor.java deleted file mode 100644 index 83b37ec0dd6..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/processor/response/DefaultResponseTypeProcessor.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.processor.response; - -import java.lang.reflect.Type; - -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.core.ResponseTypeProcessor; -import io.servicecomb.swagger.generator.core.utils.ParamUtils; -import io.swagger.converter.ModelConverters; -import io.swagger.models.properties.Property; - -public class DefaultResponseTypeProcessor implements ResponseTypeProcessor { - - @Override - public Property process(OperationGenerator operationGenerator) { - Type responseType = operationGenerator.getProviderMethod().getGenericReturnType(); - - ParamUtils.addDefinitions(operationGenerator.getSwagger(), responseType); - return ModelConverters.getInstance().readAsProperty(responseType); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/unittest/SwaggerGeneratorForTest.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/unittest/SwaggerGeneratorForTest.java deleted file mode 100644 index 99319d7c8d5..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/unittest/SwaggerGeneratorForTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.unittest; - -import java.lang.reflect.Method; -import java.util.HashSet; -import java.util.Set; - -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.servicecomb.swagger.generator.core.SwaggerGeneratorContext; - -public class SwaggerGeneratorForTest extends SwaggerGenerator { - // 可用于控制一次扫描中有哪些method需要处理 - // 如果methodNameSet为null,表示全部处理 - private Set methodNameSet; - - public SwaggerGeneratorForTest(SwaggerGeneratorContext context, Class cls) { - super(context, cls); - setPackageName("gen.cse.ms.ut"); - } - - public boolean containsMethod(String methodName) { - if (methodNameSet == null) { - // 无约束 - return true; - } - - return methodNameSet.contains(methodName); - } - - protected void clearMethod() { - if (methodNameSet != null) { - methodNameSet.clear(); - } - methodNameSet = null; - } - - public void replaceMethods(String... methodNames) { - clearMethod(); - - if (methodNames == null || methodNames.length == 0) { - return; - } - - if (methodNameSet == null) { - methodNameSet = new HashSet<>(); - } - - for (String methodName : methodNames) { - methodNameSet.add(methodName); - } - } - - @Override - protected boolean isSkipMethod(Method method) { - boolean skip = super.isSkipMethod(method); - if (skip) { - return true; - } - - return !containsMethod(method.getName()); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/unittest/UnitTestSwaggerUtils.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/unittest/UnitTestSwaggerUtils.java deleted file mode 100644 index 5e2e5aaddff..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/unittest/UnitTestSwaggerUtils.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.unittest; - -import java.io.IOException; -import java.net.URL; - -import org.apache.commons.io.IOUtils; -import org.junit.Assert; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectWriter; - -import io.servicecomb.swagger.generator.core.CompositeSwaggerGeneratorContext; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.servicecomb.swagger.generator.core.SwaggerGeneratorContext; -import io.swagger.models.Swagger; -import io.swagger.util.Yaml; - -public final class UnitTestSwaggerUtils { - private static ObjectWriter writer = Yaml.pretty(); - - private static CompositeSwaggerGeneratorContext compositeContext = new CompositeSwaggerGeneratorContext(); - - private UnitTestSwaggerUtils() { - } - - public static SwaggerGenerator generateSwagger(Class cls) { - SwaggerGeneratorContext context = compositeContext.selectContext(cls); - SwaggerGenerator generator = new SwaggerGenerator(context, cls); - generator.generate(); - - return generator; - } - - public static String loadExpect(String resPath) { - URL url = Thread.currentThread().getContextClassLoader().getResource(resPath); - if (url == null) { - return "can not found res " + resPath; - } - - try { - return IOUtils.toString(url); - } catch (IOException e) { - return e.getMessage(); - } - } - - public static String pretty(Swagger swagger) { - try { - return writer.writeValueAsString(swagger); - } catch (JsonProcessingException e) { - throw new Error(e); - } - } - - public static Swagger parse(String content) { - try { - return Yaml.mapper().readValue(content, Swagger.class); - } catch (Exception e) { - return new Swagger(); - // throw new Error(e); - } - } - - public static SwaggerGenerator testSwagger(String resPath, SwaggerGeneratorContext context, Class cls, - String... methods) { - SwaggerGeneratorForTest generator = new SwaggerGeneratorForTest(context, cls); - generator.setClassLoader(new ClassLoader() { - }); - generator.replaceMethods(methods); - - Swagger swagger = generator.generate(); - - String expectSchema = loadExpect(resPath); - Swagger expectSwagger = parse(expectSchema); - - String schema = pretty(swagger); - swagger = parse(schema); - - if (swagger != null && !swagger.equals(expectSwagger)) { - Assert.assertEquals(expectSchema, schema); - } - - return generator; - } - - public static Throwable getException(SwaggerGeneratorContext context, Class cls, - String... methods) { - try { - SwaggerGeneratorForTest generator = new SwaggerGeneratorForTest(context, cls); - generator.replaceMethods(methods); - - generator.generate(); - } catch (Throwable e) { - return e; - } - - // 不允许成功 - Assert.assertEquals("not allowed run to here", "run to here"); - return null; - } - - public static void testException(String expectMsgLevel1, String expectMsgLevel2, SwaggerGeneratorContext context, - Class cls, - String... methods) { - Throwable exception = getException(context, cls, methods); - Assert.assertEquals(expectMsgLevel1, exception.getMessage()); - Assert.assertEquals(expectMsgLevel2, exception.getCause().getMessage()); - } - - public static void testException(String expectMsg, SwaggerGeneratorContext context, Class cls, - String... methods) { - Throwable exception = getException(context, cls, methods); - Assert.assertEquals(expectMsg, exception.getMessage()); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/unittest/package-info.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/unittest/package-info.java deleted file mode 100644 index 21d8fb9e1b9..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/unittest/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * 专用于ut场景 - */ -package io.servicecomb.swagger.generator.core.unittest; diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/utils/ClassUtils.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/utils/ClassUtils.java deleted file mode 100644 index 6591e6844cb..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/utils/ClassUtils.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.utils; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.springframework.util.StringUtils; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.common.javassist.ClassConfig; -import io.servicecomb.common.javassist.JavassistUtils; -import io.servicecomb.common.javassist.MethodConfig; -import io.servicecomb.swagger.converter.ConverterMgr; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.core.SwaggerConst; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -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.BodyParameter; -import io.swagger.models.parameters.Parameter; -import io.swagger.models.properties.Property; - -public final class ClassUtils { - // reference: - // https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html - // https://en.wikipedia.org/wiki/List_of_Java_keywords - private static final Set JAVA_RESERVED_WORDS = new HashSet<>(); - - static { - JAVA_RESERVED_WORDS.addAll(Arrays.asList("true", - "false", - "null", - "abstract", - "continue", - "for", - "new", - "switch", - "assert", - "default", - "goto", - "package", - "synchronized", - "boolean", - "do", - "if", - "private", - "this", - "break", - "double", - "implements", - "protected", - "throw", - "byte", - "else", - "import", - "public", - "throws", - "case", - "enum", - "instanceof", - "return", - "transient", - "catch", - "extends", - "int", - "short", - "try", - "char", - "final", - "interface", - "static", - "void", - "class", - "finally", - "long", - "strictfp", - "volatile", - "const", - "float", - "native", - "super", - "while")); - } - - private ClassUtils() { - } - - public static Class getClassByName(ClassLoader classLoader, String clsName) { - if (classLoader == null) { - classLoader = Thread.currentThread().getContextClassLoader(); - } - try { - return classLoader.loadClass(clsName); - } catch (ClassNotFoundException e) { - return null; - } - } - - // 获取modelImpl对应的class - public static Class getOrCreateClass(ClassLoader classLoader, String packageName, Swagger swagger, - Map properties, - String clsName) { - Class cls = getClassByName(classLoader, clsName); - if (cls != null) { - return cls; - } - - ClassConfig classConfig = new ClassConfig(); - classConfig.setClassName(clsName); - - for (Entry entry : properties.entrySet()) { - JavaType propertyJavaType = - ConverterMgr.findJavaType(classLoader, - packageName, - swagger, - entry.getValue()); - classConfig.addField(entry.getKey(), propertyJavaType); - } - - cls = JavassistUtils.createClass(classConfig); - return cls; - } - - // 将一系列body parameter包装成一个class - public static Class getOrCreateBodyClass(OperationGenerator operationGenerator, - List bodyParameters) { - SwaggerGenerator swaggerGenerator = operationGenerator.getSwaggerGenerator(); - Method method = operationGenerator.getProviderMethod(); - String clsName = swaggerGenerator.ensureGetPackageName() + "." + method.getName() + "Body"; - Class cls = getClassByName(swaggerGenerator.getClassLoader(), clsName); - if (cls != null) { - return cls; - } - - ClassConfig classConfig = new ClassConfig(); - classConfig.setClassName(clsName); - - // 1.全是预备body - // 2.预备body与明确body混合 - for (BodyParameter bp : bodyParameters) { - JavaType javaType = ConverterMgr.findJavaType(swaggerGenerator.getClassLoader(), - swaggerGenerator.ensureGetPackageName(), - swaggerGenerator.getSwagger(), - bp); - classConfig.addField(bp.getName(), javaType); - } - - return JavassistUtils.createClass(classConfig); - } - - public static boolean hasAnnotation(Class cls, Class annotation) { - if (cls.getAnnotation(annotation) != null) { - return true; - } - - for (Method method : cls.getMethods()) { - if (method.getAnnotation(annotation) != null) { - return true; - } - } - - return false; - } - - public static boolean isRawJsonType(Parameter param) { - Object rawJson = param.getVendorExtensions().get(SwaggerConst.EXT_RAW_JSON_TYPE); - if (Boolean.class.isInstance(rawJson)) { - return (boolean) rawJson; - } - return false; - } - - public static Class getJavaInterface(Swagger swagger) { - return getClassByVendorExtensions(null, swagger.getInfo().getVendorExtensions(), SwaggerConst.EXT_JAVA_INTF); - } - - public static Class getClassByVendorExtensions(ClassLoader classLoader, Map vendorExtensions, - String clsKey) { - if (vendorExtensions == null) { - return null; - } - - String clsName = (String) vendorExtensions.get(clsKey); - if (StringUtils.isEmpty(clsName)) { - return null; - } - - return getClassByName(classLoader, clsName); - } - - public static Class getOrCreateInterface(SwaggerGenerator generator) { - return getOrCreateInterface(generator.getSwagger(), - generator.getClassLoader(), - generator.ensureGetPackageName()); - } - - public static Class getOrCreateInterface(Swagger swagger, ClassLoader classLoader, String packageName) { - String intfName = - (String) swagger.getInfo().getVendorExtensions().get(SwaggerConst.EXT_JAVA_INTF); - Class intf = getClassByName(classLoader, intfName); - if (intf != null) { - return intf; - } - - if (packageName == null) { - int idx = intfName.lastIndexOf("."); - if (idx == -1) { - packageName = ""; - } else { - packageName = intfName.substring(0, idx); - } - } - return createInterface(swagger, classLoader, packageName, intfName); - } - - private static Class createInterface(Swagger swagger, ClassLoader classLoader, String packageName, - String intfName) { - ClassConfig classConfig = new ClassConfig(); - classConfig.setClassName(intfName); - classConfig.setIntf(true); - - for (Path path : swagger.getPaths().values()) { - for (Operation operation : path.getOperations()) { - // 参数可能重名,所以packageName必须跟operation相关才能隔离 - String opPackageName = packageName + "." + operation.getOperationId(); - - Response result = operation.getResponses().get(SwaggerConst.SUCCESS_KEY); - JavaType resultJavaType = ConverterMgr.findJavaType(classLoader, - opPackageName, - swagger, - result.getSchema()); - - MethodConfig methodConfig = new MethodConfig(); - methodConfig.setName(operation.getOperationId()); - methodConfig.setResult(resultJavaType); - - for (Parameter parameter : operation.getParameters()) { - String paramName = parameter.getName(); - paramName = correctMethodParameterName(paramName); - - JavaType paramJavaType = ConverterMgr.findJavaType(classLoader, - opPackageName, - swagger, - parameter); - methodConfig.addParameter(paramName, paramJavaType); - } - - classConfig.addMethod(methodConfig); - } - } - - return JavassistUtils.createClass(classLoader, classConfig); - } - - public static String correctMethodParameterName(String name) { - return name.replace(".", "_").replace("-", "_"); - } - - public static String correctClassName(String name) { - String parts[] = name.split("\\.", -1); - for (int idx = 0; idx < parts.length; idx++) { - String part = parts[idx]; - if (part.isEmpty()) { - parts[idx] = "_"; - continue; - } - - part = part.replace('-', '_'); - if (Character.isDigit(part.charAt(0)) || JAVA_RESERVED_WORDS.contains(part)) { - part = "_" + part; - } - parts[idx] = part; - } - return String.join(".", parts); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/utils/ParamUtils.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/utils/ParamUtils.java deleted file mode 100644 index 2372c25d782..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/core/utils/ParamUtils.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.utils; - -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.core.DefaultParameterNameDiscoverer; -import org.springframework.core.MethodParameter; - -import com.fasterxml.jackson.databind.ser.std.MapProperty; - -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.swagger.converter.ModelConverters; -import io.swagger.models.Model; -import io.swagger.models.Swagger; -import io.swagger.models.parameters.AbstractSerializableParameter; -import io.swagger.models.parameters.BodyParameter; -import io.swagger.models.parameters.Parameter; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.ObjectProperty; -import io.swagger.models.properties.Property; -import io.swagger.models.properties.PropertyBuilder; -import io.swagger.models.properties.RefProperty; - -public final class ParamUtils { - private static DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); - - private ParamUtils() { - - } - - // 如果existName为empty,则通过原型查找 - public static String getParameterName(String existName, Method method, int paramIdx) { - if (StringUtils.isEmpty(existName)) { - existName = getParameterName(method, paramIdx); - } - - return existName; - } - - public static String getParameterName(Method method, int paramIdx) { - MethodParameter methodParameter = new MethodParameter(method, paramIdx); - methodParameter.initParameterNameDiscovery(parameterNameDiscoverer); - - String paramName = methodParameter.getParameterName(); - if (paramName == null) { - // 小于jdk8的场景中,即使有debug参数,也无法对着interface获取参数名,此时直接使用arg + paramIndex来表示 - paramName = "arg" + paramIdx; - } - return paramName; - } - - public static Type getGenericParameterType(Method method, int paramIdx) { - return method.getGenericParameterTypes()[paramIdx]; - } - - public static String generateBodyParameterName(Method method) { - return method.getName() + "Body"; - } - - public static BodyParameter createBodyParameter(OperationGenerator operationGenerator, - int paramIdx) { - Method method = operationGenerator.getProviderMethod(); - String paramName = getParameterName(method, paramIdx); - Type paramType = getGenericParameterType(method, paramIdx); - return createBodyParameter(operationGenerator.getSwagger(), paramName, paramType); - } - - public static BodyParameter createBodyParameter(Swagger swagger, String paramName, Type paramType) { - addDefinitions(swagger, paramType); - - Property property = ModelConverters.getInstance().readAsProperty(paramType); - Model model = PropertyBuilder.toModel(property); - - BodyParameter bodyParameter = new BodyParameter(); - bodyParameter.setName(paramName); - bodyParameter.setSchema(model); - - return bodyParameter; - } - - public static void addDefinitions(Swagger swagger, Type paramType) { - Map models = ModelConverters.getInstance().readAll(paramType); - for (Map.Entry entry : models.entrySet()) { - swagger.addDefinition(entry.getKey(), entry.getValue()); - } - } - - public static void setParameterType(Swagger swagger, Method method, int paramIdx, - AbstractSerializableParameter parameter) { - Type paramType = ParamUtils.getGenericParameterType(method, paramIdx); - - ParamUtils.addDefinitions(swagger, paramType); - - Property property = ModelConverters.getInstance().readAsProperty(paramType); - - if (isComplexProperty(property)) { - // 简单参数不可以是复杂类型 - String msg = String.format("not allow complex type for %s parameter, method=%s:%s, paramIdx=%d, type=%s", - parameter.getIn(), - method.getDeclaringClass().getName(), - method.getName(), - paramIdx, - paramType.getTypeName()); - throw new Error(msg); - } - parameter.setProperty(property); - } - - public static boolean isComplexProperty(Property property) { - if (RefProperty.class.isInstance(property) || ObjectProperty.class.isInstance(property) - || MapProperty.class.isInstance(property)) { - return true; - } - - if (ArrayProperty.class.isInstance(property)) { - return isComplexProperty(((ArrayProperty) property).getItems()); - } - - return false; - } - - public static int findParameterByName(String name, List parameterList) { - for (int idx = 0; idx < parameterList.size(); idx++) { - Parameter parameter = parameterList.get(idx); - if (name.equals(parameter.getName())) { - return idx; - } - } - - return -1; - } - - public static boolean isRealBodyParameter(Parameter parameter) { - return BodyParameter.class.getName().equals(parameter.getClass().getName()); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/PojoSwaggerGeneratorContext.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/PojoSwaggerGeneratorContext.java deleted file mode 100644 index 3785ccf2b3f..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/PojoSwaggerGeneratorContext.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.pojo; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -import javax.ws.rs.HttpMethod; - -import org.apache.commons.lang3.StringUtils; - -import io.servicecomb.swagger.converter.ConverterMgr; -import io.servicecomb.swagger.generator.core.AbstractSwaggerGeneratorContext; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.servicecomb.swagger.generator.core.utils.ParamUtils; -import io.servicecomb.swagger.generator.pojo.converter.parameter.PendingBodyParameterConverter; -import io.servicecomb.swagger.generator.pojo.extend.parameter.PendingBodyParameter; -import io.servicecomb.swagger.generator.pojo.processor.parameter.PojoDefaultParameterProcessor; -import io.swagger.models.parameters.BodyParameter; -import io.swagger.models.parameters.Parameter; - -public class PojoSwaggerGeneratorContext extends AbstractSwaggerGeneratorContext { - static { - ConverterMgr.addConverter(PendingBodyParameter.class, new PendingBodyParameterConverter()); - } - - @Override - public int getOrder() { - return Integer.MAX_VALUE; - } - - @Override - public boolean canProcess(Class cls) { - return true; - } - - @Override - public boolean canProcess(Method method) { - return true; - } - - @Override - protected void initDefaultParameterProcessor() { - defaultParameterProcessor = new PojoDefaultParameterProcessor(); - } - - protected void correctPath(OperationGenerator operationGenerator) { - String path = operationGenerator.getPath(); - if (StringUtils.isEmpty(path)) { - path = "/" + operationGenerator.getOperation().getOperationId(); - } - operationGenerator.setPath(path); - } - - // 必须全是body,或全是pending,如果是混合的,直接报错 - protected void handlePendingBody(OperationGenerator operationGenerator) { - List bodyParameters = collectBodyBasedParameters(operationGenerator); - if (bodyParameters.isEmpty()) { - return; - } - - if (bodyParameters.size() == 1) { - Parameter bodyParameter = bodyParameters.get(0); - replaceBodyBasedParameter(operationGenerator, bodyParameter); - return; - } - - // 将多个pending合并成一个body - mergeBodyBasedParameters(operationGenerator, bodyParameters); - } - - protected List collectBodyBasedParameters(OperationGenerator operationGenerator) { - List bodyParameters = new ArrayList<>(); - for (Parameter parameter : operationGenerator.getSwaggerParameters()) { - if (BodyParameter.class.isInstance(parameter)) { - bodyParameters.add((BodyParameter) parameter); - } - } - return bodyParameters; - } - - protected void replaceBodyBasedParameter(OperationGenerator operationGenerator, Parameter bodyBasedParameter) { - if (ParamUtils.isRealBodyParameter(bodyBasedParameter)) { - return; - } - - List swaggerParameters = operationGenerator.getSwaggerParameters(); - int idx = swaggerParameters.indexOf(bodyBasedParameter); - String bodyParamName = bodyBasedParameter.getName(); - BodyParameter bodyParameter = ((PendingBodyParameter) bodyBasedParameter).createBodyParameter(bodyParamName); - swaggerParameters.set(idx, bodyParameter); - } - - protected void mergeBodyBasedParameters(OperationGenerator operationGenerator, - List bodyParameters) { - List swaggerParameters = operationGenerator.getSwaggerParameters(); - for (Parameter parameter : bodyParameters) { - swaggerParameters.remove(parameter); - } - - // 将这些body包装为一个class,整体做为一个body参数 - String bodyParamName = ParamUtils.generateBodyParameterName(operationGenerator.getProviderMethod()); - Class cls = ClassUtils.getOrCreateBodyClass(operationGenerator, bodyParameters); - BodyParameter bodyParameter = - ParamUtils.createBodyParameter(operationGenerator.getSwagger(), bodyParamName, cls); - swaggerParameters.add(bodyParameter); - } - - protected void correctHttpMethod(OperationGenerator operationGenerator) { - if (StringUtils.isEmpty(operationGenerator.getHttpMethod())) { - operationGenerator.setHttpMethod(HttpMethod.POST); - } - } - - @Override - public void postProcessOperation(OperationGenerator operationGenerator) { - correctPath(operationGenerator); - correctHttpMethod(operationGenerator); - handlePendingBody(operationGenerator); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/converter/parameter/PendingBodyParameterConverter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/converter/parameter/PendingBodyParameterConverter.java deleted file mode 100644 index 460705f3f98..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/converter/parameter/PendingBodyParameterConverter.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.pojo.converter.parameter; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.swagger.converter.Converter; -import io.servicecomb.swagger.converter.ConverterMgr; -import io.servicecomb.swagger.generator.pojo.extend.parameter.PendingBodyParameter; -import io.swagger.models.Swagger; - -public class PendingBodyParameterConverter implements Converter { - - @Override - public JavaType convert(ClassLoader classLoader, String packageName, Swagger swagger, Object def) { - PendingBodyParameter param = (PendingBodyParameter) def; - return ConverterMgr.findJavaType(classLoader, packageName, swagger, param.getProperty()); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/extend/parameter/PendingBodyParameter.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/extend/parameter/PendingBodyParameter.java deleted file mode 100644 index 17866b5d698..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/extend/parameter/PendingBodyParameter.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.pojo.extend.parameter; - -import java.lang.reflect.Method; -import java.lang.reflect.Type; - -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.core.utils.ParamUtils; -import io.swagger.models.ModelImpl; -import io.swagger.models.RefModel; -import io.swagger.models.parameters.BodyParameter; -import io.swagger.models.properties.Property; -import io.swagger.models.properties.PropertyBuilder; -import io.swagger.models.properties.RefProperty; - -// -// 备选body -// int test(int x, Set y) -// 反向生成契约时,x、y都是备选body,最终再合并成body -// 如果直接使用BodyParamter,会导致y有信息丢失 -// 只在透明rpc模式中使用 -// jaxrs、springmvc这种模式,要求符合模式本身的定义场景,不允许随意组合 -// -public class PendingBodyParameter extends BodyParameter { - private OperationGenerator operationGenerator; - - private Property property; - - private Type type; - - public void setOperationGenerator(OperationGenerator operationGenerator) { - this.operationGenerator = operationGenerator; - } - - public Property getProperty() { - return property; - } - - public void setProperty(Property property) { - this.property = property; - } - - public Type getType() { - return type; - } - - public void setType(Type type) { - this.type = type; - } - - public Method getMethod() { - return operationGenerator.getProviderMethod(); - } - - public BodyParameter createBodyParameter(String paramName) { - String modelType = ParamUtils.generateBodyParameterName(operationGenerator.getProviderMethod()); - RefModel model = toRefModel(modelType); - - BodyParameter bodyParameter = new BodyParameter(); - bodyParameter.setName(paramName); - bodyParameter.setSchema(model); - - return bodyParameter; - } - - // swagger中的body只能是ref,不能是简单类型 - private RefModel toRefModel(String modelType) { - if (RefProperty.class.isInstance(property)) { - return (RefModel) PropertyBuilder.toModel(property); - } - - ModelImpl modelImpl = new ModelImpl(); - modelImpl.setType("object"); - modelImpl.setName(name); - modelImpl.addProperty(name, property); - - operationGenerator.getSwagger().addDefinition(modelType, modelImpl); - - RefModel refModel = new RefModel(); - refModel.setReference("#/definitions/" + modelType); - - return refModel; - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/package-info.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/package-info.java deleted file mode 100644 index 84fd3a860a4..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/package-info.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * 透明rpc相关功能独立project比较合适,可是core的ut就太麻烦了 - */ -package io.servicecomb.swagger.generator.pojo; diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/processor/parameter/PojoDefaultParameterProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/processor/parameter/PojoDefaultParameterProcessor.java deleted file mode 100644 index b279288c702..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/processor/parameter/PojoDefaultParameterProcessor.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.pojo.processor.parameter; - -import io.servicecomb.swagger.generator.core.DefaultParameterProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.pojo.extend.parameter.PendingBodyParameter; -import io.servicecomb.swagger.generator.pojo.utils.PojoParamUtils; - -public class PojoDefaultParameterProcessor implements DefaultParameterProcessor { - - @Override - public void process(OperationGenerator operationGenerator, int paramIndex) { - PendingBodyParameter pendingBodyParameter = - PojoParamUtils.createPendingBodyParameter(operationGenerator, paramIndex); - operationGenerator.addProviderParameter(pendingBodyParameter); - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/utils/PojoParamUtils.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/utils/PojoParamUtils.java deleted file mode 100644 index 99a250e61dd..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/pojo/utils/PojoParamUtils.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.pojo.utils; - -import java.lang.reflect.Method; -import java.lang.reflect.Type; - -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.core.utils.ParamUtils; -import io.servicecomb.swagger.generator.pojo.extend.parameter.PendingBodyParameter; -import io.swagger.converter.ModelConverters; -import io.swagger.models.properties.Property; - -public final class PojoParamUtils { - private PojoParamUtils() { - } - - public static PendingBodyParameter createPendingBodyParameter(OperationGenerator operationGenerator, - int paramIdx) { - Method method = operationGenerator.getProviderMethod(); - String paramName = ParamUtils.getParameterName(method, paramIdx); - Type paramType = ParamUtils.getGenericParameterType(method, paramIdx); - return createPendingBodyParameter(operationGenerator, paramName, paramType); - } - - public static PendingBodyParameter createPendingBodyParameter(OperationGenerator operationGenerator, - String paramName, Type paramType) { - ParamUtils.addDefinitions(operationGenerator.getSwagger(), paramType); - Property property = ModelConverters.getInstance().readAsProperty(paramType); - - PendingBodyParameter pendingBodyParameter = new PendingBodyParameter(); - pendingBodyParameter.setName(paramName); - pendingBodyParameter.setProperty(property); - pendingBodyParameter.setType(paramType); - pendingBodyParameter.setOperationGenerator(operationGenerator); - - return pendingBodyParameter; - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/rest/RestSwaggerGeneratorContext.java b/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/rest/RestSwaggerGeneratorContext.java deleted file mode 100644 index 410eab63a50..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/io/servicecomb/swagger/generator/rest/RestSwaggerGeneratorContext.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.rest; - -import org.springframework.util.StringUtils; - -import io.servicecomb.swagger.generator.core.AbstractSwaggerGeneratorContext; -import io.servicecomb.swagger.generator.core.OperationGenerator; - -public abstract class RestSwaggerGeneratorContext extends AbstractSwaggerGeneratorContext { - protected void correctPath(OperationGenerator operationGenerator) { - String path = operationGenerator.getPath(); - if (StringUtils.isEmpty(path)) { - path = "/"; - } - operationGenerator.setPath(path); - } - - @Override - public void postProcessOperation(OperationGenerator operationGenerator) { - checkPath(operationGenerator); - correctPath(operationGenerator); - } - - protected void checkPath(OperationGenerator operationGenerator) { - if (StringUtils.isEmpty(operationGenerator.getPath()) - && StringUtils.isEmpty(operationGenerator.getSwagger().getBasePath())) { - throw new Error("Path must not both be empty in class and method"); - } - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/SwaggerUtils.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/SwaggerUtils.java new file mode 100644 index 00000000000..c1751b728a8 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/SwaggerUtils.java @@ -0,0 +1,468 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger; + +import java.io.File; +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.common.base.DynamicEnum; +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.springframework.util.CollectionUtils; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverters; +import io.swagger.v3.core.converter.ResolvedSchema; +import io.swagger.v3.core.util.Yaml; +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.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.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; +import io.swagger.v3.oas.models.servers.Server; +import jakarta.servlet.http.Part; + +@SuppressWarnings("rawtypes") +public final class SwaggerUtils { + private SwaggerUtils() { + } + + /** + * Only ones servers and contains only base path. + */ + public static String getBasePath(OpenAPI swagger) { + if (swagger.getServers() == null || swagger.getServers().size() == 0) { + return null; + } + return swagger.getServers().get(0).getUrl(); + } + + /** + * Only ones servers and contains only base path. + */ + public static void setBasePath(OpenAPI swagger, String basePath) { + if (swagger.getServers() == null) { + swagger.setServers(new ArrayList<>()); + } + if (swagger.getServers().size() == 0) { + swagger.getServers().add(new Server()); + } + swagger.getServers().get(0).setUrl(basePath); + } + + public static String swaggerToString(OpenAPI swagger) { + try { + return Yaml.mapper().writeValueAsString(swagger); + } catch (Throwable e) { + throw new ServiceCombException("Convert swagger to string failed, ", e); + } + } + + public static OpenAPI parseAndValidateSwagger(URL url) { + try { + String swaggerContent = IOUtils.toString(url, StandardCharsets.UTF_8); + OpenAPI result = internalParseSwagger(swaggerContent); + validateSwagger(result); + return result; + } catch (Throwable e) { + throw new ServiceCombException("Parse swagger from url failed, url=" + url, e); + } + } + + public static OpenAPI parseSwagger(String swaggerContent) { + try { + return internalParseSwagger(swaggerContent); + } catch (Throwable e) { + throw new ServiceCombException("Parse swagger from content failed, ", e); + } + } + + private static void validateSwagger(OpenAPI openAPI) { + if (openAPI.getPaths() == null) { + return; + } + for (PathItem pathItem : openAPI.getPaths().values()) { + if (pathItem.getGet() != null) { + validateOperation(pathItem.getGet()); + } + if (pathItem.getPost() != null) { + validateOperation(pathItem.getPost()); + } + if (pathItem.getDelete() != null) { + validateOperation(pathItem.getDelete()); + } + if (pathItem.getPut() != null) { + validateOperation(pathItem.getPut()); + } + if (pathItem.getPatch() != null) { + validateOperation(pathItem.getPatch()); + } + } + } + + private static void validateOperation(Operation operation) { + if (operation.getRequestBody() != null) { + validateRequestBody(operation.getRequestBody()); + } + if (operation.getParameters() != null) { + validateParameters(operation.getParameters()); + } + } + + private static void validateParameters(List parameters) { + for (Parameter parameter : parameters) { + if (parameter == null) { + throw new ServiceCombException("Parameter can not be null."); + } + if (StringUtils.isEmpty(parameter.getName())) { + throw new ServiceCombException("Parameter name is required."); + } + } + } + + private static void validateRequestBody(RequestBody requestBody) { + for (String contentType : requestBody.getContent().keySet()) { + if (SwaggerConst.FILE_MEDIA_TYPE.equals(contentType) || SwaggerConst.FORM_MEDIA_TYPE.equals(contentType)) { + continue; + } + if (requestBody.getExtensions() == null) { + throw new ServiceCombException("Request body x-name extension is required."); + } + if (StringUtils.isEmpty((String) requestBody.getExtensions().get(SwaggerConst.EXT_BODY_NAME))) { + throw new ServiceCombException("Request body x-name extension is required."); + } + break; + } + } + + private static OpenAPI internalParseSwagger(String swaggerContent) throws IOException { + return Yaml.mapper().readValue(swaggerContent, OpenAPI.class); + } + + // add descriptions to response and add a default response if absent. + public static void correctResponses(Operation operation) { + if (operation.getResponses() == null) { + operation.setResponses(new ApiResponses()); + } + if (operation.getResponses().size() == 0) { + operation.getResponses().addApiResponse(SwaggerConst.SUCCESS_KEY, new ApiResponse()); + } + + for (Entry responseEntry : operation.getResponses().entrySet()) { + ApiResponse response = responseEntry.getValue(); + if (StringUtils.isEmpty(response.getDescription())) { + response.setDescription("response of " + responseEntry.getKey()); + } + } + } + + public static void correctResponses(OpenAPI swagger) { + if (swagger.getPaths() == null) { + return; + } + + for (PathItem path : swagger.getPaths().values()) { + for (Operation operation : path.readOperations()) { + correctResponses(operation); + } + } + } + + public static Schema resolveTypeSchemas(OpenAPI swagger, Type type) { + ResolvedSchema resolvedSchema = ModelConverters.getInstance().resolveAsResolvedSchema( + new AnnotatedType(type).resolveAsRef(true)); + + if (resolvedSchema == null || resolvedSchema.schema == null) { + throw new IllegalArgumentException("cannot resolve type : " + type); + } + + if (swagger.getComponents() == null) { + swagger.setComponents(new Components()); + } + + Map schemaMap = resolvedSchema.referencedSchemas; + if (!CollectionUtils.isEmpty(schemaMap)) { + Map componentSchemas = swagger.getComponents().getSchemas(); + if (componentSchemas == null) { + componentSchemas = new LinkedHashMap<>(schemaMap); + } else { + for (Map.Entry entry : schemaMap.entrySet()) { + if (!componentSchemas.containsKey(entry.getKey())) { + componentSchemas.put(entry.getKey(), entry.getValue()); + } else { + if (!schemaEquals(entry.getValue(), componentSchemas.get(entry.getKey()))) { + throw new IllegalArgumentException("duplicate param model: " + entry.getKey()); + } + } + } + } + swagger.getComponents().setSchemas(componentSchemas); + } + return resolvedSchema.schema; + } + + // swagger api equals method will compare Map address(extensions) + // and is not applicable for usage. + public static int schemaHashCode(Schema schema) { + int result = schema.getType() != null ? schema.getType().hashCode() : 0; + result = result ^ (schema.getFormat() != null ? schema.getFormat().hashCode() : 0); + result = result ^ (schema.getName() != null ? schema.getName().hashCode() : 0); + result = result ^ (schema.get$ref() != null ? schema.get$ref().hashCode() : 0); + result = result ^ (schema.getItems() != null ? schemaHashCode(schema.getItems()) : 0); + result = result ^ (schema.getAdditionalProperties() != null ? + schemaHashCode((Schema) schema.getAdditionalProperties()) : 0); + result = result ^ (schema.getProperties() != null ? propertiesHashCode(schema.getProperties()) : 0); + return result; + } + + private static int propertiesHashCode(Map properties) { + int result = 0; + for (Entry entry : properties.entrySet()) { + result = result ^ (entry.getKey().hashCode() ^ schemaHashCode(entry.getValue())); + } + return result; + } + + // swagger api equals method will compare Map address(extensions) + // and is not applicable for usage. + public static boolean schemaEquals(Schema schema1, Schema schema2) { + if (schema1 == null && schema2 == null) { + return true; + } + if (schema1 == null || schema2 == null) { + return false; + } + return StringUtils.equals(schema1.getType(), schema2.getType()) + && StringUtils.equals(schema1.getFormat(), schema2.getFormat()) + && StringUtils.equals(schema1.getName(), schema2.getName()) + && StringUtils.equals(schema1.get$ref(), schema2.get$ref()) + && schemaEquals(schema1.getItems(), schema2.getItems()) + && schemaEquals((Schema) schema1.getAdditionalProperties(), (Schema) schema2.getAdditionalProperties()) + && propertiesEquals(schema1.getProperties(), schema2.getProperties()) + && extensionEquals(schema1.getExtensions(), schema2.getExtensions()); + } + + private static boolean extensionEquals(Map extensions1, Map extensions2) { + if (extensions1 == null && extensions2 == null) { + return true; + } + if (extensions1 == null || extensions2 == null) { + return false; + } + if (extensions1.size() != extensions2.size()) { + return false; + } + boolean result = true; + for (Entry item : extensions1.entrySet()) { + if (!Objects.equals(item.getValue(), extensions2.get(item.getKey()))) { + result = false; + break; + } + } + return result; + } + + public static boolean propertiesEquals(Map properties1, Map properties2) { + if (properties1 == null && properties2 == null) { + return true; + } + if (properties1 == null || properties2 == null) { + return false; + } + if (properties1.size() != properties2.size()) { + return false; + } + boolean result = true; + for (Entry item : properties1.entrySet()) { + if (!schemaEquals(item.getValue(), properties2.get(item.getKey()))) { + result = false; + break; + } + } + return result; + } + + public static Schema getSchema(OpenAPI swagger, String ref) { + return swagger.getComponents().getSchemas().get(ref.substring(Components.COMPONENTS_SCHEMAS_REF.length())); + } + + public static String getSchemaName(String ref) { + return ref.substring(Components.COMPONENTS_SCHEMAS_REF.length()); + } + + public static Schema getSchema(OpenAPI swagger, Schema ref) { + if (ref == null) { + return null; + } + if (ref.get$ref() != null) { + return getSchema(swagger, ref.get$ref()); + } + return ref; + } + + public static boolean hasAnnotation(Class cls, Class annotation) { + if (cls.getAnnotation(annotation) != null) { + return true; + } + + for (Method method : cls.getMethods()) { + if (method.getAnnotation(annotation) != null) { + return true; + } + } + + return false; + } + + public static boolean isRawJsonType(RequestBody param) { + if (param.getExtensions() == null) { + return false; + } + Object rawJson = param.getExtensions().get(SwaggerConst.EXT_RAW_JSON_TYPE); + if (rawJson instanceof Boolean) { + return (boolean) rawJson; + } + return false; + } + + public static String getClassName(Map vendorExtensions) { + return getVendorExtension(vendorExtensions, SwaggerConst.EXT_JAVA_CLASS); + } + + public static String getInterfaceName(Map vendorExtensions) { + return getVendorExtension(vendorExtensions, SwaggerConst.EXT_JAVA_INTF); + } + + @SuppressWarnings("unchecked") + public static T getVendorExtension(Map vendorExtensions, String key) { + if (vendorExtensions == null) { + return null; + } + + return (T) vendorExtensions.get(key); + } + + public static boolean isBean(Type type) { + if (type == null) { + return false; + } + + JavaType javaType = TypeFactory.defaultInstance().constructType(type); + if (javaType.isContainerType() || javaType.isEnumType() || javaType.isTypeOrSubTypeOf(DynamicEnum.class)) { + return false; + } + + Class cls = javaType.getRawClass(); + if (ClassUtils.isPrimitiveOrWrapper(cls)) { + return false; + } + + return (cls != String.class + && cls != Date.class + && cls != LocalDate.class + && cls != LocalDateTime.class + && cls != byte[].class + && cls != File.class + && cls != BigInteger.class + && cls != BigDecimal.class + && !cls.getName().equals("org.springframework.web.multipart.MultipartFile") + && !Part.class.isAssignableFrom(cls)); + } + + public static void updateProduces(Operation operation, String[] produces) { + if (produces == null || produces.length == 0) { + return; + } + if (operation.getResponses() == null) { + operation.setResponses(new ApiResponses()); + } + if (operation.getResponses().size() == 0) { + operation.getResponses().addApiResponse(SwaggerConst.SUCCESS_KEY, new ApiResponse()); + } + for (String produce : produces) { + operation.getResponses().forEach((k, v) -> { + if (v.getContent() == null) { + v.setContent(new Content()); + } + if (v.getContent().get(produce) == null) { + v.getContent().addMediaType(produce, new MediaType()); + } + }); + } + } + + public static boolean methodExists(PathItem pathItem, String httpMethod) { + PathItem.HttpMethod method = PathItem.HttpMethod.valueOf(httpMethod); + return switch (method) { + case GET -> pathItem.getGet() != null; + case PUT -> pathItem.getPut() != null; + case POST -> pathItem.getPost() != null; + case PATCH -> pathItem.getPatch() != null; + case DELETE -> pathItem.getDelete() != null; + default -> false; + }; + } + + public static String concatAbsolutePath(OpenAPI swagger, String operationPath) { + String basePath = getBasePath(swagger); + return concatPath(basePath, operationPath); + } + + /** + * Concat the two paths to an absolute path, without end of '/'. + *

+ * e.g. "/" + "/ope" = /ope + * e.g. "/prefix" + "/ope" = /prefix/ope + */ + public static String concatPath(String basePath, String operationPath) { + return ("/" + nonNullify(basePath) + "/" + nonNullify(operationPath)) + .replaceAll("/{2,}", "/"); + } + + private static String nonNullify(String path) { + return path == null ? "" : path; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/AbstractConverter.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/AbstractConverter.java new file mode 100644 index 00000000000..31299f128a6 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/AbstractConverter.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.swagger.converter; + +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.SwaggerUtils; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; + +@SuppressWarnings("rawtypes") +public abstract class AbstractConverter implements Converter { + protected abstract Map findVendorExtensions(Object def); + + protected abstract JavaType doConvert(OpenAPI swagger, Schema def); + + @Override + public JavaType convert(OpenAPI swagger, Schema def) { + Map vendorExtensions = findVendorExtensions(def); + String canonical = SwaggerUtils.getClassName(vendorExtensions); + if (StringUtils.isEmpty(canonical)) { + return doConvert(swagger, def); + } + + try { + return TypeFactory.defaultInstance().constructFromCanonical(canonical); + } catch (Throwable e) { + return doConvert(swagger, def); + } + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/Converter.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/Converter.java new file mode 100644 index 00000000000..66915d5dffd --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/Converter.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.swagger.converter; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; + +@SuppressWarnings("rawtypes") +public interface Converter { + JavaType OBJECT_JAVA_TYPE = TypeFactory.defaultInstance().constructType(Object.class); + + JavaType STRING_JAVA_TYPE = TypeFactory.defaultInstance().constructType(String.class); + + // def can be property or model + // def can not be null + JavaType convert(OpenAPI swagger, Schema def); +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/ConverterMgr.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/ConverterMgr.java new file mode 100644 index 00000000000..5705f2460b9 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/ConverterMgr.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.converter.property.ArrayPropertyConverter; +import org.apache.servicecomb.swagger.converter.property.MapPropertyConverter; +import org.apache.servicecomb.swagger.converter.property.ObjectPropertyConverter; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.MapSchema; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.media.Schema; +import jakarta.servlet.http.Part; + +@SuppressWarnings("rawtypes") +public final class ConverterMgr { + private static final JavaType VOID_JAVA_TYPE = TypeFactory.defaultInstance().constructType(Void.class); + + // key is "type.format" of standard swagger data type + // value is related java class + private static final Map TYPE_FORMAT_MAP = new HashMap<>(); + + private static final Map, Converter> converterMap = new HashMap<>(); + + static { + initTypeFormatMap(); + initConverters(); + } + + private static String genTypeFormatKey(String type, String format) { + return type + ":" + (format == null ? "" : format); + } + + private ConverterMgr() { + + } + + private static void initTypeFormatMap() { + TYPE_FORMAT_MAP.put(genTypeFormatKey("boolean", ""), + TypeFactory.defaultInstance().constructType(Boolean.class)); + TYPE_FORMAT_MAP.put(genTypeFormatKey("integer", "int32"), + TypeFactory.defaultInstance().constructType(Integer.class)); + TYPE_FORMAT_MAP.put(genTypeFormatKey("integer", "int64"), + TypeFactory.defaultInstance().constructType(Long.class)); + TYPE_FORMAT_MAP.put(genTypeFormatKey("integer", ""), + TypeFactory.defaultInstance().constructType(BigInteger.class)); + TYPE_FORMAT_MAP.put(genTypeFormatKey("number", "float"), + TypeFactory.defaultInstance().constructType(Float.class)); + TYPE_FORMAT_MAP.put(genTypeFormatKey("number", "double"), + TypeFactory.defaultInstance().constructType(Double.class)); + TYPE_FORMAT_MAP.put(genTypeFormatKey("number", ""), + TypeFactory.defaultInstance().constructType(BigDecimal.class)); + TYPE_FORMAT_MAP.put(genTypeFormatKey("string", ""), + TypeFactory.defaultInstance().constructType(String.class)); + TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "date"), + TypeFactory.defaultInstance().constructType(LocalDate.class)); + TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "date-time"), + TypeFactory.defaultInstance().constructType(LocalDateTime.class)); + TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "password"), + TypeFactory.defaultInstance().constructType(String.class)); + + TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "byte"), + TypeFactory.defaultInstance().constructType(Byte[].class)); + TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "binary"), + TypeFactory.defaultInstance().constructType(Part.class)); + } + + private static void initConverters() { + converterMap.put(ArraySchema.class, new ArrayPropertyConverter()); + converterMap.put(MapSchema.class, new MapPropertyConverter()); + converterMap.put(ObjectSchema.class, new ObjectPropertyConverter()); + converterMap.put(Schema.class, new ObjectPropertyConverter()); + } + + public static JavaType findJavaType(String type, String format) { + String key = genTypeFormatKey(type, format); + return TYPE_FORMAT_MAP.get(key); + } + + // def is null means void + public static JavaType findJavaType(OpenAPI swagger, Schema def) { + if (def == null) { + return VOID_JAVA_TYPE; + } + JavaType javaType = findJavaType(def.getType(), def.getFormat()); + if (javaType != null) { + return javaType; + } + Converter converter = converterMap.get(def.getClass()); + if (converter == null) { + throw new Error("not support def type: " + def.getClass()); + } + return converter.convert(swagger, def); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/AbstractPropertyConverter.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/AbstractPropertyConverter.java new file mode 100644 index 00000000000..81109f9e02e --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/AbstractPropertyConverter.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.swagger.converter.property; + +import java.util.Map; + +import org.apache.servicecomb.swagger.converter.AbstractConverter; + +import io.swagger.v3.oas.models.media.Schema; + +public abstract class AbstractPropertyConverter extends AbstractConverter { + protected Map findVendorExtensions(Object def) { + return ((Schema) def).getExtensions(); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ArrayPropertyConverter.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ArrayPropertyConverter.java new file mode 100644 index 00000000000..eaa287065f6 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ArrayPropertyConverter.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.swagger.converter.property; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.apache.servicecomb.swagger.converter.ConverterMgr; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.Schema; + +@SuppressWarnings("rawtypes") +public class ArrayPropertyConverter extends AbstractPropertyConverter { + public static JavaType findJavaType(OpenAPI swagger, Schema itemProperty, Boolean uniqueItems) { + JavaType itemJavaType = ConverterMgr.findJavaType(swagger, itemProperty); + + @SuppressWarnings("rawtypes") + Class collectionClass = List.class; + if (Boolean.TRUE.equals(uniqueItems)) { + collectionClass = Set.class; + } + return TypeFactory.defaultInstance().constructCollectionType(collectionClass, itemJavaType); + } + + @Override + public JavaType doConvert(OpenAPI swagger, Schema property) { + ArraySchema arrayProperty = (ArraySchema) property; + + return findJavaType(swagger, arrayProperty.getItems(), arrayProperty.getUniqueItems()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/MapPropertyConverter.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/MapPropertyConverter.java new file mode 100644 index 00000000000..0dc2a221e19 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/MapPropertyConverter.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.swagger.converter.property; + +import java.util.Map; + +import org.apache.servicecomb.swagger.converter.ConverterMgr; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.MapSchema; +import io.swagger.v3.oas.models.media.Schema; + +@SuppressWarnings("rawtypes") +public class MapPropertyConverter extends AbstractPropertyConverter { + @Override + public JavaType doConvert(OpenAPI swagger, Schema property) { + MapSchema mapProperty = (MapSchema) property; + Object valueProperty = mapProperty.getAdditionalProperties(); + if (valueProperty instanceof Boolean) { + return TypeFactory.defaultInstance().constructType(Boolean.class); + } + return findJavaType(swagger, (Schema) valueProperty); + } + + public static JavaType findJavaType(OpenAPI swagger, Schema valueProperty) { + JavaType valueJavaType = ConverterMgr.findJavaType(swagger, valueProperty); + return TypeFactory.defaultInstance().constructMapType(Map.class, STRING_JAVA_TYPE, valueJavaType); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ObjectPropertyConverter.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ObjectPropertyConverter.java new file mode 100644 index 00000000000..3564ca4bd55 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ObjectPropertyConverter.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.swagger.converter.property; + +import java.util.Map; + +import org.apache.servicecomb.swagger.converter.AbstractConverter; + +import com.fasterxml.jackson.databind.JavaType; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; + +@SuppressWarnings("rawtypes") +public class ObjectPropertyConverter extends AbstractConverter { + + @Override + protected Map findVendorExtensions(Object def) { + return ((Schema) def).getExtensions(); + } + + @Override + protected JavaType doConvert(OpenAPI swagger, Schema def) { + return OBJECT_JAVA_TYPE; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ConcreteTypeRegister.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ConcreteTypeRegister.java new file mode 100644 index 00000000000..fe35b2c32c1 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ConcreteTypeRegister.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.swagger.extend; + +import java.lang.reflect.Type; +import java.util.Set; + +public interface ConcreteTypeRegister { + void register(Set types); +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/DefaultModelResolveObjectMapperProvider.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/DefaultModelResolveObjectMapperProvider.java new file mode 100644 index 00000000000..4b01f22c082 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/DefaultModelResolveObjectMapperProvider.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.swagger.extend; + +import org.apache.servicecomb.swagger.extend.module.EnumModuleExt; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +import io.swagger.v3.core.util.Json; + + +public class DefaultModelResolveObjectMapperProvider implements ModelResolveObjectMapperProvider { + @Override + public int getOrder() { + return 100; + } + + @Override + public ObjectMapper getMapper() { + ObjectMapper mapper = Json.mapper(); + mapper.registerModule(new EnumModuleExt()); + mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, false); + return mapper; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolveObjectMapperProvider.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolveObjectMapperProvider.java new file mode 100644 index 00000000000..68e05da4846 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolveObjectMapperProvider.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.swagger.extend; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Implement this interface to create custom {@link ObjectMapper} for Swagger schema generation. + */ +public interface ModelResolveObjectMapperProvider { + /** + * @return order The smaller value it is, the higher priority this provider has. + */ + default int getOrder() { + return 0; + } + + /** + * @return the ObjectMapper, which is used by Swagger to introspect the param types and generate property collections. + */ + ObjectMapper getMapper(); +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolverExt.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolverExt.java new file mode 100644 index 00000000000..8caec2dd15e --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolverExt.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.extend; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.common.base.DynamicEnum; +import org.apache.servicecomb.foundation.common.base.EnumUtils; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.extend.property.creator.ByteArrayPropertyCreator; +import org.apache.servicecomb.swagger.extend.property.creator.BytePropertyCreator; +import org.apache.servicecomb.swagger.extend.property.creator.InputStreamPropertyCreator; +import org.apache.servicecomb.swagger.extend.property.creator.PartPropertyCreator; +import org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator; +import org.apache.servicecomb.swagger.generator.SwaggerConst; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverter; +import io.swagger.v3.core.converter.ModelConverterContext; +import io.swagger.v3.core.jackson.ModelResolver; +import io.swagger.v3.core.util.PrimitiveType; +import io.swagger.v3.oas.models.media.NumberSchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.media.StringSchema; + +/** + * Customize swagger model converters to support: + * + * 1. byte and byte[] related types + * 2. stream related types + * 3. jason mapper customization + * 4. add x-java-class to model + */ +@SuppressWarnings({"unchecked", "rawtypes"}) +public class ModelResolverExt extends ModelResolver { + private final Map propertyCreatorMap = new HashMap<>(); + + private final Set concreteInterfaces = new HashSet<>(); + + public ModelResolverExt() { + super(findMapper()); + + addPropertyCreator(new BytePropertyCreator()); + addPropertyCreator(new ByteArrayPropertyCreator()); + + addPropertyCreator(new InputStreamPropertyCreator()); + addPropertyCreator(new PartPropertyCreator()); + + SPIServiceUtils.getAllService(PropertyCreator.class) + .forEach(this::addPropertyCreator); + SPIServiceUtils.getAllService(ConcreteTypeRegister.class) + .forEach(r -> r.register(concreteInterfaces)); + } + + private static ObjectMapper findMapper() { + ModelResolveObjectMapperProvider objectMapperProvider = SPIServiceUtils + .getPriorityHighestService(ModelResolveObjectMapperProvider.class); + if (null == objectMapperProvider) { + objectMapperProvider = new DefaultModelResolveObjectMapperProvider(); + } + return objectMapperProvider.getMapper(); + } + + private void addPropertyCreator(PropertyCreator creator) { + for (Class cls : creator.classes()) { + propertyCreatorMap.put(cls, creator); + propertyCreatorMap.put(TypeFactory.defaultInstance().constructType(cls), creator); + } + } + + + @Override + public Schema resolve(AnnotatedType propType, ModelConverterContext context, Iterator next) { + PropertyCreator creator = propertyCreatorMap.get(propType.getType()); + if (creator != null) { + return creator.createProperty(); + } + + if (EnumUtils.isDynamicEnum(propType.getType())) { + return resolveDynamicEnum(TypeFactory.defaultInstance().constructType(propType.getType())); + } + + Schema result = super.resolve(propType, context, next); + if (!StringUtils.isEmpty(result.get$ref())) { + Schema referencedSchema = context.getDefinedModels() != null + ? context.getDefinedModels().get(SwaggerUtils.getSchemaName(result.get$ref())) : null; + if (referencedSchema != null) { + if (referencedSchema.getExtensions() == null) { + referencedSchema.setExtensions(new HashMap<>()); + } + if (propType.getType() instanceof JavaType) { + referencedSchema.getExtensions().put(SwaggerConst.EXT_JAVA_CLASS, + ((JavaType) propType.getType()).toCanonical()); + } else { + referencedSchema.getExtensions().put(SwaggerConst.EXT_JAVA_CLASS, + TypeFactory.defaultInstance().constructType(propType.getType()).toCanonical()); + } + } + } + return result; + } + + private Schema resolveDynamicEnum(JavaType propType) { + Class enumClass = propType.getRawClass(); + Class enumValueClass = propType.findTypeParameters(DynamicEnum.class)[0].getRawClass(); + Schema property = PrimitiveType.createProperty(enumValueClass); + + if (property instanceof StringSchema) { + List enums = SwaggerEnum.DYNAMIC.readEnumValues(enumClass); + property.setEnum(enums); + } + + if (property instanceof NumberSchema) { + List enums = SwaggerEnum.DYNAMIC.readEnumValues(enumClass); + property.setEnum(enums); + } + + return property; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/SwaggerEnum.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/SwaggerEnum.java new file mode 100644 index 00000000000..7914fc887eb --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/SwaggerEnum.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.swagger.extend; + +import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.common.base.DynamicEnum; +import org.apache.servicecomb.foundation.common.base.EnumUtils; + +import io.swagger.v3.core.util.Json; +import io.swagger.v3.oas.annotations.Parameter; + + +public enum SwaggerEnum { + JDK { + @SuppressWarnings({"unchecked", "deprecation"}) + @Override + protected T readEnumValue(Field enumField) { + Enum enumValue = EnumUtils.readEnum(enumField); + return (T) Json.mapper().getSerializationConfig().getAnnotationIntrospector().findEnumValue(enumValue); + } + }, + DYNAMIC { + @Override + protected T readEnumValue(Field enumField) { + DynamicEnum enumValue = EnumUtils.readEnum(enumField); + return enumValue.getValue(); + } + }; + + public String findPropertyDescription(Class enumClass, Annotation[] annotations) { + StringBuilder sb = new StringBuilder(); + + String propertyDescription = readDescription(annotations, null); + if (StringUtils.isNotEmpty(propertyDescription)) { + appendLine(sb, propertyDescription); + } + + EnumUtils.findEnumFields(enumClass).forEach(enumField -> { + Object enumValue = readEnumValue(enumField); + String description = readDescription(enumField.getAnnotations(), ""); + appendLine(sb, "- %s: %s", enumValue, description); + }); + + return sb.toString(); + } + + public List readEnumValues(Class enumClass) { + return EnumUtils.findEnumFields(enumClass) + .map(this::readEnumValue) + .collect(Collectors.toList()); + } + + protected abstract T readEnumValue(Field enumField); + + @SuppressWarnings("unchecked") + private T findAnnotation(Annotation[] annotations, Class cls) { + if (annotations == null) { + return null; + } + + return Arrays.stream(annotations) + .filter(annotation -> cls.isAssignableFrom(annotation.getClass())) + .map(annotation -> (T) annotation) + .findAny() + .orElse(null); + } + + private String readDescription(Annotation[] annotations, String defaultDescription) { + Parameter apiParam = findAnnotation(annotations, Parameter.class); + if (apiParam != null && StringUtils.isNotEmpty(apiParam.description())) { + return apiParam.description(); + } + + return defaultDescription; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/annotations/RawJsonRequestBody.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/annotations/RawJsonRequestBody.java new file mode 100644 index 00000000000..743bc3f4a0c --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/annotations/RawJsonRequestBody.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.swagger.extend.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RawJsonRequestBody { + // aliasFor "name" + String value() default ""; + + // aliasFor "value" + String name() default ""; + + boolean required() default true; +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/introspector/JsonPropertyIntrospector.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/introspector/JsonPropertyIntrospector.java new file mode 100644 index 00000000000..4e377b81a11 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/introspector/JsonPropertyIntrospector.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.extend.introspector; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.common.base.EnumUtils; +import org.apache.servicecomb.swagger.extend.SwaggerEnum; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.introspect.Annotated; + +import io.swagger.v3.core.jackson.SwaggerAnnotationIntrospector; + +import java.util.Objects; + + +public class JsonPropertyIntrospector extends SwaggerAnnotationIntrospector { + private static final long serialVersionUID = 4157263023893695762L; + + @SuppressWarnings("deprecation") + @Override + public String findEnumValue(Enum value) { + try { + JsonProperty annotation = value.getClass().getField(value.name()).getAnnotation(JsonProperty.class); + if (null == annotation || StringUtils.isEmpty(annotation.value())) { + return super.findEnumValue(value); + } + return annotation.value(); + } catch (NoSuchFieldException e) { + return super.findEnumValue(value); + } + } + + @Override + public String findPropertyDescription(Annotated annotated) { + Class enumClass = annotated.getRawType(); + if (enumClass.isEnum() && Objects.nonNull(annotated.getAnnotated())) { + return SwaggerEnum.JDK.findPropertyDescription(enumClass, annotated.getAnnotated().getAnnotations()); + } + if (EnumUtils.isDynamicEnum(enumClass) && Objects.nonNull(annotated.getAnnotated())) { + return SwaggerEnum.DYNAMIC.findPropertyDescription(enumClass, annotated.getAnnotated().getAnnotations()); + } + + return super.findPropertyDescription(annotated); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/module/EnumModuleExt.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/module/EnumModuleExt.java new file mode 100644 index 00000000000..d7bd0f6d918 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/module/EnumModuleExt.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.swagger.extend.module; + +import org.apache.servicecomb.swagger.extend.introspector.JsonPropertyIntrospector; + +import com.fasterxml.jackson.databind.module.SimpleModule; + +public class EnumModuleExt extends SimpleModule { + private static final long serialVersionUID = 2934449381601447264L; + + public EnumModuleExt() { + super("SCB-swagger-enum"); + } + + @Override + public void setupModule(SetupContext context) { + context.insertAnnotationIntrospector(new JsonPropertyIntrospector()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/ByteArrayPropertyCreator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/ByteArrayPropertyCreator.java new file mode 100644 index 00000000000..768a07a411f --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/ByteArrayPropertyCreator.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.swagger.extend.property.creator; + +import io.swagger.v3.oas.models.media.ByteArraySchema; +import io.swagger.v3.oas.models.media.Schema; + +/** + * Swagger core will generate byte array to type=array, this is not correct. + * In Open API, byte array should use type=string,format=byte. + */ +@SuppressWarnings({"rawtypes"}) +public class ByteArrayPropertyCreator implements PropertyCreator { + + private final Class[] classes = {Byte[].class, byte[].class}; + + @Override + public Schema createProperty() { + return new ByteArraySchema(); + } + + @Override + public Class[] classes() { + return classes; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/BytePropertyCreator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/BytePropertyCreator.java new file mode 100644 index 00000000000..66e53e67814 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/BytePropertyCreator.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.swagger.extend.property.creator; + + +import io.swagger.v3.oas.models.media.IntegerSchema; +import io.swagger.v3.oas.models.media.Schema; + +/** + * Swagger core will generate byte to type=string,format=byte, this is not correct. + * In Open API, type=string,format=byte is for byte array. + */ +@SuppressWarnings({"rawtypes"}) +public class BytePropertyCreator implements PropertyCreator { + + private final Class[] classes = {Byte.class, byte.class}; + + @Override + public Schema createProperty() { + return new IntegerSchema(); + } + + @Override + public Class[] classes() { + return classes; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/InputStreamPropertyCreator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/InputStreamPropertyCreator.java new file mode 100644 index 00000000000..8a0556c145a --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/InputStreamPropertyCreator.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.swagger.extend.property.creator; + +import java.io.InputStream; + +import io.swagger.v3.oas.models.media.ByteArraySchema; +import io.swagger.v3.oas.models.media.Schema; + +public class InputStreamPropertyCreator implements PropertyCreator { + + private final Class[] classes = {InputStream.class}; + + @Override + public Schema createProperty() { + return new ByteArraySchema(); + } + + @Override + public Class[] classes() { + return classes; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PartPropertyCreator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PartPropertyCreator.java new file mode 100644 index 00000000000..3a489e5f7ef --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PartPropertyCreator.java @@ -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. + */ + +package org.apache.servicecomb.swagger.extend.property.creator; + +import io.swagger.v3.oas.models.media.FileSchema; +import io.swagger.v3.oas.models.media.Schema; +import jakarta.servlet.http.Part; + +public class PartPropertyCreator implements PropertyCreator { + private final Class[] classes = {Part.class}; + + @Override + public Schema createProperty() { + return new FileSchema(); + } + + @Override + public Class[] classes() { + return classes; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PropertyCreator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PropertyCreator.java new file mode 100644 index 00000000000..52629ed7871 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PropertyCreator.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.swagger.extend.property.creator; + +import io.swagger.v3.oas.models.media.Schema; + +@SuppressWarnings({"rawtypes"}) +public interface PropertyCreator { + Schema createProperty(); + + Class[] classes(); +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ClassAnnotationProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ClassAnnotationProcessor.java new file mode 100644 index 00000000000..58e5f6c23c4 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ClassAnnotationProcessor.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.swagger.generator; + +import java.lang.reflect.Type; + +public interface ClassAnnotationProcessor { + Type getProcessType(); + + void process(SwaggerGenerator swaggerGenerator, ANNOTATION annotation); +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/MethodAnnotationProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/MethodAnnotationProcessor.java new file mode 100644 index 00000000000..4502bbc9719 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/MethodAnnotationProcessor.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.swagger.generator; + +import java.lang.reflect.Type; + +public interface MethodAnnotationProcessor { + Type getProcessType(); + + void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, ANNOTATION annotation); +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/OperationGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/OperationGenerator.java new file mode 100644 index 00000000000..358db852989 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/OperationGenerator.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.swagger.generator; + + +import org.apache.servicecomb.swagger.generator.core.OperationGeneratorContext; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; + +public interface OperationGenerator { + OpenAPI getSwagger(); + + Operation getOperation(); + + void setHttpMethod(String httpMethod); + + String getHttpMethod(); + + void addOperationToSwagger(); + + void setPath(String value); + + OperationGeneratorContext getOperationGeneratorContext(); + + /** + * Used to check if one of operation has form parameter + */ + boolean isForm(); + + /** + * Used to check if one of operation form parameter is binary + */ + boolean isBinary(); + + /** + * + * Used to check if this operation is websocket + */ + boolean isWebsocket(); +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterAnnotationProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterAnnotationProcessor.java new file mode 100644 index 00000000000..efba1bf571c --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterAnnotationProcessor.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.swagger.generator; + +import java.lang.reflect.Type; + +public interface ParameterAnnotationProcessor { + Type getProcessType(); + + /** + * Get parameter name from annotation. This method is used in both + * swagger generation and invocation. In invocation, need to find the + * swagger parameter name of the method parameter. + */ + String getParameterName(ANNOTATION annotation); + + void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, ANNOTATION annotation); +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterGenerator.java new file mode 100644 index 00000000000..2547b5c7f20 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterGenerator.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.swagger.generator; + +import java.lang.annotation.Annotation; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.swagger.generator.core.ParameterGeneratorContext; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; + +import com.fasterxml.jackson.databind.JavaType; + +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.CookieParameter; +import io.swagger.v3.oas.models.parameters.HeaderParameter; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.PathParameter; +import io.swagger.v3.oas.models.parameters.QueryParameter; +import io.swagger.v3.oas.models.parameters.RequestBody; + +public class ParameterGenerator { + private final List annotations; + + private final OperationGenerator operationGenerator; + + private final ParameterGeneratorContext parameterGeneratorContext; + + /** + * Used for pojo wrap parameter, while parameter type is null. + */ + public ParameterGenerator(OperationGenerator operationGenerator, String parameterName, Schema schema) { + this.operationGenerator = operationGenerator; + this.parameterGeneratorContext = new ParameterGeneratorContext(operationGenerator.getOperationGeneratorContext()); + this.parameterGeneratorContext.setParameterName(parameterName); + this.parameterGeneratorContext.setSchema(schema); + this.annotations = Collections.emptyList(); + } + + /** + * Used for @BeanParam like parameters, while extract JavaType of the bean parameter type. + */ + public ParameterGenerator(OperationGenerator operationGenerator, + Map> methodAnnotationMap, + String parameterName, + Annotation[] parameterAnnotations, JavaType genericType) { + this.operationGenerator = operationGenerator; + this.annotations = SwaggerGeneratorUtils.collectParameterAnnotations(parameterAnnotations, + methodAnnotationMap, + parameterName); + this.parameterGeneratorContext = new ParameterGeneratorContext(operationGenerator.getOperationGeneratorContext()); + this.parameterGeneratorContext.setParameterName(parameterName); + this.parameterGeneratorContext.setParameterType(genericType); + } + + /** + * Used for normal method parameter initialization, while extract JavaType from method parameter. + */ + public ParameterGenerator(OperationGenerator operationGenerator, + Map> methodAnnotationMap, + java.lang.reflect.Parameter methodParameter, JavaType genericType) { + this(operationGenerator, + methodAnnotationMap, + methodParameter.isNamePresent() ? methodParameter.getName() : null, + methodParameter.getAnnotations(), + genericType); + } + + /** + * Used for annotation defined parameter, while initial parameter type is null + * and will extract JavaType annotation processors. + */ + public ParameterGenerator(OperationGenerator operationGenerator, String parameterName, List annotations) { + this.operationGenerator = operationGenerator; + this.parameterGeneratorContext = new ParameterGeneratorContext(operationGenerator.getOperationGeneratorContext()); + this.parameterGeneratorContext.setParameterName(parameterName); + this.annotations = annotations; + } + + public ParameterGeneratorContext getParameterGeneratorContext() { + return this.parameterGeneratorContext; + } + + public List getAnnotations() { + return annotations; + } + + public JavaType getGenericType() { + return this.parameterGeneratorContext.getParameterType(); + } + + public HttpParameterType getHttpParameterType() { + return this.parameterGeneratorContext.getHttpParameterType(); + } + + public void setHttpParameterType(HttpParameterType httpParameterType) { + this.parameterGeneratorContext.setHttpParameterType(httpParameterType); + } + + public boolean isForm() { + return parameterGeneratorContext.isForm(); + } + + public boolean isBinary() { + return parameterGeneratorContext.isBinary(); + } + + public void generate() { + this.parameterGeneratorContext.updateConsumes( + this.operationGenerator.isForm(), this.operationGenerator.isBinary(), this.operationGenerator.isWebsocket()); + + if (this.parameterGeneratorContext.getHttpParameterType() == HttpParameterType.BODY) { + if (parameterGeneratorContext.getSupportedConsumes().size() == 0) { + throw new IllegalArgumentException("Consumes not provided for BODY parameter, or is empty " + + "by annotations rule."); + } + RequestBody requestBody = new RequestBody(); + requestBody.setRequired(parameterGeneratorContext.getRequired()); + Map extensions = new HashMap<>(); + extensions.put(SwaggerConst.EXT_BODY_NAME, parameterGeneratorContext.getParameterName()); + if (parameterGeneratorContext.getRawJson() != null) { + extensions.put(SwaggerConst.EXT_RAW_JSON_TYPE, parameterGeneratorContext.getRawJson()); + } + requestBody.setExtensions(extensions); + requestBody.setContent(new Content()); + for (String media : parameterGeneratorContext.getSupportedConsumes()) { + MediaType mediaType = new MediaType(); + mediaType.setSchema(parameterGeneratorContext.getSchema()); + requestBody.getContent().addMediaType(media, mediaType); + } + this.operationGenerator.getOperation().setRequestBody(requestBody); + return; + } + if (this.parameterGeneratorContext.getHttpParameterType() == HttpParameterType.FORM) { + if (parameterGeneratorContext.getSupportedConsumes().size() == 0) { + throw new IllegalArgumentException("Consumes not provided for FORM parameter, or is empty " + + "by annotations rule."); + } + RequestBody requestBody = this.operationGenerator.getOperation().getRequestBody(); + if (requestBody == null) { + requestBody = new RequestBody(); + requestBody.setContent(new Content()); + this.operationGenerator.getOperation().setRequestBody(requestBody); + } + for (String media : parameterGeneratorContext.getSupportedConsumes()) { + MediaType mediaType = requestBody.getContent().get(media); + if (mediaType == null) { + mediaType = new MediaType(); + mediaType.setSchema(new ObjectSchema()); + requestBody.getContent().addMediaType(media, mediaType); + } + mediaType.getSchema().addProperty(parameterGeneratorContext.getParameterName(), + parameterGeneratorContext.getSchema()); + } + return; + } + Parameter parameter; + switch (this.parameterGeneratorContext.getHttpParameterType()) { + case PATH -> parameter = new PathParameter(); + case QUERY -> parameter = new QueryParameter(); + case HEADER -> parameter = new HeaderParameter(); + case COOKIE -> parameter = new CookieParameter(); + default -> throw new IllegalStateException("not support httpParameterType " + + this.parameterGeneratorContext.getHttpParameterType()); + } + parameter.setName(parameterGeneratorContext.getParameterName()); + parameter.setSchema(parameterGeneratorContext.getSchema()); + parameter.setRequired(parameterGeneratorContext.getRequired()); + parameter.setExplode(parameterGeneratorContext.getExplode()); + this.operationGenerator.getOperation().addParametersItem(parameter); + + // validations and other annotations supported by swagger default + if (parameterGeneratorContext.getParameterType() != null) { + io.swagger.v3.core.util.ParameterProcessor.applyAnnotations(parameter, + parameterGeneratorContext.getParameterType(), + annotations, operationGenerator.getSwagger().getComponents(), + null, null, null); + } + // spring mvc DefaultValue annotation not processed by swagger api. + if (parameterGeneratorContext.getDefaultValue() != null) { + parameter.getSchema().setDefault(parameterGeneratorContext.getDefaultValue()); + } + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterTypeProcessor.java new file mode 100644 index 00000000000..e6af81e0d5e --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterTypeProcessor.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.swagger.generator; + +import com.fasterxml.jackson.databind.JavaType; + +public interface ParameterTypeProcessor { + JavaType getProcessType(); + + void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator); +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ResponseTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ResponseTypeProcessor.java new file mode 100644 index 00000000000..a1efa5112ae --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ResponseTypeProcessor.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.swagger.generator; + +import java.lang.reflect.Type; + +import io.swagger.v3.oas.models.media.Schema; + +public interface ResponseTypeProcessor { + Type getProcessType(); + + /** + * @return if genericResponseType is CompletableFuture<String>, then return String + */ + Type extractResponseType(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + Type genericResponseType); + + default Type extractResponseType(Type genericResponseType) { + return extractResponseType(null, null, genericResponseType); + } + + Schema process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Type genericResponseType); +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerConst.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerConst.java new file mode 100644 index 00000000000..70990922e07 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerConst.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.swagger.generator; + +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +public final class SwaggerConst { + private SwaggerConst() { + + } + + public static final String SUCCESS_KEY = String.valueOf(Response.Status.OK.getStatusCode()); + + public static final String DEFAULT_MEDIA_TYPE = MediaType.APPLICATION_JSON; + + public static final String FORM_MEDIA_TYPE = MediaType.APPLICATION_FORM_URLENCODED; + + public static final String FILE_MEDIA_TYPE = MediaType.MULTIPART_FORM_DATA; + + public static final String PROTOBUF_TYPE = "application/protobuf"; + + public static final String WEBSOCKET_TYPE = "application/websocket"; + + public static final String TAG_WEBSOCKET = "websocket"; + + public static final String EXT_JAVA_INTF = "x-java-interface"; + + public static final String EXT_JAVA_CLASS = "x-java-class"; + + public static final String EXT_RAW_JSON_TYPE = "x-raw-json"; + + public static final String EXT_JSON_VIEW = "x-json-view"; + + public static final String EXT_BODY_NAME = "x-name"; +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerContextRegister.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerContextRegister.java new file mode 100644 index 00000000000..8095b669802 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerContextRegister.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.swagger.generator; + +import java.lang.reflect.Type; + +public interface SwaggerContextRegister { + Type getContextType(); +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGenerator.java new file mode 100644 index 00000000000..cc9bbaaa58b --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGenerator.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.swagger.generator; + +import java.lang.reflect.Method; +import java.util.List; + +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.swagger.generator.core.SwaggerGeneratorContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.swagger.v3.oas.models.OpenAPI; + +public interface SwaggerGenerator { + Logger LOGGER = LoggerFactory.getLogger(SwaggerGenerator.class); + + static OpenAPI generate(Class cls) { + return create(cls).generate(); + } + + static SwaggerGenerator create(Class cls) { + List factories = SPIServiceUtils.getOrLoadSortedService(SwaggerGeneratorFactory.class); + for (SwaggerGeneratorFactory factory : factories) { + if (factory.canProcess(cls)) { + LOGGER.info("select [{}] for [{}] to generate schema.", factory.getClass().getName(), cls.getName()); + return factory.create(cls); + } + } + + throw new IllegalStateException("impossible, must be bug. can not generate swagger for " + cls.getName()); + } + + SwaggerGeneratorContext getSwaggerGeneratorContext(); + + /** + * base path contains application server context-path, and schema base path. + */ + void setBasePath(String basePath); + + void scanClassAnnotation(); + + OpenAPI generate(); + + Class getClazz(); + + OpenAPI getOpenAPI(); + + void setHttpMethod(String httpMethod); + + void replaceMethodWhiteList(String... methodNames); + + T createOperationGenerator(Method method); +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGeneratorFactory.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGeneratorFactory.java new file mode 100644 index 00000000000..3178ff36aff --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGeneratorFactory.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator; + +import org.apache.servicecomb.foundation.common.utils.SPIOrder; + +public interface SwaggerGeneratorFactory extends SPIOrder { + boolean canProcess(Class cls); + + SwaggerGenerator create(Class cls); +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGeneratorUtils.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGeneratorUtils.java new file mode 100644 index 00000000000..c8b0498de8b --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGeneratorUtils.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.swagger.generator; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +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.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.swagger.generator.core.processor.response.DefaultResponseTypeProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.swagger.v3.core.util.Json; + +@SuppressWarnings("rawtypes") +public final class SwaggerGeneratorUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(SwaggerGeneratorUtils.class); + + // all static fields load from SPI and stateless + private static final Set contextTypes = SPIServiceUtils.getOrLoadSortedService(SwaggerContextRegister.class) + .stream() + .map(swaggerContextRegister -> TypeFactory.defaultInstance() + .constructType(swaggerContextRegister.getContextType())) + .collect(Collectors.toSet()); + + private static final Map> classAnnotationProcessors = new HashMap<>(); + + private static final Map> methodAnnotationProcessors = new HashMap<>(); + + private static final Map responseTypeProcessors = new HashMap<>(); + + private static final DefaultResponseTypeProcessor defaultResponseTypeProcessor = new DefaultResponseTypeProcessor(); + + private static final List parameterAnnotationProcessors = SPIServiceUtils + .getOrLoadSortedService(ParameterAnnotationProcessor.class); + + private static final List parameterTypeProcessors = SPIServiceUtils + .getOrLoadSortedService(ParameterTypeProcessor.class); + + static { + // low order value has high priority + for (ClassAnnotationProcessor processor : SPIServiceUtils + .getOrLoadSortedService(ClassAnnotationProcessor.class)) { + if (classAnnotationProcessors.putIfAbsent(processor.getProcessType(), processor) != null) { + LOGGER.info("ignore duplicated ClassAnnotationProcessor, type={}, processor={}.", + processor.getProcessType().getTypeName(), processor.getClass().getName()); + } + } + + for (MethodAnnotationProcessor processor : SPIServiceUtils + .getOrLoadSortedService(MethodAnnotationProcessor.class)) { + if (methodAnnotationProcessors.putIfAbsent(processor.getProcessType(), processor) != null) { + LOGGER.info("ignore duplicated MethodAnnotationProcessor, type={}, processor={}.", + processor.getProcessType().getTypeName(), processor.getClass().getName()); + } + } + + for (ResponseTypeProcessor processor : SPIServiceUtils.getOrLoadSortedService(ResponseTypeProcessor.class)) { + if (responseTypeProcessors.putIfAbsent(processor.getProcessType(), processor) != null) { + LOGGER.info("ignore duplicated ResponseTypeProcessor, type={}, processor={}.", + processor.getProcessType().getTypeName(), processor.getClass().getName()); + } + } + + List modules = SPIServiceUtils.getOrLoadSortedService(Module.class); + Json.mapper().registerModules(modules.toArray(new Module[0])); + } + + private SwaggerGeneratorUtils() { + } + + @SuppressWarnings("unchecked") + public static ClassAnnotationProcessor findClassAnnotationProcessor(Type type) { + return (ClassAnnotationProcessor) classAnnotationProcessors.get(type); + } + + @SuppressWarnings("unchecked") + public static MethodAnnotationProcessor findMethodAnnotationProcessor(Type type) { + return (MethodAnnotationProcessor) methodAnnotationProcessors.get(type); + } + + @SuppressWarnings("unchecked") + public static ParameterAnnotationProcessor findParameterAnnotationProcessor(Type type) { + for (ParameterAnnotationProcessor processor : parameterAnnotationProcessors) { + if (processor.getProcessType() == type) { + return processor; + } + } + return null; + } + + public static ParameterTypeProcessor findParameterTypeProcessor(Type type) { + for (ParameterTypeProcessor processor : parameterTypeProcessors) { + if (processor.getProcessType() == type) { + return processor; + } + } + return null; + } + + public static ResponseTypeProcessor findResponseTypeProcessor(Type type) { + ResponseTypeProcessor processor = responseTypeProcessors.get(type); + if (processor != null) { + return processor; + } + + if (type instanceof ParameterizedType) { + return responseTypeProcessors.getOrDefault(((ParameterizedType) type).getRawType(), defaultResponseTypeProcessor); + } + + return defaultResponseTypeProcessor; + } + + public static boolean isContextParameter(JavaType type) { + return contextTypes.contains(type); + } + + public static Annotation[] collectAnnotations(BeanPropertyDefinition propertyDefinition) { + List annotations = new ArrayList<>(); + if (propertyDefinition.getField() != null) { + Collections.addAll(annotations, propertyDefinition.getField().getAnnotated().getAnnotations()); + } + if (propertyDefinition.getGetter() != null) { + Collections.addAll(annotations, propertyDefinition.getGetter().getAnnotated().getAnnotations()); + } + if (propertyDefinition.getSetter() != null) { + Collections.addAll(annotations, propertyDefinition.getSetter().getAnnotated().getAnnotations()); + } + return annotations.toArray(new Annotation[0]); + } + + public static String collectParameterName(java.lang.reflect.Parameter methodParameter) { + return collectParameterName(methodParameter.getDeclaringExecutable(), methodParameter.getAnnotations(), + methodParameter.isNamePresent() ? methodParameter.getName() : null); + } + + public static String collectParameterName(Method method, BeanPropertyDefinition propertyDefinition) { + Annotation[] annotations = collectAnnotations(propertyDefinition); + return collectParameterName(method, annotations, propertyDefinition.getName()); + } + + public static String collectParameterName(Executable executable, Annotation[] annotations, String defaultName) { + // 1.annotations + // it's ambiguous to use different name in different annotation + // so we only read the first available name + for (Annotation annotation : annotations) { + ParameterAnnotationProcessor processor = findParameterAnnotationProcessor( + annotation.annotationType()); + if (processor == null) { + continue; + } + + String name = processor.getParameterName(annotation); + if (StringUtils.isNotEmpty(name)) { + return name; + } + } + + // 2.use signature name + // ensure present parameter name + if (StringUtils.isNotEmpty(defaultName)) { + return defaultName; + } + + String msg = String.format(""" + parameter name is not present, method=%s:%s + solution: + change pom.xml, add compiler argument: -parameters, for example: + + org.apache.maven.plugins + maven-compiler-plugin + + -parameters + + """, + executable.getDeclaringClass().getName(), executable.getName()); + throw new IllegalStateException(msg); + } + + public static List collectParameterAnnotations(Annotation[] parameterAnnotations, + Map> methodAnnotationMap, String parameterName) { + List methodAnnotations = methodAnnotationMap.remove(parameterName); + if (methodAnnotations == null) { + methodAnnotations = Collections.emptyList(); + } + + List annotations = new ArrayList<>(); + Collections.addAll(annotations, parameterAnnotations); + annotations.addAll(methodAnnotations); + + return annotations; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerParameterAnnotationProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerParameterAnnotationProcessor.java new file mode 100644 index 00000000000..72633d01c2f --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerParameterAnnotationProcessor.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator; + +/** + * A generic class to help classify implementations. Processors of swagger annotations + * will implement this class. + */ +public abstract class SwaggerParameterAnnotationProcessor implements + ParameterAnnotationProcessor { +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractOperationGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractOperationGenerator.java new file mode 100644 index 00000000000..f3c00478f7b --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractOperationGenerator.java @@ -0,0 +1,444 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator.core; + +import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.collectAnnotations; +import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.findMethodAnnotationProcessor; +import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.findResponseTypeProcessor; +import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.isContextParameter; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.inject.PlaceholderResolver; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.ParameterTypeProcessor; +import org.apache.servicecomb.swagger.generator.ResponseTypeProcessor; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; +import org.apache.servicecomb.swagger.generator.core.utils.MethodUtils; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.google.common.reflect.TypeToken; + +import io.swagger.v3.core.util.Json; +import io.swagger.v3.core.util.ReflectionUtils; +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.headers.Header; +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.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; +import jakarta.servlet.http.HttpServletResponse; + +@SuppressWarnings("rawtypes") +public abstract class AbstractOperationGenerator implements OperationGenerator { + protected AbstractSwaggerGenerator swaggerGenerator; + + protected OpenAPI swagger; + + protected Class clazz; + + protected Method method; + + protected String httpMethod; + + protected List parameterGenerators = new ArrayList<>(); + + protected String path; + + protected Operation swaggerOperation; + + protected OperationGeneratorContext operationGeneratorContext; + + public AbstractOperationGenerator(AbstractSwaggerGenerator swaggerGenerator, Method method) { + this.swaggerGenerator = swaggerGenerator; + this.swagger = swaggerGenerator.getOpenAPI(); + this.clazz = swaggerGenerator.getClazz(); + this.method = method; + this.httpMethod = swaggerGenerator.getHttpMethod(); + + operationGeneratorContext = new OperationGeneratorContext(swaggerGenerator.getSwaggerGeneratorContext()); + swaggerOperation = new Operation(); + } + + @Override + public OperationGeneratorContext getOperationGeneratorContext() { + return operationGeneratorContext; + } + + @Override + public void setHttpMethod(String httpMethod) { + if (StringUtils.isEmpty(httpMethod)) { + return; + } + + this.httpMethod = httpMethod.toUpperCase(Locale.US); + } + + @Override + public OpenAPI getSwagger() { + return this.swagger; + } + + @Override + public String getHttpMethod() { + return httpMethod; + } + + @Override + public Operation getOperation() { + return swaggerOperation; + } + + public String getOperationId() { + return swaggerOperation.getOperationId(); + } + + @Override + public void setPath(String path) { + path = new PlaceholderResolver().replaceFirst(path); + if (!path.startsWith("/")) { + path = "/" + path; + } + this.path = path; + } + + public void generate() { + scanMethodAnnotation(); + scanMethodParameters(); + scanResponse(); + correctOperation(); + } + + protected void scanMethodAnnotation() { + for (Annotation annotation : Arrays.stream(method.getAnnotations()) + .sorted(Comparator.comparing(a -> a.annotationType().getSimpleName())) + .collect(Collectors.toList())) { + MethodAnnotationProcessor processor = findMethodAnnotationProcessor(annotation.annotationType()); + if (processor == null) { + continue; + } + processor.process(swaggerGenerator, this, annotation); + } + + if (StringUtils.isEmpty(swaggerOperation.getOperationId())) { + swaggerOperation.setOperationId(MethodUtils.findSwaggerMethodName(method)); + } + } + + protected void scanMethodParameters() { + // init generators + initParameterGenerators(); + + // scan annotations and types + Set names = new HashSet<>(); + int bodyCount = 0; + for (ParameterGenerator parameterGenerator : parameterGenerators) { + scanMethodParameter(parameterGenerator); + + if (!names.add(parameterGenerator.getParameterGeneratorContext().getParameterName())) { + throw new IllegalArgumentException( + String.format("not support duplicated parameter, name=%s.", + parameterGenerator.getParameterGeneratorContext().getParameterName())); + } + if (parameterGenerator.getHttpParameterType() == HttpParameterType.BODY) { + if (bodyCount > 0) { + throw new IllegalArgumentException(String.format("Defined %d body parameter.", bodyCount)); + } + bodyCount++; + } + } + + // generate + for (ParameterGenerator parameterGenerator : parameterGenerators) { + parameterGenerator.generate(); + } + } + + protected void initParameterGenerators() { + // 1.group method annotations by parameter name + // key is parameter name + Map> methodAnnotationMap = initMethodAnnotationByParameterName(); + + // 2.create ParameterGenerators by method parameters, merge annotations with method annotations + initMethodParameterGenerators(methodAnnotationMap); + + // 3.create ParameterGenerators remains method annotations + initRemainMethodAnnotationsParameterGenerators(methodAnnotationMap); + } + + protected void initMethodParameterGenerators(Map> methodAnnotationMap) { + for (java.lang.reflect.Parameter methodParameter : method.getParameters()) { + Type genericType = TypeToken.of(clazz) + .resolveType(methodParameter.getParameterizedType()) + .getType(); + ParameterGenerator parameterGenerator = new ParameterGenerator( + this, methodAnnotationMap, methodParameter, + TypeFactory.defaultInstance().constructType(genericType)); + validateParameter(parameterGenerator.getGenericType()); + if (isContextParameter(parameterGenerator.getGenericType())) { + continue; + } + + // jaxrs: @BeanParam + // springmvc: is query, and is bean type + if (isAggregatedParameter(parameterGenerator, methodParameter)) { + extractAggregatedParameterGenerators(methodAnnotationMap, methodParameter); + continue; + } + + parameterGenerators.add(parameterGenerator); + } + } + + protected boolean isAggregatedParameter(ParameterGenerator parameterGenerator, + java.lang.reflect.Parameter methodParameter) { + return false; + } + + protected void extractAggregatedParameterGenerators(Map> methodAnnotationMap, + java.lang.reflect.Parameter methodParameter) { + JavaType javaType = TypeFactory.defaultInstance().constructType(methodParameter.getParameterizedType()); + BeanDescription beanDescription = Json.mapper().getSerializationConfig().introspect(javaType); + for (BeanPropertyDefinition propertyDefinition : beanDescription.findProperties()) { + if (!propertyDefinition.couldSerialize()) { + continue; + } + + Annotation[] annotations = collectAnnotations(propertyDefinition); + ParameterGenerator propertyParameterGenerator = new ParameterGenerator(this, + methodAnnotationMap, + propertyDefinition.getName(), + annotations, + propertyDefinition.getPrimaryType()); + parameterGenerators.add(propertyParameterGenerator); + } + } + + protected void initRemainMethodAnnotationsParameterGenerators(Map> methodAnnotationMap) { + for (Entry> entry : methodAnnotationMap.entrySet()) { + ParameterGenerator parameterGenerator = new ParameterGenerator(this, entry.getKey(), entry.getValue()); + parameterGenerators.add(parameterGenerator); + } + } + + private Map> initMethodAnnotationByParameterName() { + Map> methodAnnotations = new LinkedHashMap<>(); + for (Annotation annotation : method.getAnnotations()) { + if (annotation instanceof io.swagger.v3.oas.annotations.Parameters) { + for (io.swagger.v3.oas.annotations.Parameter apiImplicitParam + : ((io.swagger.v3.oas.annotations.Parameters) annotation).value()) { + addMethodAnnotationByParameterName(methodAnnotations, apiImplicitParam.name(), apiImplicitParam); + } + continue; + } + + if (annotation instanceof io.swagger.v3.oas.annotations.Parameter) { + addMethodAnnotationByParameterName(methodAnnotations, + ((io.swagger.v3.oas.annotations.Parameter) annotation).name(), annotation); + } + } + return methodAnnotations; + } + + private void addMethodAnnotationByParameterName(Map> methodAnnotations, String name, + Annotation annotation) { + if (StringUtils.isEmpty(name)) { + throw new IllegalStateException(String.format("%s.name should not be empty. method=%s:%s", + annotation.annotationType().getSimpleName(), + method.getDeclaringClass().getName(), + method.getName())); + } + + methodAnnotations.computeIfAbsent(name, n -> new ArrayList<>()) + .add(annotation); + } + + protected void validateParameter(JavaType type) { + if (type.isTypeOrSubTypeOf(HttpServletResponse.class)) { + // not support, log the reason + throw new IllegalStateException( + "all input/output of ServiceComb operation are models, not allow to use HttpServletResponse."); + } + } + + protected void scanMethodParameter(ParameterGenerator parameterGenerator) { + for (Annotation annotation : parameterGenerator.getAnnotations()) { + ParameterAnnotationProcessor processor = SwaggerGeneratorUtils + .findParameterAnnotationProcessor(annotation.annotationType()); + if (processor != null) { + processor.process(this.swaggerGenerator, this, parameterGenerator, annotation); + } + } + + Schema schema = parameterGenerator.getParameterGeneratorContext().getSchema(); + if (schema == null) { + JavaType parameterType = parameterGenerator.getGenericType(); + ParameterTypeProcessor processor = SwaggerGeneratorUtils.findParameterTypeProcessor(parameterType); + if (processor != null) { + processor.process(this.swaggerGenerator, this, parameterGenerator); + } else { + parameterGenerator.getParameterGeneratorContext().setSchema(SwaggerUtils.resolveTypeSchemas(this.swagger, + parameterType)); + } + } + } + + @Override + public boolean isForm() { + for (ParameterGenerator parameterGenerator : parameterGenerators) { + if (parameterGenerator.isForm()) { + return true; + } + } + return false; + } + + @Override + public boolean isBinary() { + for (ParameterGenerator parameterGenerator : parameterGenerators) { + if (parameterGenerator.isBinary()) { + return true; + } + } + return false; + } + + @Override + public boolean isWebsocket() { + return this.swaggerOperation.getTags() != null && + this.swaggerOperation.getTags().contains(SwaggerConst.TAG_WEBSOCKET); + } + + @Override + public void addOperationToSwagger() { + if (StringUtils.isEmpty(httpMethod)) { + return; + } + + if (swagger.getPaths() == null) { + swagger.setPaths(new Paths()); + } + + PathItem pathObj = swagger.getPaths().get(path); + if (pathObj == null) { + pathObj = new PathItem(); + swagger.path(path, pathObj); + } else if (SwaggerUtils.methodExists(pathObj, httpMethod)) { + throw new IllegalStateException(String.format("Duplicate operation path detected. method=%s:%s.", + method.getDeclaringClass().getName(), + method.getName())); + } + + pathObj.operation(PathItem.HttpMethod.valueOf(httpMethod), swaggerOperation); + } + + public void correctOperation() { + SwaggerUtils.correctResponses(swaggerOperation); + } + + public void scanResponse() { + operationGeneratorContext.updateProduces(); + swaggerOperation.setResponses(new ApiResponses()); + for (Entry> response : operationGeneratorContext.getResponses().entrySet()) { + ApiResponse apiResponse = new ApiResponse(); + if (StringUtils.isNotEmpty(operationGeneratorContext.getResponseDescriptions().get(response.getKey()))) { + apiResponse.setDescription(operationGeneratorContext.getResponseDescriptions().get(response.getKey())); + } + if (operationGeneratorContext.getResponseHeaders().get(response.getKey()) != null) { + operationGeneratorContext.getResponseHeaders().get(response.getKey()).forEach((k, v) -> { + Header header = new Header(); + header.setSchema(v); + apiResponse.addHeaderObject(k, header); + }); + } + Schema schema = response.getValue() != null ? response.getValue() : + createResponseModel(); + + if (schema == null) { + swaggerOperation.getResponses().addApiResponse(response.getKey(), apiResponse); + continue; + } + + apiResponse.setContent(new Content()); + // file download using WILDCARD content-type + if ("string".equals(schema.getType()) && "binary".equals(schema.getFormat())) { + MediaType mediaType = new MediaType(); + mediaType.setSchema(schema); + apiResponse.getContent().addMediaType(jakarta.ws.rs.core.MediaType.WILDCARD, mediaType); + } else { + for (String produce : operationGeneratorContext.getSupportedProduces()) { + MediaType mediaType = new MediaType(); + mediaType.setSchema(schema); + apiResponse.getContent().addMediaType(produce, mediaType); + } + } + swaggerOperation.getResponses().addApiResponse(response.getKey(), apiResponse); + } + } + + protected Schema createResponseModel() { + Type responseType = + TypeToken.of(clazz) + .resolveType(method.getGenericReturnType()) + .getType(); + if (ReflectionUtils.isVoid(responseType)) { + return null; + } + + ResponseTypeProcessor processor = findResponseTypeProcessor(responseType); + return processor.process(swaggerGenerator, this, responseType); + } + + public Method getMethod() { + return method; + } + + public List getParameterGenerators() { + return parameterGenerators; + } + + public Operation getSwaggerOperation() { + return swaggerOperation; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractSwaggerGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractSwaggerGenerator.java new file mode 100644 index 00000000000..6549418441d --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractSwaggerGenerator.java @@ -0,0 +1,253 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core; + +import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.findClassAnnotationProcessor; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.inject.PlaceholderResolver; +import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.utils.MethodUtils; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.servers.Server; + + +/** + *

+ * support:
+ * 1.pojo + swagger annotation
+ *   wrap all input parameter to be fields of body
+ * 2.pojo + swagger annotation + jaxrs annotation
+ * 3.pojo + swagger annotation + springmvc annotation
+ * 
+ */ +public abstract class AbstractSwaggerGenerator implements SwaggerGenerator { + protected SwaggerGeneratorContext swaggerGeneratorContext = new SwaggerGeneratorContext(); + + protected Class cls; + + protected OpenAPI openAPI; + + // allowed to control only process some methods + // empty means all methods are available + protected Set methodWhiteList = new HashSet<>(); + + // key is operationId + // to check if operationId is duplicated + protected Map operationGenerators = new LinkedHashMap<>(); + + protected String httpMethod; + + public AbstractSwaggerGenerator(Class cls) { + this.openAPI = new OpenAPI(); + this.openAPI.components(new Components()) + .paths(new Paths()) + .servers(new ArrayList<>()) + .info(new Info()); + this.cls = cls; + } + + public OpenAPI getOpenAPI() { + return openAPI; + } + + @Override + public Class getClazz() { + return cls; + } + + public String getHttpMethod() { + return httpMethod; + } + + @Override + public void setHttpMethod(String httpMethod) { + this.httpMethod = httpMethod.toUpperCase(Locale.US); + } + + @Override + public SwaggerGeneratorContext getSwaggerGeneratorContext() { + return swaggerGeneratorContext; + } + + public OpenAPI generate() { + LOGGER.info("generate schema from [{}]", cls); + scanClassAnnotation(); + + scanMethods(); + addOperationsToSwagger(); + + correctSwagger(); + + return openAPI; + } + + public void scanClassAnnotation() { + for (Annotation annotation : cls.getAnnotations()) { + ClassAnnotationProcessor processor = findClassAnnotationProcessor(annotation.annotationType()); + if (processor == null) { + continue; + } + processor.process(this, annotation); + } + } + + /** + * fill empty and required field to be default value + * if can not build default value, then throw exceptions + */ + protected void correctSwagger() { + correctBasePath(); + correctInfo(); + } + + private void correctBasePath() { + if (openAPI.getServers() == null) { + openAPI.setServers(new ArrayList<>()); + } + if (openAPI.getServers().size() <= 0) { + Server server = new Server(); + server.setUrl("/" + cls.getSimpleName()); + openAPI.getServers().add(server); + } + } + + private void correctInfo() { + Info info = openAPI.getInfo(); + if (info == null) { + info = new Info(); + openAPI.setInfo(info); + } + + if (StringUtils.isEmpty(info.getTitle())) { + info.setTitle("swagger definition for " + cls.getName()); + } + if (StringUtils.isEmpty(info.getVersion())) { + info.setVersion("1.0.0"); + } + } + + @Override + public void replaceMethodWhiteList(String... methodNames) { + methodWhiteList.clear(); + + if (methodNames == null || methodNames.length == 0) { + return; + } + + methodWhiteList.addAll(Arrays.asList(methodNames)); + } + + /** + * Whether this method should be processed as a swagger operation + * @return true if this isn't a swagger operation; otherwise, false. + */ + protected boolean isSkipMethod(Method method) { + if (method.getDeclaringClass().getName().equals(Object.class.getName())) { + return true; + } + // skip static method + int modifiers = method.getModifiers(); + if (Modifier.isStatic(modifiers)) { + return true; + } + // skip bridge method + if (method.isBridge()) { + return true; + } + + Operation apiOperation = method.getAnnotation(Operation.class); + if (apiOperation != null && apiOperation.hidden()) { + return true; + } + + if (!methodWhiteList.isEmpty()) { + return !methodWhiteList.contains(MethodUtils.findSwaggerMethodName(method)); + } + + return false; + } + + protected void scanMethods() { + List methods = MethodUtils.findSwaggerMethods(cls); + + for (Method method : methods) { + if (isSkipMethod(method)) { + continue; + } + + AbstractOperationGenerator operationGenerator = createOperationGenerator(method); + operationGenerator.setHttpMethod(httpMethod); + try { + operationGenerator.generate(); + } catch (Throwable e) { + String msg = String.format("Generate swagger operation failed, method=%s:%s, cause=%s", + this.cls.getSimpleName(), method.getName(), e.getMessage()); + throw new IllegalStateException(msg, e); + } + + if (StringUtils.isEmpty(operationGenerator.httpMethod)) { + throw new IllegalStateException( + String.format("HttpMethod must not both be empty in class and method, method=%s:%s.", + cls.getName(), method.getName())); + } + + if (operationGenerators.putIfAbsent(operationGenerator.getOperationId(), operationGenerator) != null) { + throw new IllegalStateException( + String.format("OperationId must be unique. method=%s:%s.", cls.getName(), method.getName())); + } + } + } + + protected void addOperationsToSwagger() { + for (OperationGenerator operationGenerator : operationGenerators.values()) { + operationGenerator.addOperationToSwagger(); + } + } + + @Override + public void setBasePath(String basePath) { + basePath = new PlaceholderResolver().replaceFirst(basePath); + if (openAPI.getServers() == null) { + openAPI.setServers(new ArrayList<>()); + } + if (openAPI.getServers().size() == 0) { + openAPI.getServers().add(new Server()); + } + openAPI.getServers().get(0).setUrl(basePath); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/OperationGeneratorContext.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/OperationGeneratorContext.java new file mode 100644 index 00000000000..3cf1105c836 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/OperationGeneratorContext.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator.core; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.swagger.generator.SwaggerConst; + +import com.fasterxml.jackson.databind.JavaType; + +import io.swagger.v3.oas.models.media.Schema; + +public class OperationGeneratorContext extends SwaggerGeneratorContext { + private final Map> responses = new HashMap<>(); + + private JavaType responseType; + + private final Map responseDescriptions = new HashMap<>(); + + // statusCode:headerName:headerSchema + private final Map>> responseHeaders = new HashMap<>(); + + public OperationGeneratorContext(SwaggerGeneratorContext parent) { + super(parent); + updateResponse(SwaggerConst.SUCCESS_KEY, null); + } + + public Map> getResponses() { + return responses; + } + + public void updateResponse(String code, Schema schema) { + this.responses.put(code, schema); + } + + public JavaType getResponseType() { + return responseType; + } + + public void setResponseType(JavaType responseType) { + this.responseType = responseType; + } + + public Map getResponseDescriptions() { + return responseDescriptions; + } + + public void updateResponseDescription(String code, String description) { + this.responseDescriptions.put(code, description); + } + + public Map>> getResponseHeaders() { + return responseHeaders; + } + + public void updateResponseHeader(String code, String header, Schema schema) { + this.responseHeaders.computeIfAbsent(code, key -> new HashMap<>()); + this.responseHeaders.get(code).put(header, schema); + } + + public void updateProduces() { + List removed = new ArrayList<>(); + for (String media : supportedProduces) { + if (SUPPORTED_BODY_CONTENT_TYPE.contains(media)) { + continue; + } + removed.add(media); + } + supportedProduces.removeAll(removed); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/ParameterGeneratorContext.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/ParameterGeneratorContext.java new file mode 100644 index 00000000000..dc9f6c9c0da --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/ParameterGeneratorContext.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator.core; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; + +import com.fasterxml.jackson.databind.JavaType; + +import io.swagger.v3.oas.models.media.Schema; +import jakarta.ws.rs.core.MediaType; + +public class ParameterGeneratorContext extends OperationGeneratorContext { + /** + * Parameter type. Maybe override by annotations. And for pojo wrapped parameter, + * this is null. + */ + private JavaType parameterType; + + private String parameterName; + + private HttpParameterType httpParameterType; + + private Boolean explode; + + private Boolean required; + + private Object defaultValue; + + private Boolean rawJson; + + private Schema schema; + + public ParameterGeneratorContext(OperationGeneratorContext parent) { + super(parent); + } + + public JavaType getParameterType() { + return parameterType; + } + + public void setParameterType(JavaType parameterType) { + this.parameterType = parameterType; + } + + public String getParameterName() { + return parameterName; + } + + public void setParameterName(String parameterName) { + this.parameterName = parameterName; + } + + public Boolean getExplode() { + return explode; + } + + public void setExplode(Boolean explode) { + this.explode = explode; + } + + public Boolean getRequired() { + return required; + } + + public void setRequired(Boolean required) { + this.required = required; + } + + public Boolean getRawJson() { + return rawJson; + } + + public void setRawJson(Boolean rawJson) { + this.rawJson = rawJson; + } + + public Schema getSchema() { + return schema; + } + + public void setSchema(Schema schema) { + this.schema = schema; + } + + public HttpParameterType getHttpParameterType() { + return httpParameterType; + } + + public void setHttpParameterType(HttpParameterType httpParameterType) { + this.httpParameterType = httpParameterType; + } + + public Object getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + } + + public void updateConsumes(boolean isForm, boolean isBinary, boolean isWebSocket) { + List removed = new ArrayList<>(); + if (isWebSocket) { + supportedConsumes.clear(); + supportedConsumes.add(SwaggerConst.WEBSOCKET_TYPE); + return; + } + + if (httpParameterType == HttpParameterType.BODY) { + if (isForm) { + throw new IllegalArgumentException("Both form and body parameter not allowed."); + } + for (String media : supportedConsumes) { + if (SUPPORTED_BODY_CONTENT_TYPE.contains(media)) { + continue; + } + removed.add(media); + } + supportedConsumes.removeAll(removed); + return; + } + + if (httpParameterType == HttpParameterType.FORM) { + for (String media : supportedConsumes) { + if (!SUPPORTED_FORM_CONTENT_TYPE.contains(media)) { + removed.add(media); + continue; + } + if (isBinary && supportedConsumes.contains(MediaType.APPLICATION_FORM_URLENCODED)) { + removed.add(MediaType.APPLICATION_FORM_URLENCODED); + } + if (!isBinary && supportedConsumes.contains(MediaType.MULTIPART_FORM_DATA)) { + removed.add(MediaType.MULTIPART_FORM_DATA); + } + } + supportedConsumes.removeAll(removed); + return; + } + + supportedConsumes.clear(); + } + + public boolean isForm() { + return httpParameterType == HttpParameterType.FORM; + } + + public boolean isBinary() { + if ("string".equals(this.schema.getType()) && "binary".equals(this.schema.getFormat())) { + return true; + } + + return "array".equals(this.schema.getType()) && + "string".equals(this.schema.getItems().getType()) && + "binary".equals(this.schema.getItems().getFormat()); + } + + public boolean isObject() { + return "object".equals(this.schema.getType()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/SwaggerGeneratorContext.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/SwaggerGeneratorContext.java new file mode 100644 index 00000000000..7b1eeda13e0 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/SwaggerGeneratorContext.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator.core; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.servicecomb.swagger.generator.SwaggerConst; + +import jakarta.ws.rs.core.MediaType; + +/** + * Context information to help generate specific open api parts. + */ +public class SwaggerGeneratorContext { + protected static final List SUPPORTED_CONTENT_TYPE + = Arrays.asList(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, MediaType.TEXT_PLAIN, + MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_FORM_URLENCODED, SwaggerConst.WEBSOCKET_TYPE, + MediaType.SERVER_SENT_EVENTS); + + protected static final List SUPPORTED_BODY_CONTENT_TYPE + = Arrays.asList(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, + MediaType.TEXT_PLAIN); + + protected static final List SUPPORTED_FORM_CONTENT_TYPE + = Arrays.asList(MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_FORM_URLENCODED); + + protected List supportedConsumes; + + protected List supportedProduces; + + public SwaggerGeneratorContext() { + supportedConsumes = new ArrayList<>(); + supportedConsumes.addAll(SUPPORTED_CONTENT_TYPE); + supportedProduces = new ArrayList<>(); + supportedProduces.addAll(SUPPORTED_CONTENT_TYPE); + } + + public SwaggerGeneratorContext(SwaggerGeneratorContext parent) { + supportedConsumes = new ArrayList<>(); + supportedConsumes.addAll(parent.supportedConsumes); + supportedProduces = new ArrayList<>(); + supportedProduces.addAll(parent.supportedProduces); + } + + public void updateProduces(List produces) { + checkMediaTypeValid(produces); + supportedProduces.clear(); + supportedProduces.addAll(produces); + } + + public void updateConsumes(List consumes) { + checkMediaTypeValid(consumes); + supportedConsumes.clear(); + supportedConsumes.addAll(consumes); + } + + public List getSupportedConsumes() { + return supportedConsumes; + } + + public List getSupportedProduces() { + return supportedProduces; + } + + private void checkMediaTypeValid(List produces) { + for (String produce : produces) { + if (!SUPPORTED_CONTENT_TYPE.contains(produce)) { + throw new IllegalArgumentException("Not support media type " + produce); + } + } + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/model/HttpParameterType.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/model/HttpParameterType.java new file mode 100644 index 00000000000..00254e99b3f --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/model/HttpParameterType.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.swagger.generator.core.model; + +import java.util.Locale; + +import org.apache.commons.lang3.StringUtils; + +import io.swagger.v3.oas.annotations.enums.ParameterIn; + +public enum HttpParameterType { + /** io.swagger.v3.oas.annotations.enums.ParameterIn.PATH */ + PATH, + /** io.swagger.v3.oas.annotations.enums.ParameterIn.QUERY */ + QUERY, + /** io.swagger.v3.oas.annotations.enums.ParameterIn.HEADER */ + HEADER, + /** io.swagger.v3.oas.annotations.enums.ParameterIn.COOKIE */ + COOKIE, + + /** request bodies: application/x-www-form-urlencoded, multipart/form-data */ + FORM, + + /** request bodies: application/json, etc. */ + BODY; + + public static HttpParameterType from(ParameterIn in) { + return switch (in) { + case COOKIE -> HttpParameterType.COOKIE; + case QUERY -> HttpParameterType.QUERY; + case HEADER -> HttpParameterType.HEADER; + case PATH -> HttpParameterType.PATH; + default -> null; + }; + } + + public static HttpParameterType parse(String value) { + if (StringUtils.isEmpty(value)) { + return null; + } + + return HttpParameterType.valueOf(value.toUpperCase(Locale.US)); + } + + public static boolean isBodyParameter(HttpParameterType type) { + return BODY == type || FORM == type; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/model/SwaggerOperation.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/model/SwaggerOperation.java new file mode 100644 index 00000000000..5aedab55f4d --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/model/SwaggerOperation.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.swagger.generator.core.model; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem.HttpMethod; + +public class SwaggerOperation { + private final OpenAPI swagger; + + private final String path; + + private final HttpMethod httpMethod; + + private final Operation operation; + + public SwaggerOperation(OpenAPI swagger, String path, HttpMethod httpMethod, Operation operation) { + this.swagger = swagger; + this.path = path; + this.httpMethod = httpMethod; + this.operation = operation; + } + + public int parameterCount() { + int result = 0; + if (operation.getRequestBody() != null) { + result++; + } + if (operation.getParameters() != null) { + result += operation.getParameters().size(); + } + return result; + } + + public OpenAPI getSwagger() { + return swagger; + } + + public String getPath() { + return path; + } + + public HttpMethod getHttpMethod() { + return httpMethod; + } + + public Operation getOperation() { + return operation; + } + + public String getOperationId() { + return operation.getOperationId(); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/model/SwaggerOperations.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/model/SwaggerOperations.java new file mode 100644 index 00000000000..487685b228f --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/model/SwaggerOperations.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.swagger.generator.core.model; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +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.PathItem.HttpMethod; +import io.swagger.v3.oas.models.Paths; + +public class SwaggerOperations { + public static SwaggerOperations generate(Class cls) { + OpenAPI swagger = SwaggerGenerator.create(cls).generate(); + return new SwaggerOperations(swagger); + } + + private final OpenAPI swagger; + + // key is operationId + private final Map operations = new HashMap<>(); + + public SwaggerOperations(OpenAPI swagger) { + this.swagger = swagger; + Paths paths = swagger.getPaths(); + if (paths == null || paths.isEmpty()) { + return; + } + + for (Entry pathEntry : paths.entrySet()) { + for (Entry operationEntry : pathEntry.getValue().readOperationsMap().entrySet()) { + if (StringUtils.isEmpty(operationEntry.getValue().getOperationId())) { + throw new IllegalStateException(String + .format("OperationId can not be empty, path=%s, httpMethod=%s.", + pathEntry.getKey(), operationEntry.getKey())); + } + + SwaggerOperation swaggerOperation = new SwaggerOperation(swagger, pathEntry.getKey(), operationEntry.getKey(), + operationEntry.getValue()); + if (operations.putIfAbsent(operationEntry.getValue().getOperationId(), swaggerOperation) != null) { + throw new IllegalStateException( + "please make sure operationId is unique, duplicated operationId is " + operationEntry.getValue() + .getOperationId()); + } + } + } + } + + public OpenAPI getSwagger() { + return swagger; + } + + public SwaggerOperation findOperation(String operationId) { + return operations.get(operationId); + } + + public Map getOperations() { + return operations; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/AnnotationUtils.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/AnnotationUtils.java new file mode 100644 index 00000000000..532c5efdd33 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/AnnotationUtils.java @@ -0,0 +1,335 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core.processor.annotation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerConst; + +import io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.extensions.Extension; +import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.info.Contact; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.info.License; +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.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.MediaType; + +/** + * Utility class to convert from OpenAPI annotations to models. + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +public final class AnnotationUtils { + private AnnotationUtils() { + + } + + public static List serversModel(Server[] servers) { + if (servers == null) { + return null; + } + return Arrays.stream(servers).map(AnnotationUtils::convertServer).collect(Collectors.toList()); + } + + public static io.swagger.v3.oas.models.servers.Server convertServer(Server server) { + io.swagger.v3.oas.models.servers.Server item = new io.swagger.v3.oas.models.servers.Server(); + item.setUrl(server.url()); + item.setDescription(server.description()); + return item; + } + + public static io.swagger.v3.oas.models.info.Info infoModel(Info infoAnnotation) { + if (infoAnnotation == null) { + return null; + } + + io.swagger.v3.oas.models.info.Info info = new io.swagger.v3.oas.models.info.Info(); + + info.setTitle(infoAnnotation.title()); + info.setVersion(infoAnnotation.version()); + if (StringUtils.isNotEmpty(infoAnnotation.description())) { + info.setDescription(infoAnnotation.description()); + } + if (StringUtils.isNotEmpty(infoAnnotation.termsOfService())) { + info.setTermsOfService(infoAnnotation.termsOfService()); + } + info.setContact(contactModel(infoAnnotation.contact())); + info.setLicense(licenseModel(infoAnnotation.license())); + info.setExtensions(extensionsModel(infoAnnotation.extensions())); + + return info; + } + + public static io.swagger.v3.oas.models.info.License licenseModel(License licenseAnnotation) { + io.swagger.v3.oas.models.info.License license = new io.swagger.v3.oas.models.info.License(); + + if (StringUtils.isNotEmpty(licenseAnnotation.name())) { + license.setName(licenseAnnotation.name()); + } + if (StringUtils.isNotEmpty(licenseAnnotation.url())) { + license.setUrl(licenseAnnotation.url()); + } + + if (StringUtils.isEmpty(license.getName()) && StringUtils.isEmpty(license.getUrl())) { + return null; + } + + return license; + } + + public static io.swagger.v3.oas.models.info.Contact contactModel(Contact contactAnnotation) { + io.swagger.v3.oas.models.info.Contact contact = new io.swagger.v3.oas.models.info.Contact(); + + if (StringUtils.isNotEmpty(contactAnnotation.name())) { + contact.setName(contactAnnotation.name()); + } + if (StringUtils.isNotEmpty(contactAnnotation.url())) { + contact.setUrl(contactAnnotation.url()); + } + if (StringUtils.isNotEmpty(contactAnnotation.email())) { + contact.setEmail(contactAnnotation.email()); + } + + if (StringUtils.isEmpty(contact.getName()) && + StringUtils.isEmpty(contact.getUrl()) && + StringUtils.isEmpty(contact.getEmail())) { + return null; + } + + return contact; + } + + public static List tagsModel(Tag[] tagArray) { + if (tagArray == null) { + return null; + } + + List tags = Arrays.stream(tagArray) + .filter(t -> !t.name().isEmpty()) + .map(AnnotationUtils::tagModel) + .collect(Collectors.toList()); + return tags.isEmpty() ? null : tags; + } + + public static List tagsModel(String[] tagArray) { + if (tagArray == null) { + return null; + } + + List tags = Arrays.stream(tagArray) + .filter(t -> !t.isEmpty()) + .collect(Collectors.toList()); + return tags.isEmpty() ? null : tags; + } + + public static io.swagger.v3.oas.models.tags.Tag tagModel(Tag tagAnnotation) { + io.swagger.v3.oas.models.tags.Tag tag = new io.swagger.v3.oas.models.tags.Tag(); + tag.setName(tagAnnotation.name()); + tag.setDescription(tagAnnotation.description()); + tag.setExternalDocs(externalDocumentationModel(tagAnnotation.externalDocs())); + tag.setExtensions(extensionsModel(tagAnnotation.extensions())); + return tag; + } + + public static io.swagger.v3.oas.models.ExternalDocumentation externalDocumentationModel( + ExternalDocumentation externalDocs) { + io.swagger.v3.oas.models.ExternalDocumentation doc = new io.swagger.v3.oas.models.ExternalDocumentation(); + doc.setUrl(externalDocs.url()); + doc.setDescription(externalDocs.description()); + return doc; + } + + public static Map extensionsModel(Extension[] extensions) { + Map result = new HashMap<>(); + Stream.of(extensions).forEach(e -> result.put(e.name(), extensionPropertiesModel(e.properties()))); + return result; + } + + public static Map extensionPropertiesModel(ExtensionProperty[] properties) { + Map result = new HashMap<>(); + Stream.of(properties).forEach(e -> result.put(e.name(), e.value())); + return result; + } + + public static String responseCodeModel(ApiResponse apiResponse) { + if (StringUtils.isEmpty(apiResponse.responseCode())) { + return "200"; + } + return apiResponse.responseCode(); + } + + public static io.swagger.v3.oas.models.responses.ApiResponses apiResponsesModel(OpenAPI openAPI, + ApiResponses apiResponses) { + io.swagger.v3.oas.models.responses.ApiResponses result = + new io.swagger.v3.oas.models.responses.ApiResponses(); + result.setExtensions(extensionsModel(apiResponses.extensions())); + for (ApiResponse apiResponse : apiResponses.value()) { + result.addApiResponse(responseCodeModel(apiResponse), apiResponseModel(openAPI, apiResponse)); + } + return result; + } + + public static io.swagger.v3.oas.models.responses.ApiResponses apiResponsesModel(OpenAPI openAPI, + ApiResponse[] apiResponses) { + io.swagger.v3.oas.models.responses.ApiResponses result = + new io.swagger.v3.oas.models.responses.ApiResponses(); + for (ApiResponse apiResponse : apiResponses) { + if (result.get(responseCodeModel(apiResponse)) != null) { + throw new IllegalStateException("not support too many ApiResponse with same status code"); + } else { + result.addApiResponse(responseCodeModel(apiResponse), apiResponseModel(openAPI, apiResponse)); + } + } + return result; + } + + public static io.swagger.v3.oas.models.responses.ApiResponse apiResponseModel(OpenAPI openAPI, + ApiResponse apiResponse) { + io.swagger.v3.oas.models.responses.ApiResponse result = + new io.swagger.v3.oas.models.responses.ApiResponse(); + result.setDescription(apiResponse.description()); + result.setContent(contentModel(openAPI, apiResponse.content())); + result.setHeaders(headersModel(openAPI, apiResponse.headers())); + return result; + } + + public static Map headersModel(OpenAPI openAPI, Header[] headers) { + Map result = new HashMap<>(); + for (Header header : headers) { + io.swagger.v3.oas.models.headers.Header model = + new io.swagger.v3.oas.models.headers.Header(); + model.setDescription(header.description()); + model.setSchema(schemaModel(openAPI, header.schema())); + result.put(header.name(), model); + } + return result; + } + + public static io.swagger.v3.oas.models.media.Content contentModel(OpenAPI openAPI, Content[] contents) { + io.swagger.v3.oas.models.media.Content result = new io.swagger.v3.oas.models.media.Content(); + for (io.swagger.v3.oas.annotations.media.Content content : contents) { + String mediaTypeName = mediaTypeModel(content); + MediaType mediaType = result.get(mediaTypeName); + boolean isForm = SwaggerConst.FORM_MEDIA_TYPE.equals(mediaTypeName) || + SwaggerConst.FILE_MEDIA_TYPE.equals(mediaTypeName); + if (mediaType == null) { + mediaType = new MediaType(); + if (isForm) { + io.swagger.v3.oas.models.media.Schema schema = new io.swagger.v3.oas.models.media.Schema(); + schema.setProperties(new HashMap<>()); + schema.addProperty(content.schema().name(), schemaModel(openAPI, content.schema(), content.examples())); + mediaType.setSchema(schema); + } else { + mediaType.setSchema(schemaModel(openAPI, content.schema(), content.examples())); + } + result.addMediaType(mediaTypeName, mediaType); + } else { + if (isForm) { + mediaType.getSchema().addProperty(content.schema().name(), + schemaModel(openAPI, content.schema(), content.examples())); + } else { + throw new IllegalStateException("Not allowed to define duplicated content type for " + mediaTypeName); + } + } + } + return result; + } + + public static io.swagger.v3.oas.models.parameters.RequestBody requestBodyModel(OpenAPI openAPI, + RequestBody requestBody) { + if (requestBody == null || isOperationDefaultRequestBody(requestBody)) { + return null; + } + io.swagger.v3.oas.models.parameters.RequestBody result = new io.swagger.v3.oas.models.parameters.RequestBody(); + result.setContent(AnnotationUtils.contentModel(openAPI, requestBody.content())); + return result; + } + + private static boolean isOperationDefaultRequestBody(RequestBody requestBody) { + return "".equals(requestBody.description()) && requestBody.content().length == 0 + && !requestBody.required() && requestBody.extensions().length == 0; + } + + private static String mediaTypeModel(io.swagger.v3.oas.annotations.media.Content content) { + if (StringUtils.isEmpty(content.mediaType())) { + return SwaggerConst.DEFAULT_MEDIA_TYPE; + } + return content.mediaType(); + } + + public static io.swagger.v3.oas.models.media.Schema schemaModel(OpenAPI openAPI, Schema schema) { + if (schema.implementation() != Void.class) { + io.swagger.v3.oas.models.media.Schema result = + SwaggerUtils.resolveTypeSchemas(openAPI, schema.implementation()); + result.setDescription(schema.description()); + result.setExample(schema.example()); + result.setNullable(schema.nullable()); + return result; + } + + io.swagger.v3.oas.models.media.Schema result = + new io.swagger.v3.oas.models.media.Schema(); + result.setType(schema.type()); + result.setFormat(schema.format()); + result.setDescription(schema.description()); + result.setExample(schema.example()); + result.setNullable(schema.nullable()); + return result; + } + + public static io.swagger.v3.oas.models.media.Schema schemaModel(OpenAPI openAPI, Schema schema, + ExampleObject[] exampleObjects) { + io.swagger.v3.oas.models.media.Schema result = schemaModel(openAPI, schema); + List examples = new ArrayList<>(); + for (ExampleObject exampleObject : exampleObjects) { + examples.add(exampleObject.name() + ":" + exampleObject.value()); + } + result.setExamples(examples); + return result; + } + + public static io.swagger.v3.oas.models.Operation operationModel(OpenAPI openAPI, Operation apiOperationAnnotation) { + io.swagger.v3.oas.models.Operation result = new io.swagger.v3.oas.models.Operation(); + result.setSummary(apiOperationAnnotation.summary()); + result.setDescription(apiOperationAnnotation.description()); + result.setExtensions(extensionsModel(apiOperationAnnotation.extensions())); + result.setResponses(apiResponsesModel(openAPI, apiOperationAnnotation.responses())); + result.setOperationId(apiOperationAnnotation.operationId()); + result.setTags(tagsModel(apiOperationAnnotation.tags())); + result.setRequestBody(requestBodyModel(openAPI, apiOperationAnnotation.requestBody())); + return result; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiResponseMethodProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiResponseMethodProcessor.java new file mode 100644 index 00000000000..3f6850a2128 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiResponseMethodProcessor.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.swagger.generator.core.processor.annotation; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.springframework.util.CollectionUtils; + +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.responses.ApiResponse; + +public class ApiResponseMethodProcessor implements MethodAnnotationProcessor { + @Override + public Type getProcessType() { + return ApiResponse.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ApiResponse apiResponse) { + List produces = new ArrayList<>(); + String responseCode = apiResponse.responseCode(); + if (StringUtils.isEmpty(responseCode) || "default".equals(responseCode)) { + responseCode = SwaggerConst.SUCCESS_KEY; + } + Class type = null; + for (Content content : apiResponse.content()) { + if (StringUtils.isNotEmpty(content.mediaType())) { + produces.add(content.mediaType()); + } + if (content.schema() != null && content.schema().implementation() != Void.class) { + type = content.schema().implementation(); + } + } + operationGenerator.getOperationGeneratorContext().updateResponse(responseCode, + type == null ? null : SwaggerUtils.resolveTypeSchemas(swaggerGenerator.getOpenAPI(), type)); + if (StringUtils.isNotEmpty(apiResponse.description())) { + operationGenerator.getOperationGeneratorContext().updateResponseDescription(responseCode, + apiResponse.description()); + } + for (Header header : apiResponse.headers()) { + if (header.schema() == null || header.schema().implementation() == Void.class) { + throw new IllegalArgumentException("@ApiResponse header schema implementation must be defined."); + } + if (StringUtils.isEmpty(header.name())) { + throw new IllegalArgumentException("@ApiResponse header name must be defined."); + } + operationGenerator.getOperationGeneratorContext().updateResponseHeader(responseCode, + header.name(), AnnotationUtils.schemaModel(swaggerGenerator.getOpenAPI(), header.schema())); + } + if (!CollectionUtils.isEmpty(produces)) { + operationGenerator.getOperationGeneratorContext().updateProduces(produces); + } + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiResponsesMethodProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiResponsesMethodProcessor.java new file mode 100644 index 00000000000..383598aa1de --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/ApiResponsesMethodProcessor.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.swagger.generator.core.processor.annotation; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.springframework.util.CollectionUtils; + +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; + + +public class ApiResponsesMethodProcessor implements MethodAnnotationProcessor { + @Override + public Type getProcessType() { + return ApiResponses.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ApiResponses apiResponses) { + List produces = new ArrayList<>(); + for (ApiResponse apiResponse : apiResponses.value()) { + if (StringUtils.isEmpty(apiResponse.responseCode()) || "default".equals(apiResponse.responseCode())) { + throw new IllegalArgumentException("@ApiResponse status code must be defined."); + } + Class type = null; + for (Content content : apiResponse.content()) { + if (StringUtils.isNotEmpty(content.mediaType())) { + produces.add(content.mediaType()); + } + if (content.schema() != null && content.schema().implementation() != Void.class) { + type = content.schema().implementation(); + } + } + operationGenerator.getOperationGeneratorContext().updateResponse(apiResponse.responseCode(), + type == null ? null : SwaggerUtils.resolveTypeSchemas(swaggerGenerator.getOpenAPI(), type)); + if (StringUtils.isNotEmpty(apiResponse.description())) { + operationGenerator.getOperationGeneratorContext().updateResponseDescription(apiResponse.responseCode(), + apiResponse.description()); + } + for (Header header : apiResponse.headers()) { + if (header.schema() == null || header.schema().implementation() == Void.class) { + throw new IllegalArgumentException("@ApiResponse header schema implementation must be defined."); + } + if (StringUtils.isEmpty(header.name())) { + throw new IllegalArgumentException("@ApiResponse header name must be defined."); + } + operationGenerator.getOperationGeneratorContext().updateResponseHeader(apiResponse.responseCode(), + header.name(), AnnotationUtils.schemaModel(swaggerGenerator.getOpenAPI(), header.schema())); + } + } + if (!CollectionUtils.isEmpty(produces)) { + operationGenerator.getOperationGeneratorContext().updateProduces(produces); + } + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OpenAPIDefinitionProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OpenAPIDefinitionProcessor.java new file mode 100644 index 00000000000..67406375f13 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OpenAPIDefinitionProcessor.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.swagger.generator.core.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.models.OpenAPI; + +public class OpenAPIDefinitionProcessor implements ClassAnnotationProcessor { + @Override + public Type getProcessType() { + return OpenAPIDefinition.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OpenAPIDefinition definitionAnnotation) { + OpenAPI swagger = swaggerGenerator.getOpenAPI(); + swagger.setServers(AnnotationUtils.serversModel(definitionAnnotation.servers())); + swagger.setTags(AnnotationUtils.tagsModel(definitionAnnotation.tags())); + swagger.setInfo(AnnotationUtils.infoModel(definitionAnnotation.info())); + swagger.setExternalDocs(AnnotationUtils.externalDocumentationModel(definitionAnnotation.externalDocs())); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OperationMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OperationMethodAnnotationProcessor.java new file mode 100644 index 00000000000..86986a90c6a --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OperationMethodAnnotationProcessor.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core.processor.annotation; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.springframework.util.CollectionUtils; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.responses.ApiResponse; + +public class OperationMethodAnnotationProcessor implements MethodAnnotationProcessor { + public Type getProcessType() { + return Operation.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, + OperationGenerator operationGenerator, Operation apiOperationAnnotation) { + io.swagger.v3.oas.models.Operation operation = operationGenerator.getOperation(); + + if (!StringUtils.isEmpty(apiOperationAnnotation.method())) { + operationGenerator.setHttpMethod(apiOperationAnnotation.method()); + } + + if (apiOperationAnnotation.responses() != null) { + // This is a bit different from swagger annotation intention. + // Here we assume @Content defines all supported media type and only one implementation class for one status code. + List produces = new ArrayList<>(); + for (ApiResponse apiResponse : apiOperationAnnotation.responses()) { + if (StringUtils.isEmpty(apiResponse.responseCode()) || "default".equals(apiResponse.responseCode())) { + throw new IllegalArgumentException("@ApiResponse status code must be defined."); + } + Class type = null; + for (Content content : apiResponse.content()) { + if (StringUtils.isNotEmpty(content.mediaType())) { + produces.add(content.mediaType()); + } + if (content.schema() != null && content.schema().implementation() != Void.class) { + type = content.schema().implementation(); + } + } + operationGenerator.getOperationGeneratorContext().updateResponse(apiResponse.responseCode(), + type == null ? null : SwaggerUtils.resolveTypeSchemas(swaggerGenerator.getOpenAPI(), type)); + if (StringUtils.isNotEmpty(apiResponse.description())) { + operationGenerator.getOperationGeneratorContext().updateResponseDescription(apiResponse.responseCode(), + apiResponse.description()); + } + for (Header header : apiResponse.headers()) { + if (header.schema() == null || header.schema().implementation() == Void.class) { + throw new IllegalArgumentException("@ApiResponse header schema implementation must be defined."); + } + if (StringUtils.isEmpty(header.name())) { + throw new IllegalArgumentException("@ApiResponse header name must be defined."); + } + operationGenerator.getOperationGeneratorContext().updateResponseHeader(apiResponse.responseCode(), + header.name(), AnnotationUtils.schemaModel(swaggerGenerator.getOpenAPI(), header.schema())); + } + } + if (!CollectionUtils.isEmpty(produces)) { + operationGenerator.getOperationGeneratorContext().updateProduces(produces); + } + } + + io.swagger.v3.oas.models.Operation specificOperation = + AnnotationUtils.operationModel(swaggerGenerator.getOpenAPI(), apiOperationAnnotation); + if (!StringUtils.isEmpty(specificOperation.getSummary())) { + operation.setSummary(specificOperation.getSummary()); + } + + if (!StringUtils.isEmpty(specificOperation.getDescription())) { + operation.setDescription(specificOperation.getDescription()); + } + + if (!StringUtils.isEmpty(specificOperation.getOperationId())) { + operation.setOperationId(specificOperation.getOperationId()); + } + + if (!CollectionUtils.isEmpty(specificOperation.getExtensions())) { + operation.setExtensions(specificOperation.getExtensions()); + } + + if (!CollectionUtils.isEmpty(specificOperation.getTags())) { + operation.setTags(specificOperation.getTags()); + } + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/RequestBodyMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/RequestBodyMethodAnnotationProcessor.java new file mode 100644 index 00000000000..aeace6a5756 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/RequestBodyMethodAnnotationProcessor.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.swagger.generator.core.processor.annotation; + +import java.lang.reflect.Type; + +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.annotations.parameters.RequestBody; + +public class RequestBodyMethodAnnotationProcessor implements MethodAnnotationProcessor { + @Override + public Type getProcessType() { + return RequestBody.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + RequestBody annotation) { + io.swagger.v3.oas.models.parameters.RequestBody requestBody = + AnnotationUtils.requestBodyModel(swaggerGenerator.getOpenAPI(), annotation); + operationGenerator.getOperation().setRequestBody(requestBody); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/package-info.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/package-info.java new file mode 100644 index 00000000000..f420bafbe7b --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/package-info.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. + */ + +/** + * swagger竟然没提供从annotation生成swagger对象的功能,只好自己做了 + */ +package org.apache.servicecomb.swagger.generator.core.processor.annotation; diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/HttpServletRequestContextRegister.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/HttpServletRequestContextRegister.java new file mode 100644 index 00000000000..8e9f7199685 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/HttpServletRequestContextRegister.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.swagger.generator.core.processor.parameter; + +import java.lang.reflect.Type; + +import jakarta.servlet.http.HttpServletRequest; + +import org.apache.servicecomb.swagger.generator.SwaggerContextRegister; + +public class HttpServletRequestContextRegister implements SwaggerContextRegister { + @Override + public Type getContextType() { + return HttpServletRequest.class; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/ParameterParameterAnnotationProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/ParameterParameterAnnotationProcessor.java new file mode 100644 index 00000000000..12849da4e63 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/ParameterParameterAnnotationProcessor.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.swagger.generator.core.processor.parameter; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerParameterAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; + +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.Explode; +import io.swagger.v3.oas.annotations.enums.ParameterIn; + +public class ParameterParameterAnnotationProcessor + extends SwaggerParameterAnnotationProcessor { + @Override + public Class getProcessType() { + return io.swagger.v3.oas.annotations.Parameter.class; + } + + @Override + public String getParameterName(Parameter annotation) { + if (StringUtils.isNotEmpty(annotation.name())) { + return annotation.name(); + } + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, io.swagger.v3.oas.annotations.Parameter annotation) { + if (annotation.schema() != null + && annotation.schema().implementation() != null + && annotation.schema().implementation() != Void.class) { + parameterGenerator.getParameterGeneratorContext() + .setParameterType(TypeFactory.defaultInstance().constructType(annotation.schema().implementation())); + } + + if (StringUtils.isNotEmpty(getParameterName(annotation))) { + parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); + } + + if (annotation.in() != null && annotation.in() != ParameterIn.DEFAULT) { + parameterGenerator.setHttpParameterType(HttpParameterType.from(annotation.in())); + } + + if (Explode.TRUE.equals(annotation.explode())) { + parameterGenerator.getParameterGeneratorContext().setExplode(true); + } + if (Explode.FALSE.equals(annotation.explode())) { + parameterGenerator.getParameterGeneratorContext().setExplode(false); + } + parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartArrayParameterTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartArrayParameterTypeProcessor.java new file mode 100644 index 00000000000..daf9f584558 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartArrayParameterTypeProcessor.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.swagger.generator.core.processor.parameter; + +import org.apache.servicecomb.swagger.generator.ParameterGenerator; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.FileSchema; +import jakarta.servlet.http.Part; + +public class PartArrayParameterTypeProcessor extends PartParameterTypeProcessor { + @Override + public JavaType getProcessType() { + return TypeFactory.defaultInstance().constructType(Part[].class); + } + + @Override + protected void formParameterSchema(ParameterGenerator parameterGenerator) { + parameterGenerator.getParameterGeneratorContext().setSchema(new ArraySchema().items(new FileSchema())); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartListParameterTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartListParameterTypeProcessor.java new file mode 100644 index 00000000000..8d47dfdc359 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartListParameterTypeProcessor.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.swagger.generator.core.processor.parameter; + +import java.util.List; + +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import jakarta.servlet.http.Part; + +public class PartListParameterTypeProcessor extends PartArrayParameterTypeProcessor { + @Override + public JavaType getProcessType() { + return TypeFactory.defaultInstance().constructType(ParameterizedTypeUtil.make(List.class, Part.class)); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartParameterTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartParameterTypeProcessor.java new file mode 100644 index 00000000000..d35db0df987 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartParameterTypeProcessor.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator.core.processor.parameter; + +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.ParameterTypeProcessor; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.swagger.v3.oas.models.media.FileSchema; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.MediaType; + +public class PartParameterTypeProcessor implements ParameterTypeProcessor { + @Override + public JavaType getProcessType() { + return TypeFactory.defaultInstance().constructType(Part.class); + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator) { + if (!parameterGenerator.getParameterGeneratorContext().getSupportedConsumes() + .contains(MediaType.MULTIPART_FORM_DATA)) { + throw new IllegalArgumentException("Part type must declare consumes " + MediaType.MULTIPART_FORM_DATA); + } + if (parameterGenerator.getHttpParameterType() != HttpParameterType.FORM) { + throw new IllegalArgumentException( + getProcessType().getRawClass().getSimpleName() + " type must declare as form parameter."); + } + if (StringUtils.isEmpty(parameterGenerator.getParameterGeneratorContext().getParameterName())) { + throw new IllegalArgumentException( + "Name is required for parameter " + getProcessType().getRawClass().getSimpleName()); + } + parameterGenerator.getParameterGeneratorContext().updateConsumes(List.of(MediaType.MULTIPART_FORM_DATA)); + + formParameterSchema(parameterGenerator); + } + + protected void formParameterSchema(ParameterGenerator parameterGenerator) { + parameterGenerator.getParameterGeneratorContext().setSchema(new FileSchema()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/RawJsonRequestBodyProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/RawJsonRequestBodyProcessor.java new file mode 100644 index 00000000000..1d64144365f --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/RawJsonRequestBodyProcessor.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.swagger.generator.core.processor.parameter; + +import java.util.Arrays; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerParameterAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; + +import jakarta.ws.rs.core.MediaType; + +public class RawJsonRequestBodyProcessor extends + SwaggerParameterAnnotationProcessor { + @Override + public Class getProcessType() { + return RawJsonRequestBody.class; + } + + @Override + public String getParameterName(RawJsonRequestBody annotation) { + if (StringUtils.isNotEmpty(annotation.value())) { + return annotation.value(); + } else if (StringUtils.isNotEmpty(annotation.name())) { + return annotation.name(); + } + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, RawJsonRequestBody annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.BODY); + + if (StringUtils.isNotEmpty(getParameterName(annotation))) { + parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); + } + + parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); + parameterGenerator.getParameterGeneratorContext().setRawJson(true); + parameterGenerator.getParameterGeneratorContext().updateConsumes(Arrays.asList(MediaType.APPLICATION_JSON, + MediaType.TEXT_PLAIN)); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/RequestBodyParameterAnnotationProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/RequestBodyParameterAnnotationProcessor.java new file mode 100644 index 00000000000..06ab22cae34 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/RequestBodyParameterAnnotationProcessor.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.swagger.generator.core.processor.parameter; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerParameterAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; + +import io.swagger.v3.oas.annotations.parameters.RequestBody; + +public class RequestBodyParameterAnnotationProcessor extends + SwaggerParameterAnnotationProcessor { + @Override + public Class getProcessType() { + return io.swagger.v3.oas.annotations.parameters.RequestBody.class; + } + + @Override + public String getParameterName(RequestBody requestBody) { + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, io.swagger.v3.oas.annotations.parameters.RequestBody annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.BODY); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/ServerWebSocketContextRegister.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/ServerWebSocketContextRegister.java new file mode 100644 index 00000000000..70bb4255096 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/ServerWebSocketContextRegister.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.swagger.generator.core.processor.parameter; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.SwaggerContextRegister; + +import io.vertx.core.http.ServerWebSocket; + +public class ServerWebSocketContextRegister implements SwaggerContextRegister { + @Override + public Type getContextType() { + return ServerWebSocket.class; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/CompletableFutureProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/CompletableFutureProcessor.java new file mode 100644 index 00000000000..09fc57537a3 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/CompletableFutureProcessor.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.swagger.generator.core.processor.response; + +import java.util.concurrent.CompletableFuture; + +public class CompletableFutureProcessor extends DefaultResponseTypeProcessor { + public CompletableFutureProcessor() { + extractActualType = true; + } + + @Override + public Class getProcessType() { + return CompletableFuture.class; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/DefaultResponseTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/DefaultResponseTypeProcessor.java new file mode 100644 index 00000000000..46788ae8ecc --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/DefaultResponseTypeProcessor.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator.core.processor.response; + +import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.findResponseTypeProcessor; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ResponseTypeProcessor; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import io.swagger.v3.core.util.ReflectionUtils; +import io.swagger.v3.oas.models.media.Schema; +import jakarta.servlet.http.Part; + +public class DefaultResponseTypeProcessor implements ResponseTypeProcessor { + protected boolean extractActualType; + + @Override + public Type getProcessType() { + // not care for this. + return null; + } + + @Override + public Type extractResponseType(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + Type genericResponseType) { + if (extractActualType) { + genericResponseType = ((ParameterizedType) genericResponseType).getActualTypeArguments()[0]; + } + + return doExtractResponseType(genericResponseType); + } + + private Type doExtractResponseType(Type genericResponseType) { + if (!(genericResponseType instanceof ParameterizedType)) { + return genericResponseType; + } + + // eg: + // genericResponseType is CompletableFuture> + // responseType is ResponseEntity + // responseRawType is ResponseEntity + Type responseRawType = genericResponseType; + if (genericResponseType instanceof ParameterizedType) { + responseRawType = ((ParameterizedType) genericResponseType).getRawType(); + } + + ResponseTypeProcessor processor = findResponseTypeProcessor(responseRawType); + if (responseRawType.equals(processor.getProcessType())) { + return processor.extractResponseType(genericResponseType); + } + + return genericResponseType; + } + + @Override + public Schema process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + Type genericResponseType) { + Type responseType = extractResponseType(swaggerGenerator, operationGenerator, genericResponseType); + if (responseType == null || ReflectionUtils.isVoid(responseType)) { + return null; + } + + if (responseType instanceof Class && Part.class.isAssignableFrom((Class) responseType)) { + responseType = Part.class; + } + + return SwaggerUtils.resolveTypeSchemas(swaggerGenerator.getOpenAPI(), responseType); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/OptionalProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/OptionalProcessor.java new file mode 100644 index 00000000000..6d8b6047c97 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/OptionalProcessor.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.swagger.generator.core.processor.response; + +import java.lang.reflect.Type; +import java.util.Optional; + +public class OptionalProcessor extends DefaultResponseTypeProcessor { + public OptionalProcessor() { + extractActualType = true; + } + + @Override + public Type getProcessType() { + return Optional.class; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/PublisherProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/PublisherProcessor.java new file mode 100644 index 00000000000..8388477dca5 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/PublisherProcessor.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.swagger.generator.core.processor.response; + +import java.lang.reflect.Type; +import java.util.List; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.reactivestreams.Publisher; + +import io.swagger.v3.oas.models.media.Schema; +import jakarta.ws.rs.core.MediaType; + +public class PublisherProcessor extends DefaultResponseTypeProcessor { + public PublisherProcessor() { + extractActualType = true; + } + + @Override + public Type getProcessType() { + return Publisher.class; + } + + @Override + public Schema process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + Type genericResponseType) { + operationGenerator.getOperationGeneratorContext() + .updateProduces(List.of(MediaType.SERVER_SENT_EVENTS)); + return super.process(swaggerGenerator, operationGenerator, genericResponseType); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/unittest/UnitTestSwaggerUtils.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/unittest/UnitTestSwaggerUtils.java new file mode 100644 index 00000000000..c3d6270424e --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/unittest/UnitTestSwaggerUtils.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core.unittest; + +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.junit.jupiter.api.Assertions; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; + +import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.oas.models.OpenAPI; + +public final class UnitTestSwaggerUtils { + private static final ObjectWriter writer = Yaml.pretty(); + + private UnitTestSwaggerUtils() { + } + + public static String loadExpect(String resPath) { + URL url = Thread.currentThread().getContextClassLoader().getResource(resPath); + if (url == null) { + return "can not found res " + resPath; + } + + try { + return IOUtils.toString(url, StandardCharsets.UTF_8); + } catch (IOException e) { + return e.getMessage(); + } + } + + public static String pretty(OpenAPI swagger) { + try { + return writer.writeValueAsString(swagger); + } catch (JsonProcessingException e) { + throw new Error(e); + } + } + + public static OpenAPI parse(String content) { + try { + return Yaml.mapper().readValue(content, OpenAPI.class); + } catch (Exception e) { + return new OpenAPI(); + // throw new Error(e); + } + } + + public static SwaggerGenerator testSwagger(String resPath, Class cls, String... methods) { + SwaggerGenerator generator = SwaggerGenerator.create(cls); + generator.replaceMethodWhiteList(methods); + + OpenAPI swagger = generator.generate(); + String schema = pretty(swagger).trim(); + + String expectSchema = loadExpect(resPath).replace("\r\n", "\n").trim(); + int offset = expectSchema.indexOf("---\nopenapi: 3.0.1"); + if (offset > 0) { + expectSchema = expectSchema.substring(offset + 4); + } + + try { + ObjectMapper yaml = new ObjectMapper(new YAMLFactory()); + JsonNode expected = yaml.readTree(expectSchema); + JsonNode actual = yaml.readTree(schema); + + if (!actual.equals(expected)) { + ObjectMapper json = new ObjectMapper(); + String expectedPretty = json.writerWithDefaultPrettyPrinter().writeValueAsString(expected); + String actualPretty = json.writerWithDefaultPrettyPrinter().writeValueAsString(actual); + Assertions.fail("OpenAPI mismatch.\n=== EXPECTED ===\n" + expectedPretty + "\n=== ACTUAL ===\n" + actualPretty); + } + } catch (Exception e) { + Assertions.fail("Failed to parse/compare OpenAPI YAML: " + e.getMessage(), e); + } + + return generator; + } + + public static Throwable getException(Class cls, String... methods) { + try { + SwaggerGenerator generator = SwaggerGenerator.create(cls); + generator.replaceMethodWhiteList(methods); + + generator.generate(); + } catch (Throwable e) { + return e; + } + + // 不允许成功 + Assertions.assertEquals("not allowed run to here", "run to here"); + return null; + } + + public static void testException(String expectMsgLevel1, String expectMsgLevel2, String expectMsgLevel3, Class cls, + String... methods) { + Throwable exception = getException(cls, methods); + Assertions.assertEquals(expectMsgLevel1, exception.getMessage()); + Assertions.assertEquals(expectMsgLevel2, exception.getCause().getMessage()); + Assertions.assertEquals(expectMsgLevel3, exception.getCause().getCause().getMessage()); + } + + public static void testException(String expectMsgLevel1, String expectMsgLevel2, Class cls, String... methods) { + Throwable exception = getException(cls, methods); + Assertions.assertEquals(expectMsgLevel1, exception.getMessage()); + Assertions.assertEquals(expectMsgLevel2, exception.getCause().getMessage()); + } + + public static void testException(String expectMsg, Class cls, String... methods) { + Throwable exception = getException(cls, methods); + Assertions.assertEquals(expectMsg, exception.getMessage()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/unittest/package-info.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/unittest/package-info.java new file mode 100644 index 00000000000..909b911e506 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/unittest/package-info.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. + */ + +/** + * 专用于ut场景 + */ +package org.apache.servicecomb.swagger.generator.core.unittest; diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/MethodUtils.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/MethodUtils.java new file mode 100644 index 00000000000..bf2d012cf9d --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/utils/MethodUtils.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core.utils; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; + +import io.swagger.v3.oas.annotations.Operation; + + +public class MethodUtils { + /** + * Get the methods of cls which are valid for generating Swagger schema. + * @param cls The REST interface class, or so called "controller" class, to be analysed. + * @return the valid methods to be used to generate Swagger schema, sorted by their Swagger operation name. + */ + public static List findSwaggerMethods(Class cls) { + Method[] methods = cls.getMethods(); + List result = new ArrayList<>(methods.length); + + for (Method m : methods) { + if (!isSkipMethod(cls, m)) { + result.add(m); + } + } + + // order of cls.getMethods() is undefined and not stable + // so we must sort them first to make generation is stable + result.sort(Comparator.comparing(MethodUtils::findSwaggerMethodName)); + return result; + } + + public static Map findSwaggerMethodsMapOfOperationId(Class cls) { + List methods = findSwaggerMethods(cls); + Map result = new HashMap<>(); + methods.forEach((item) -> result.put(findSwaggerMethodName(item), item)); + return result; + } + + /** + * Pick out those methods not proper to be added into the Swagger schema. + * + * @param cls the owner class of the method + * @param method the method to be validate + * @return true if this method should be abandoned; + * false if this method should be added in to Swagger schema + */ + public static boolean isSkipMethod(Class cls, Method method) { + if (method.isDefault()) { + return true; + } + if (method.getDeclaringClass() == Object.class) { + return true; + } + if (method.getDeclaringClass().isInterface() + && !cls.isInterface()) { + // inherited template methods + return true; + } + // skip static method + int modifiers = method.getModifiers(); + if (Modifier.isStatic(modifiers)) { + return true; + } + // skip bridge method + if (method.isBridge()) { + return true; + } + + Operation apiOperation = method.getAnnotation(Operation.class); + return apiOperation != null && apiOperation.hidden(); + } + + /** + * Get the operationId in schema of this method, + * no matter whether it should be hidden. + * @return If the operation name is specified via {@link Operation}, use that one. + * Otherwise the method name is returned. + */ + public static String findSwaggerMethodName(Method method) { + Operation apiOperationAnnotation = method.getAnnotation(Operation.class); + if (apiOperationAnnotation == null || StringUtils.isEmpty(apiOperationAnnotation.operationId())) { + return method.getName(); + } + + return apiOperationAnnotation.operationId(); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/pojo/PojoOperationGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/pojo/PojoOperationGenerator.java new file mode 100644 index 00000000000..ffc7e540a88 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/pojo/PojoOperationGenerator.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.swagger.generator.pojo; + +import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.isContextParameter; + +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.core.AbstractOperationGenerator; +import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; +import org.apache.servicecomb.swagger.generator.core.utils.MethodUtils; + +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.google.common.reflect.TypeToken; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.media.Schema; +import jakarta.ws.rs.HttpMethod; + +public class PojoOperationGenerator extends AbstractOperationGenerator { + public PojoOperationGenerator(AbstractSwaggerGenerator swaggerGenerator, Method method) { + super(swaggerGenerator, method); + } + + @Override + protected void initParameterGenerators() { + List bodyParameters = new ArrayList<>(); + for (java.lang.reflect.Parameter methodParameter : method.getParameters()) { + Type type = TypeToken.of(clazz) + .resolveType(methodParameter.getParameterizedType()) + .getType(); + ParameterGenerator parameterGenerator = new ParameterGenerator( + this, Collections.emptyMap(), methodParameter, + TypeFactory.defaultInstance().constructType(type)); + validateParameter(parameterGenerator.getGenericType()); + if (isContextParameter(parameterGenerator.getGenericType())) { + continue; + } + + bodyParameters.add(parameterGenerator); + } + + tryWrapParametersToBody(bodyParameters); + } + + private void tryWrapParametersToBody(List bodyParameters) { + if (bodyParameters.size() == 0) { + return; + } + + if (bodyParameters.size() == 1 && SwaggerUtils.isBean(bodyParameters.get(0).getGenericType())) { + ParameterGenerator parameterGenerator = bodyParameters.get(0); + parameterGenerator.setHttpParameterType(HttpParameterType.BODY); + parameterGenerators.add(parameterGenerator); + return; + } + + wrapParametersToBody(bodyParameters); + } + + private void wrapParametersToBody(List bodyFields) { + // process annotations like parameter name + for (ParameterGenerator parameterGenerator : bodyFields) { + scanMethodParameter(parameterGenerator); + } + + String simpleRef = MethodUtils.findSwaggerMethodName(method) + "Body"; + + Schema bodyModel = new ObjectSchema(); + for (ParameterGenerator parameterGenerator : bodyFields) { + bodyModel.addProperty(parameterGenerator.getParameterGeneratorContext().getParameterName(), + parameterGenerator.getParameterGeneratorContext().getSchema()); + } + + swagger.getComponents().addSchemas(simpleRef, bodyModel); + Schema bodyModelNew = new Schema<>(); + bodyModelNew.set$ref(Components.COMPONENTS_SCHEMAS_REF + simpleRef); + ParameterGenerator newParameterGenerator = new ParameterGenerator(this, simpleRef, bodyModelNew); + newParameterGenerator.setHttpParameterType(HttpParameterType.BODY); + parameterGenerators.add(newParameterGenerator); + } + + @Override + public void correctOperation() { + correctPath(); + correctHttpMethod(); + super.correctOperation(); + } + + protected void correctPath() { + if (StringUtils.isEmpty(path)) { + path = "/" + getOperationId(); + } + } + + protected void correctHttpMethod() { + if (StringUtils.isEmpty(httpMethod)) { + setHttpMethod(HttpMethod.POST); + } + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/pojo/PojoSwaggerGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/pojo/PojoSwaggerGenerator.java new file mode 100644 index 00000000000..0a2e6985aeb --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/pojo/PojoSwaggerGenerator.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.swagger.generator.pojo; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; + +import jakarta.ws.rs.core.MediaType; + +public class PojoSwaggerGenerator extends AbstractSwaggerGenerator { + protected static final List SUPPORTED_CONTENT_TYPE + = Arrays.asList(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, MediaType.TEXT_PLAIN); + + public PojoSwaggerGenerator(Class cls) { + super(cls); + swaggerGeneratorContext.updateConsumes(SUPPORTED_CONTENT_TYPE); + swaggerGeneratorContext.updateProduces(SUPPORTED_CONTENT_TYPE); + } + + @SuppressWarnings("unchecked") + @Override + public T createOperationGenerator(Method method) { + return (T) new PojoOperationGenerator(this, method); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/pojo/PojoSwaggerGeneratorFactory.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/pojo/PojoSwaggerGeneratorFactory.java new file mode 100644 index 00000000000..05053f8609d --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/pojo/PojoSwaggerGeneratorFactory.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.swagger.generator.pojo; + +import java.lang.annotation.Annotation; + +import jakarta.ws.rs.Path; + +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PojoSwaggerGeneratorFactory implements SwaggerGeneratorFactory { + private static final Logger LOGGER = LoggerFactory.getLogger(PojoSwaggerGeneratorFactory.class); + + @Override + public int getOrder() { + return Integer.MAX_VALUE; + } + + @Override + public boolean canProcess(Class cls) { + for (Annotation annotation : cls.getAnnotations()) { + // we check the annotations by class name to avoid importing extra dependencies in this module + if (annotation instanceof Path + || "org.springframework.web.bind.annotation.RequestMapping" + .equals(annotation.annotationType().getCanonicalName())) { + LOGGER.info( + "There is @RequestMapping or @Path annotation on the REST interface class, but POJO swagger generator is chosen. " + + "If this is unexpected, maybe you should check your dependency jar files."); + } + } + return true; + } + + @Override + public SwaggerGenerator create(Class cls) { + return new PojoSwaggerGenerator(cls); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/rest/RestOperationGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/rest/RestOperationGenerator.java new file mode 100644 index 00000000000..9ce1c1ac9e6 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/rest/RestOperationGenerator.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator.rest; + +import java.lang.reflect.Method; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.core.AbstractOperationGenerator; +import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; + +public abstract class RestOperationGenerator extends AbstractOperationGenerator { + public RestOperationGenerator(AbstractSwaggerGenerator swaggerGenerator, Method method) { + super(swaggerGenerator, method); + } + + @Override + public void correctOperation() { + checkPath(); + correctPath(); + super.correctOperation(); + } + + protected void checkPath() { + if (StringUtils.isEmpty(path) + && StringUtils.isEmpty(SwaggerUtils.getBasePath(swagger))) { + throw new IllegalStateException("Path must not both be empty in class and method"); + } + } + + protected void correctPath() { + if (StringUtils.isEmpty(path)) { + path = "/"; + } + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/rest/RestSwaggerGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/rest/RestSwaggerGenerator.java new file mode 100644 index 00000000000..a6811f684ab --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/rest/RestSwaggerGenerator.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator.rest; + +import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; + +public abstract class RestSwaggerGenerator extends AbstractSwaggerGenerator { + public RestSwaggerGenerator(Class cls) { + super(cls); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.CommonParameterTypeProcessor b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.CommonParameterTypeProcessor deleted file mode 100644 index c43684941c9..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.CommonParameterTypeProcessor +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -io.servicecomb.swagger.generator.core.processor.parametertype.HttpServletRequestProcessor \ No newline at end of file diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.SwaggerGeneratorContext b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.SwaggerGeneratorContext deleted file mode 100644 index e5601509e20..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.SwaggerGeneratorContext +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -io.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorContext \ No newline at end of file diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/io.swagger.converter.ModelConverter b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/io.swagger.converter.ModelConverter deleted file mode 100644 index e46d2019dc5..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/io.swagger.converter.ModelConverter +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -io.servicecomb.swagger.extend.ModelResolverExt \ No newline at end of file diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/io.swagger.v3.core.converter.ModelConverter b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/io.swagger.v3.core.converter.ModelConverter new file mode 100644 index 00000000000..371283fc5e5 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/io.swagger.v3.core.converter.ModelConverter @@ -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.swagger.extend.ModelResolverExt diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor new file mode 100644 index 00000000000..3b9c9f84002 --- /dev/null +++ b/swagger/swagger-generator/generator-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.swagger.generator.core.processor.annotation.OpenAPIDefinitionProcessor diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor new file mode 100644 index 00000000000..249784556de --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor @@ -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. +# + +org.apache.servicecomb.swagger.generator.core.processor.annotation.OperationMethodAnnotationProcessor +org.apache.servicecomb.swagger.generator.core.processor.annotation.ApiResponsesMethodProcessor +org.apache.servicecomb.swagger.generator.core.processor.annotation.ApiResponseMethodProcessor +org.apache.servicecomb.swagger.generator.core.processor.annotation.RequestBodyMethodAnnotationProcessor diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor new file mode 100644 index 00000000000..eba9ece3fd7 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.apache.servicecomb.swagger.generator.core.processor.parameter.RequestBodyParameterAnnotationProcessor +org.apache.servicecomb.swagger.generator.core.processor.parameter.ParameterParameterAnnotationProcessor +org.apache.servicecomb.swagger.generator.core.processor.parameter.RawJsonRequestBodyProcessor diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterTypeProcessor b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterTypeProcessor new file mode 100644 index 00000000000..130af5d2a49 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterTypeProcessor @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.apache.servicecomb.swagger.generator.core.processor.parameter.PartParameterTypeProcessor +org.apache.servicecomb.swagger.generator.core.processor.parameter.PartArrayParameterTypeProcessor +org.apache.servicecomb.swagger.generator.core.processor.parameter.PartListParameterTypeProcessor diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor new file mode 100644 index 00000000000..9abf37e8c9d --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.apache.servicecomb.swagger.generator.core.processor.response.CompletableFutureProcessor +org.apache.servicecomb.swagger.generator.core.processor.response.OptionalProcessor +org.apache.servicecomb.swagger.generator.core.processor.response.PublisherProcessor diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister new file mode 100644 index 00000000000..db52891e086 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister @@ -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.swagger.generator.core.processor.parameter.HttpServletRequestContextRegister +org.apache.servicecomb.swagger.generator.core.processor.parameter.ServerWebSocketContextRegister diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGenerator b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGenerator new file mode 100644 index 00000000000..35c874ef2a2 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGenerator @@ -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.swagger.generator.pojo.PojoSwaggerGenerator diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory new file mode 100644 index 00000000000..ba83d84d0d5 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory @@ -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.swagger.generator.pojo.PojoSwaggerGeneratorFactory diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestApiImplicitParams.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestApiImplicitParams.java deleted file mode 100644 index 6f0295cc861..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestApiImplicitParams.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import org.junit.Assert; -import org.junit.Test; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.swagger.converter.ConverterMgr; -import io.servicecomb.swagger.generator.core.schema.User; -import io.servicecomb.swagger.generator.core.unittest.SwaggerGeneratorForTest; -import io.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorContext; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.models.Operation; -import io.swagger.models.Path; -import io.swagger.models.Swagger; -import io.swagger.models.parameters.Parameter; - -public class TestApiImplicitParams { - SwaggerGeneratorContext context = new PojoSwaggerGeneratorContext(); - - interface ApiImplicitParamsAnnotation { - @ApiImplicitParams( - value = {@ApiImplicitParam( - paramType = "body", - name = "body", - dataType = "io.servicecomb.swagger.generator.core.schema.User")}) - void testBody(); - } - - @Test - public void testBody() { - SwaggerGenerator swaggerGenerator = - new SwaggerGeneratorForTest(context, ApiImplicitParamsAnnotation.class); - swaggerGenerator.generate(); - - Swagger swagger = swaggerGenerator.getSwagger(); - Path path = swagger.getPaths().get("/testBody"); - Operation operation = path.getOperations().get(0); - Parameter parameter = operation.getParameters().get(0); - - JavaType javaType = ConverterMgr.findJavaType(swaggerGenerator, parameter); - Assert.assertEquals(User.class, javaType.getRawClass()); - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestApiOperation.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestApiOperation.java deleted file mode 100644 index a76660f7849..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestApiOperation.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import java.util.Arrays; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.swagger.generator.core.unittest.SwaggerGeneratorForTest; -import io.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; -import io.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorContext; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.Extension; -import io.swagger.annotations.ExtensionProperty; -import io.swagger.annotations.ResponseHeader; -import io.swagger.models.Operation; -import io.swagger.models.Path; -import io.swagger.models.Response; -import io.swagger.models.Scheme; -import io.swagger.models.Swagger; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.Property; - -public class TestApiOperation { - SwaggerGeneratorContext context = new PojoSwaggerGeneratorContext(); - - interface ApiOperationAnnotation { - @ApiOperation( - value = "summary", - notes = "notes", - tags = {"tag1", "tag2"}, - httpMethod = "GET", - nickname = "test", - produces = "application/json", - consumes = "application/json", - protocols = "http,https", - code = 202, - responseHeaders = {@ResponseHeader(name = "h1", response = int.class)}, - extensions = {@Extension( - name = "x-tagA", - properties = {@ExtensionProperty(name = "x-tagAExt", value = "value of tagAExt")})}) - void testBase(); - - @ApiOperation(value = "aaa", code = 202, response = String.class) - int testPrimitive(); - - @ApiOperation(value = "aaa", response = Integer.class, responseContainer = "Map") - int testMap(); - - @ApiOperation(value = "aaa", response = Integer.class, responseContainer = "List") - int testList(); - - @ApiOperation(value = "aaa", response = Integer.class, responseContainer = "Set") - int testSet(); - } - - interface UnknownResponseContainer { - @ApiOperation(value = "aaa", response = Integer.class, responseContainer = "xxx") - int testUnknown(); - } - - @Test - public void testApiOperation() { - SwaggerGenerator swaggerGenerator = - new SwaggerGeneratorForTest(context, ApiOperationAnnotation.class); - swaggerGenerator.generate(); - - Swagger swagger = swaggerGenerator.getSwagger(); - testBase(swagger.getPath("/test")); - testPrimitive(swagger.getPath("/testPrimitive")); - testMap(swagger.getPath("/testMap")); - testList(swagger.getPath("/testList")); - testSet(swagger.getPath("/testSet")); - } - - @Test - public void testUnknown() { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.core.TestApiOperation$UnknownResponseContainer:testUnknown", - "not support responseContainer xxx", - context, - UnknownResponseContainer.class); - } - - private void testSet(Path path) { - Operation operation = path.getPost(); - Property result200 = operation.getResponses().get("200").getSchema(); - Assert.assertEquals(ArrayProperty.class, result200.getClass()); - Assert.assertEquals(true, ((ArrayProperty) result200).getUniqueItems()); - } - - private void testList(Path path) { - Operation operation = path.getPost(); - Property result200 = operation.getResponses().get("200").getSchema(); - Assert.assertEquals(ArrayProperty.class, result200.getClass()); - Assert.assertEquals(null, ((ArrayProperty) result200).getUniqueItems()); - } - - private void testMap(Path path) { - Operation operation = path.getPost(); - Property result200 = operation.getResponses().get("200").getSchema(); - Assert.assertEquals(MapProperty.class, result200.getClass()); - } - - private void testPrimitive(Path path) { - Operation operation = path.getPost(); - - Assert.assertEquals(2, operation.getResponses().size()); - - Property result200 = operation.getResponses().get("200").getSchema(); - Assert.assertEquals("integer", result200.getType()); - Assert.assertEquals("int32", result200.getFormat()); - - Property result202 = operation.getResponses().get("202").getSchema(); - Assert.assertEquals("string", result202.getType()); - Assert.assertEquals(null, result202.getFormat()); - } - - private void testBase(Path path) { - Assert.assertEquals(1, path.getOperations().size()); - - Operation operation = path.getGet(); - - Assert.assertEquals("summary", operation.getSummary()); - Assert.assertEquals("notes", operation.getDescription()); - Assert.assertEquals(Arrays.asList("tag1", "tag2"), operation.getTags()); - Assert.assertEquals(Arrays.asList("application/json"), operation.getProduces()); - Assert.assertEquals(Arrays.asList("application/json"), operation.getConsumes()); - Assert.assertEquals(Arrays.asList(Scheme.HTTP, Scheme.HTTPS), operation.getSchemes()); - - Map responseMap = operation.getResponses(); - Assert.assertEquals(2, responseMap.size()); - - Response response = responseMap.get(SwaggerConst.SUCCESS_KEY); - Assert.assertNotNull(response); - Assert.assertEquals(null, response.getSchema()); - - response = responseMap.get("202"); - Assert.assertNotNull(response); - Assert.assertEquals(null, response.getSchema()); - - Assert.assertEquals(1, response.getHeaders().size()); - Assert.assertEquals("integer", response.getHeaders().get("h1").getType()); - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestApiResponse.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestApiResponse.java deleted file mode 100644 index ef804f11670..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestApiResponse.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.swagger.converter.ConverterMgr; -import io.servicecomb.swagger.generator.core.unittest.SwaggerGeneratorForTest; -import io.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorContext; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import io.swagger.annotations.ResponseHeader; -import io.swagger.models.Operation; -import io.swagger.models.Path; -import io.swagger.models.Response; -import io.swagger.models.Swagger; -import io.swagger.models.properties.Property; - -public class TestApiResponse { - SwaggerGeneratorContext context = new PojoSwaggerGeneratorContext(); - - interface ApiResponseAnnotation { - @ApiResponse( - responseHeaders = {@ResponseHeader(name = "k1", response = int.class), - @ResponseHeader(name = "k2", response = String.class)}, - code = 200, - message = "") - void testApiResponseHeader(); - - @ResponseHeader(name = "k1", response = int.class) - void testResponseHeader(); - - @ApiResponse( - code = 200, - response = int.class, - message = "msg") - void testSingle(); - - @ApiResponses({@ApiResponse(code = 200, response = int.class, message = "msg1"), - @ApiResponse(code = 301, response = String.class, message = "msg2")}) - void testMulti(); - } - - @Test - public void testBody() { - SwaggerGenerator swaggerGenerator = - new SwaggerGeneratorForTest(context, ApiResponseAnnotation.class); - swaggerGenerator.generate(); - - checkApiResponseHeader(swaggerGenerator); - checkResponseHeader(swaggerGenerator); - checkSingle(swaggerGenerator); - checkMulti(swaggerGenerator); - } - - private void checkResponseHeader(SwaggerGenerator generator) { - Swagger swagger = generator.getSwagger(); - - Path path = swagger.getPaths().get("/testResponseHeader"); - Operation operation = path.getOperations().get(0); - Assert.assertEquals("testResponseHeader", operation.getOperationId()); - - Response response = operation.getResponses().get("200"); - Property property = response.getHeaders().get("k1"); - Assert.assertEquals(Integer.class, ConverterMgr.findJavaType(generator, property).getRawClass()); - } - - private void checkApiResponseHeader(SwaggerGenerator generator) { - Swagger swagger = generator.getSwagger(); - - Path path = swagger.getPaths().get("/testApiResponseHeader"); - Operation operation = path.getOperations().get(0); - Assert.assertEquals("testApiResponseHeader", operation.getOperationId()); - - Response response = operation.getResponses().get("200"); - Property property = response.getHeaders().get("k1"); - Assert.assertEquals(Integer.class, ConverterMgr.findJavaType(generator, property).getRawClass()); - - property = response.getHeaders().get("k2"); - Assert.assertEquals(String.class, ConverterMgr.findJavaType(generator, property).getRawClass()); - } - - public void checkSingle(SwaggerGenerator generator) { - Swagger swagger = generator.getSwagger(); - - Path path = swagger.getPaths().get("/testSingle"); - Operation operation = path.getOperations().get(0); - Assert.assertEquals("testSingle", operation.getOperationId()); - - Response response = operation.getResponses().get("200"); - Assert.assertEquals(Integer.class, ConverterMgr.findJavaType(generator, response.getSchema()).getRawClass()); - } - - public void checkMulti(SwaggerGenerator generator) { - Swagger swagger = generator.getSwagger(); - - Path path = swagger.getPaths().get("/testMulti"); - - Operation operation = path.getOperations().get(0); - Assert.assertEquals("testMulti", operation.getOperationId()); - - Response response = operation.getResponses().get("200"); - Assert.assertEquals(Integer.class, ConverterMgr.findJavaType(generator, response.getSchema()).getRawClass()); - - response = operation.getResponses().get("301"); - Assert.assertEquals(String.class, ConverterMgr.findJavaType(generator, response.getSchema()).getRawClass()); - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestArrayType.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestArrayType.java deleted file mode 100644 index 02aa032bfd6..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestArrayType.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core; - -import java.lang.reflect.Method; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.foundation.common.utils.ReflectUtils; -import io.servicecomb.swagger.generator.core.schema.ArrayType; -import io.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import javassist.CtClass; -import javassist.CtMethod; - -public class TestArrayType { - @Test - public void test() throws Exception { - SwaggerGenerator generator = UnitTestSwaggerUtils.generateSwagger(ArrayType.class); - Class cls = ClassUtils.getOrCreateInterface(generator); - Method method = ReflectUtils.findMethod(cls, "testBytes"); - - Class param = (Class) method.getParameters()[0].getParameterizedType(); - Assert.assertEquals(byte[].class, param.getField("value").getType()); - Assert.assertEquals(byte[].class, method.getReturnType()); - } - - public CtMethod findMethod(CtClass cc, String methodName) { - for (CtMethod method : cc.getMethods()) { - if (methodName.equals(method.getName())) { - return method; - } - } - - return null; - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestClassUtils.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestClassUtils.java deleted file mode 100644 index 42ee1ddde43..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestClassUtils.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import static org.hamcrest.core.Is.is; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.ws.rs.Path; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.swagger.generator.core.schema.User; -import io.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.swagger.annotations.SwaggerDefinition; - -@SwaggerDefinition -public class TestClassUtils { - // copy from ClassUtils - // do not use JAVA_RESERVED_WORDS in ClassUtils directly - // because that's the test target. - private static final Set JAVA_RESERVED_WORDS = new HashSet<>(); - - static { - JAVA_RESERVED_WORDS.addAll(Arrays.asList("true", - "false", - "null", - "abstract", - "continue", - "for", - "new", - "switch", - "assert", - "default", - "goto", - "package", - "synchronized", - "boolean", - "do", - "if", - "private", - "this", - "break", - "double", - "implements", - "protected", - "throw", - "byte", - "else", - "import", - "public", - "throws", - "case", - "enum", - "instanceof", - "return", - "transient", - "catch", - "extends", - "int", - "short", - "try", - "char", - "final", - "interface", - "static", - "void", - "class", - "finally", - "long", - "strictfp", - "volatile", - "const", - "float", - "native", - "super", - "while")); - } - - @Test - public void testHasAnnotation() { - Assert.assertEquals(true, ClassUtils.hasAnnotation(TestClassUtils.class, SwaggerDefinition.class)); - Assert.assertEquals(true, ClassUtils.hasAnnotation(TestClassUtils.class, Test.class)); - - Assert.assertEquals(false, ClassUtils.hasAnnotation(TestClassUtils.class, Path.class)); - } - - public static class Impl { - public List getUser(List names) { - return null; - } - } - - @Test - public void testCreateInterface() { - SwaggerGenerator generator = UnitTestSwaggerUtils.generateSwagger(Impl.class); - Class intf = ClassUtils.getOrCreateInterface(generator); - - Assert.assertEquals("gen.swagger.ImplIntf", intf.getName()); - Assert.assertEquals(1, intf.getMethods().length); - - Method method = intf.getMethods()[0]; - Assert.assertEquals("getUser", method.getName()); - - Assert.assertEquals("gen.swagger.getUser.names", method.getGenericParameterTypes()[0].getTypeName()); - Assert.assertEquals("java.util.List", - method.getGenericReturnType().getTypeName()); - } - - @Test - public void testCorrectClassNameInTheCrossed() { - String result = ClassUtils.correctClassName("a-b"); - Assert.assertThat(result, is("a_b")); - - result = ClassUtils.correctClassName("a.a-b"); - Assert.assertThat(result, is("a.a_b")); - } - - @Test - public void testCorrectClassNameStartWithNumber() { - String result = ClassUtils.correctClassName("100"); - Assert.assertThat(result, is("_100")); - - result = ClassUtils.correctClassName("a.100"); - Assert.assertThat(result, is("a._100")); - } - - @Test - public void testCorrectClassNameReservedWords() { - String name = String.join(".", JAVA_RESERVED_WORDS); - String expectResult = "_" + String.join("._", JAVA_RESERVED_WORDS); - - String result = ClassUtils.correctClassName(name); - Assert.assertThat(result, is(expectResult)); - } - - @Test - public void testCorrectClassNameEmptyPart() { - String result = ClassUtils.correctClassName("..a..a.."); - Assert.assertThat(result, is("_._.a._.a._._")); - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestInvalidType.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestInvalidType.java deleted file mode 100644 index 9f8e0cb72dc..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestInvalidType.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core; - -import org.junit.Test; - -import io.servicecomb.swagger.generator.core.schema.InvalidType; -import io.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; -import io.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorContext; - -public class TestInvalidType { - static SwaggerGeneratorContext context = new PojoSwaggerGeneratorContext(); - - @Test - public void testIntf() { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.core.schema.InvalidType:testIntf", - "[simple type, class io.servicecomb.swagger.generator.core.schema.InvalidType$InvalidIntf] is interface. Must be a concrete type.", - context, - InvalidType.class, - "testIntf"); - } - - @Test - public void testAbstractClass() { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.core.schema.InvalidType:testAbstractClass", - "[simple type, class io.servicecomb.swagger.generator.core.schema.InvalidType$InvalidClass] is abstract class. Must be a concrete type.", - context, - InvalidType.class, - "testAbstractClass"); - } - - @Test - public void testObject() { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.core.schema.InvalidType:testObject", - "java.lang.Object not support. Must be a concrete type.", - context, - InvalidType.class, - "testObject"); - } - - @Test - public void testNotClearList() { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.core.schema.InvalidType:testNotClearList", - "java.lang.Object not support. Must be a concrete type.", - context, - InvalidType.class, - "testNotClearList"); - } - - @Test - public void testNotClearSet() { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.core.schema.InvalidType:testNotClearSet", - "java.lang.Object not support. Must be a concrete type.", - context, - InvalidType.class, - "testNotClearSet"); - } - - @Test - public void testNotClearMap() { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.core.schema.InvalidType:testNotClearMap", - "java.lang.Object not support. Must be a concrete type.", - context, - InvalidType.class, - "testNotClearMap"); - } - - @Test - public void testInvalidFieldClass() { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.core.schema.InvalidType:testInvalidFieldClass", - "java.lang.Object not support. Must be a concrete type.", - context, - InvalidType.class, - "testInvalidFieldClass"); - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestParamUtils.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestParamUtils.java deleted file mode 100644 index 0c25f8cdf3f..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestParamUtils.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import static org.mockito.Mockito.when; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.swagger.models.parameters.Parameter; - -public class TestParamUtils { - @Test - public void testGetRawJsonType() { - Parameter param = Mockito.mock(Parameter.class); - Map extensions = new HashMap<>(); - when(param.getVendorExtensions()).thenReturn(extensions); - - extensions.put(SwaggerConst.EXT_RAW_JSON_TYPE, true); - Assert.assertTrue(ClassUtils.isRawJsonType(param)); - - extensions.put(SwaggerConst.EXT_RAW_JSON_TYPE, "test"); - Assert.assertFalse(ClassUtils.isRawJsonType(param)); - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestProperty.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestProperty.java deleted file mode 100644 index a833501120f..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestProperty.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import java.util.Arrays; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.swagger.converter.property.StringPropertyConverter; -import io.servicecomb.swagger.generator.core.unittest.SwaggerGeneratorForTest; -import io.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorContext; -import io.swagger.converter.ModelConverters; -import io.swagger.models.properties.StringProperty; - -public class TestProperty { - SwaggerGeneratorContext context = new PojoSwaggerGeneratorContext(); - - @Test - public void testStringProperty() { - SwaggerGenerator generator = new SwaggerGeneratorForTest(context, null); - - List enums = Arrays.asList("testStringProperty_a", "testStringProperty_b"); - - StringProperty sp = new StringProperty(); - sp._enum(enums); - - StringPropertyConverter spc = new StringPropertyConverter(); - JavaType jt = - spc.convert(generator.getClassLoader(), generator.ensureGetPackageName(), generator.getSwagger(), sp); - - StringProperty spNew = (StringProperty) ModelConverters.getInstance().readAsProperty(jt); - Assert.assertEquals(enums, spNew.getEnum()); - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestSwaggerDefinition.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestSwaggerDefinition.java deleted file mode 100644 index a283f092aca..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestSwaggerDefinition.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import java.util.Arrays; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.swagger.generator.core.unittest.SwaggerGeneratorForTest; -import io.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorContext; -import io.swagger.annotations.Contact; -import io.swagger.annotations.Extension; -import io.swagger.annotations.ExtensionProperty; -import io.swagger.annotations.ExternalDocs; -import io.swagger.annotations.Info; -import io.swagger.annotations.License; -import io.swagger.annotations.SwaggerDefinition; -import io.swagger.annotations.Tag; -import io.swagger.models.Swagger; - -public class TestSwaggerDefinition { - SwaggerGeneratorContext context = new PojoSwaggerGeneratorContext(); - - @SwaggerDefinition( - basePath = "base", - host = "host", - consumes = {"json", "xml"}, - produces = {"abc", "123"}, - tags = {@Tag( - name = "tagA", - description = "desc of tagA", - externalDocs = @ExternalDocs(value = "tagA ext docs", url = "url of tagA ext docs"), - extensions = {@Extension( - name = "x-tagA", - properties = {@ExtensionProperty(name = "x-tagAExt", value = "value of tagAExt")})})}, - info = @Info( - title = "title of SwaggerAnnotation", - version = "0.1", - termsOfService = "termsOfService", - description = "description of info for SwaggerAnnotation", - contact = @Contact(name = "contact", email = "contact@email.com", url = "http://contact"), - license = @License(name = "license ", url = "http://license"), - extensions = {@Extension( - name = "x-info", - properties = {@ExtensionProperty(name = "x-infoExt", value = "value of infoExt")})}), - externalDocs = @ExternalDocs( - value = "SwaggerAnnotation ext docs", - url = "url of SwaggerAnnotation ext docs")) - interface SwaggerAnnotation { - } - - interface SwaggerNoAnnotation { - } - - @SuppressWarnings("unchecked") - @Test - public void testSwaggerDefinition() { - SwaggerGenerator swaggerGenerator = - new SwaggerGeneratorForTest(context, SwaggerAnnotation.class); - swaggerGenerator.generate(); - - Swagger swagger = swaggerGenerator.getSwagger(); - - Assert.assertEquals(SwaggerAnnotation.class.getName(), - swagger.getInfo().getVendorExtensions().get(SwaggerConst.EXT_JAVA_INTF)); - Assert.assertEquals("2.0", swagger.getSwagger()); - Assert.assertEquals("/base", swagger.getBasePath()); - Assert.assertEquals("host", swagger.getHost()); - Assert.assertEquals(Arrays.asList("json", "xml"), swagger.getConsumes()); - Assert.assertEquals(Arrays.asList("abc", "123"), swagger.getProduces()); - - Assert.assertEquals(1, swagger.getTags().size()); - io.swagger.models.Tag tagA = swagger.getTags().get(0); - Assert.assertEquals("tagA", tagA.getName()); - Assert.assertEquals("desc of tagA", tagA.getDescription()); - Assert.assertEquals("tagA ext docs", tagA.getExternalDocs().getDescription()); - Assert.assertEquals("url of tagA ext docs", tagA.getExternalDocs().getUrl()); - Assert.assertEquals(1, tagA.getVendorExtensions().size()); - - Map tagValue = (Map) tagA.getVendorExtensions().get("x-tagA"); - Assert.assertEquals("value of tagAExt", tagValue.get("x-tagAExt")); - - io.swagger.models.Info info = swagger.getInfo(); - Assert.assertEquals("title of SwaggerAnnotation", info.getTitle()); - Assert.assertEquals("0.1", info.getVersion()); - Assert.assertEquals("termsOfService", info.getTermsOfService()); - Assert.assertEquals("description of info for SwaggerAnnotation", info.getDescription()); - - Assert.assertEquals("contact", info.getContact().getName()); - Assert.assertEquals("contact@email.com", info.getContact().getEmail()); - Assert.assertEquals("http://contact", info.getContact().getUrl()); - - Assert.assertEquals("license ", info.getLicense().getName()); - Assert.assertEquals("http://license", info.getLicense().getUrl()); - - Assert.assertEquals(2, info.getVendorExtensions().size()); - - Map infoValue = (Map) info.getVendorExtensions().get("x-info"); - Assert.assertEquals("value of infoExt", infoValue.get("x-infoExt")); - - Assert.assertEquals("SwaggerAnnotation ext docs", swagger.getExternalDocs().getDescription()); - Assert.assertEquals("url of SwaggerAnnotation ext docs", swagger.getExternalDocs().getUrl()); - } - - @Test - public void testFillDefault() { - SwaggerGenerator swaggerGenerator = - new SwaggerGeneratorForTest(context, SwaggerNoAnnotation.class); - swaggerGenerator.generate(); - swaggerGenerator.correctSwagger(); - - Swagger swagger = swaggerGenerator.getSwagger(); - - Assert.assertEquals("2.0", swagger.getSwagger()); - Assert.assertEquals("/SwaggerNoAnnotation", swagger.getBasePath()); - Assert.assertEquals("swagger definition for " + SwaggerNoAnnotation.class.getName(), - swagger.getInfo().getTitle()); - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestSwaggerUtils.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestSwaggerUtils.java deleted file mode 100644 index 1015ab46ad6..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/TestSwaggerUtils.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Date; - -import org.junit.Assert; -import org.junit.Test; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.common.javassist.JavassistUtils; -import io.servicecomb.foundation.common.utils.ReflectUtils; -import io.servicecomb.swagger.converter.ConverterMgr; -import io.servicecomb.swagger.generator.core.schema.Color; -import io.servicecomb.swagger.generator.core.schema.InvalidResponseHeader; -import io.servicecomb.swagger.generator.core.schema.RepeatOperation; -import io.servicecomb.swagger.generator.core.schema.Schema; -import io.servicecomb.swagger.generator.core.schema.User; -import io.servicecomb.swagger.generator.core.unittest.SwaggerGeneratorForTest; -import io.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.servicecomb.swagger.generator.core.utils.ParamUtils; -import io.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorContext; -import io.swagger.models.Model; -import io.swagger.models.RefModel; -import io.swagger.models.Swagger; - -public class TestSwaggerUtils { - SwaggerGeneratorContext context = new PojoSwaggerGeneratorContext(); - - @Test - public void testConverter() { - SwaggerGenerator generator = new SwaggerGeneratorForTest(context, null); - Swagger swagger = generator.getSwagger(); - ParamUtils.addDefinitions(swagger, User.class); - Model model = swagger.getDefinitions().get("User"); - model.getVendorExtensions().clear(); - - JavaType javaType = ConverterMgr.findJavaType(generator, model); - checkJavaType(swagger, javaType); - - RefModel refModel = new RefModel(); - refModel.setReference("User"); - javaType = ConverterMgr.findJavaType(generator, refModel); - checkJavaType(swagger, javaType); - } - - protected void checkJavaType(Swagger swagger, JavaType javaType) { - Class cls = javaType.getRawClass(); - Field[] fields = cls.getFields(); - Assert.assertEquals("gen.cse.ms.ut.User", cls.getName()); - Assert.assertEquals("name", fields[0].getName()); - Assert.assertEquals(String.class, fields[0].getType()); - Assert.assertEquals("age", fields[1].getName()); - Assert.assertEquals(Integer.class, fields[1].getType()); - } - - private SwaggerGenerator testSchemaMethod(String resultName, String... methodNames) { - return UnitTestSwaggerUtils.testSwagger("schemas/" + resultName + ".yaml", - context, - Schema.class, - methodNames); - } - - @Test - public void testBoolean() { - testSchemaMethod("boolean", "testboolean"); - testSchemaMethod("booleanObject", "testBoolean"); - } - - @Test - public void testByte() { - testSchemaMethod("byte", "testbyte"); - testSchemaMethod("byteObject", "testByte"); - } - - @Test - public void testShort() { - testSchemaMethod("short", "testshort"); - testSchemaMethod("shortObject", "testShort"); - } - - @Test - public void testInt() { - testSchemaMethod("int", "testint"); - testSchemaMethod("intObject", "testInteger"); - } - - @Test - public void testLong() { - testSchemaMethod("long", "testlong"); - testSchemaMethod("longObject", "testLong"); - } - - @Test - public void testFloat() { - testSchemaMethod("float", "testfloat"); - testSchemaMethod("floatObject", "testFloat"); - } - - @Test - public void testDouble() { - testSchemaMethod("double", "testdouble"); - testSchemaMethod("doubleObject", "testDouble"); - } - - @Test - public void testEnum() { - SwaggerGenerator generator = testSchemaMethod("enum", "testEnum"); - JavassistUtils.detach("gen.cse.ms.ut.SchemaIntf"); - Class intf = ClassUtils.getOrCreateInterface(generator); - - Method method = ReflectUtils.findMethod(intf, "testEnum"); - Class bodyCls = method.getParameterTypes()[0]; - Field[] fields = bodyCls.getFields(); - Assert.assertEquals(Color.class, fields[0].getType()); - Assert.assertEquals(fields[0].getType(), fields[1].getType()); - } - - @Test - public void testChar() { - testSchemaMethod("char", "testchar"); - testSchemaMethod("charObject", "testChar"); - } - - @Test - public void testBytes() { - testSchemaMethod("bytes", "testbytes"); - testSchemaMethod("bytesObject", "testBytes"); - } - - @Test - public void testString() { - testSchemaMethod("string", "testString"); - } - - @Test - public void testObject() { - testSchemaMethod("object", "testObject"); - } - - @Test - public void testArray() { - testSchemaMethod("array", "testArray"); - } - - @Test - public void testSet() { - testSchemaMethod("set", "testSet"); - } - - @Test - public void testList() { - testSchemaMethod("list", "testList"); - } - - @Test - public void testMap() { - testSchemaMethod("map", "testMap"); - } - - @Test - public void testMapList() { - testSchemaMethod("mapList", "testMapList"); - } - - @Test - public void testAllType() { - testSchemaMethod("allType", "testAllType"); - } - - @Test - public void testMultiParam() { - testSchemaMethod("multiParam", "testMultiParam"); - } - - @Test - public void testAllMethod() { - testSchemaMethod("allMethod"); - } - - @Test - public void testResponseHeader() { - testSchemaMethod("responseHeader", "testResponseHeader"); - } - - @Test - public void testApiResponse() { - testSchemaMethod("apiResponse", "testApiResponse"); - } - - @Test - public void testApiOperation() { - testSchemaMethod("apiOperation", "testApiOperation"); - } - - @Test - public void testDate() { - SwaggerGenerator generator = testSchemaMethod("date", "testDate"); - JavassistUtils.detach("gen.cse.ms.ut.SchemaIntf"); - Class intf = ClassUtils.getOrCreateInterface(generator); - - Method method = ReflectUtils.findMethod(intf, "testDate"); - Assert.assertEquals(Date.class, method.getReturnType()); - } - - @Test - public void testRepeatOperation() { - UnitTestSwaggerUtils.testException( - "OperationId must be unique. io.servicecomb.swagger.generator.core.schema.RepeatOperation:add", - context, - RepeatOperation.class); - } - - @Test - public void testInvalidResponseHeader() { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.core.schema.InvalidResponseHeader:test", - "invalid responseHeader, ResponseHeaderConfig [name=h, ResponseConfigBase [description=, responseReference=null, responseClass=class java.lang.Void, responseContainer=]]", - context, - InvalidResponseHeader.class, - "test"); - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/AllType.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/AllType.java deleted file mode 100644 index 0a845bccb80..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/AllType.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.schema; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class AllType { - public boolean bValue; - - public byte byteValue; - - public Byte byteObjectValue; - - public short sValue; - - public Short sObjectValue; - - public int iValue; - - public Integer iObjectValue; - - public long lValue; - - public Long lObjectValue; - - public float fValue; - - public Float fObjectValue; - - public double dValue; - - public Double dObjectValue; - - public Color enumValue; - - public char cValue; - - public Character cObjectValue; - - public byte[] bytes; - - public String strValue; - - public Set set; - - public List list; - - public Map map; -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/ArrayType.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/ArrayType.java deleted file mode 100644 index 34d36f307ba..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/ArrayType.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.schema; - -public class ArrayType { - public byte[] testBytes(byte[] value) { - return null; - } - - public User[] testUsers(User[] value) { - return null; - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/Color.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/Color.java deleted file mode 100644 index be3b5028513..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/Color.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.schema; - -public enum Color { - WHITE, - BLACK -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/InvalidResponseHeader.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/InvalidResponseHeader.java deleted file mode 100644 index 885689e915f..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/InvalidResponseHeader.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.schema; - -import io.swagger.annotations.ResponseHeader; - -public class InvalidResponseHeader { - @ResponseHeader(name = "h") - public void test() { - - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/InvalidType.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/InvalidType.java deleted file mode 100644 index 953355fa843..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/InvalidType.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.schema; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class InvalidType { - public static interface InvalidIntf { - - } - - public static abstract class InvalidClass { - - } - - public static class InvalidFieldClass { - public Object obj; - } - - public InvalidIntf testIntf(InvalidIntf input) { - return null; - } - - public InvalidClass testAbstractClass(InvalidClass input) { - return null; - } - - public Object testObject() { - return null; - } - - @SuppressWarnings("rawtypes") - public List testNotClearList() { - return null; - } - - @SuppressWarnings("rawtypes") - public Set testNotClearSet() { - return null; - } - - public Map testNotClearMap() { - return null; - } - - public InvalidFieldClass testInvalidFieldClass() { - return null; - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/RepeatOperation.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/RepeatOperation.java deleted file mode 100644 index 8378b9db911..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/RepeatOperation.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.core.schema; - -public class RepeatOperation { - public int add(int x, int y) { - return x + y; - } - - public int add(int x, int y, int z) { - return x + y + z; - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/Schema.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/Schema.java deleted file mode 100644 index 266a431dc0f..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/Schema.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.schema; - -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ResponseHeader; - -public class Schema { - @ApiOperation(value = "", hidden = true) - public void hidden() { - - } - - @ApiResponse(responseHeaders = {@ResponseHeader(name = "h", response = String.class)}, code = 200, message = "") - public void testApiResponse() { - - } - - @ApiOperation(responseHeaders = {@ResponseHeader(name = "h", response = String.class)}, value = "") - public void testApiOperation() { - - } - - @ResponseHeader(name = "h", response = String.class) - public void testResponseHeader() { - - } - - public void testboolean(boolean value) { - - } - - public void testBoolean(Boolean value) { - - } - - public void testbyte(byte value) { - - } - - public void testByte(Byte value) { - - } - - public void testshort(short value) { - - } - - public void testShort(Short value) { - - } - - public void testint(int value) { - - } - - public void testInteger(Integer value) { - - } - - public void testlong(long value) { - - } - - public void testLong(Long value) { - - } - - public void testfloat(float value) { - - } - - public void testFloat(Float value) { - - } - - public void testdouble(double value) { - - } - - public void testDouble(Double value) { - - } - - public void testEnum(Color color, Color color1) { - - } - - public void testchar(char value) { - - } - - public void testChar(Character value) { - - } - - public void testbytes(byte[] value) { - - } - - public void testBytes(Byte[] value) { - - } - - public void testString(String value) { - - } - - public void testObject(User user) { - - } - - public void testArray(String[] value) { - - } - - public void testSet(Set value) { - - } - - public void testList(List value) { - - } - - public void testMap(Map value) { - - } - - public Date testDate() { - return null; - } - - public void testMapList(Map> value) { - - } - - public void testAllType(AllType obj) { - } - - public List testMultiParam(AllType obj, - boolean bValue, - byte byteValue, - short sValue, - int iValue, - long lValue, - float fValue, - double dValue, - Color enumValue, - char cValue, - byte[] bytes, - String strValue, - String[] strArray, - Set set, - List list, - Map map) { - return Collections.emptyList(); - } -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/User.java b/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/User.java deleted file mode 100644 index 3807223015c..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/java/io/servicecomb/swagger/generator/core/schema/User.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.core.schema; - -public class User { - public String name; - - public int age; -} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/TestSwaggerUtils.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/TestSwaggerUtils.java new file mode 100644 index 00000000000..8cf0fa503f5 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/TestSwaggerUtils.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger; + +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import io.swagger.v3.core.util.Yaml; +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.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; + +public class TestSwaggerUtils { + + @Test + public void swaggerToStringNormal() { + OpenAPI swagger = new OpenAPI(); + String content = SwaggerUtils.swaggerToString(swagger); + + OpenAPI newSwagger = SwaggerUtils.parseSwagger(content); + Assertions.assertEquals(swagger, newSwagger); + } + + + @Test + public void parseSwaggerUrlNormal() throws IOException { + String content = "openapi: 3.0.1"; + URL url = Mockito.mock(URL.class); + try (MockedStatic ioUtilsMockedStatic = Mockito.mockStatic(IOUtils.class)) { + ioUtilsMockedStatic.when(() -> IOUtils.toString(url, StandardCharsets.UTF_8)).thenReturn(content); + OpenAPI swagger = Yaml.mapper().readValue(content, OpenAPI.class); + OpenAPI result = SwaggerUtils.parseAndValidateSwagger(url); + Assertions.assertEquals(swagger, result); + Assertions.assertEquals("3.0.1", result.getOpenapi()); + } + } + + @Test + public void parseSwaggerUrlException() throws IOException { + URL url = Mockito.mock(URL.class); + try (MockedStatic ioUtilsMockedStatic = Mockito.mockStatic(IOUtils.class)) { + ioUtilsMockedStatic.when(() -> IOUtils.toString(url, StandardCharsets.UTF_8)) + .thenThrow(new RuntimeExceptionWithoutStackTrace("failed")); + ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, + () -> SwaggerUtils.parseAndValidateSwagger(url)); + Assertions.assertTrue(exception.getMessage().contains("Parse swagger from url failed, ")); + } + } + + @Test + public void parseSwaggerContentException() { + ServiceCombException exception = Assertions.assertThrows(ServiceCombException.class, + () -> SwaggerUtils.parseSwagger("")); + Assertions.assertEquals("Parse swagger from content failed, ", exception.getMessage()); + } + + @Test + public void correctResponsesOperationFixEmptyDescription() { + ApiResponse response = new ApiResponse(); + + Operation operation = new Operation(); + operation.setResponses(new ApiResponses()); + operation.getResponses().addApiResponse("200", response); + + SwaggerUtils.correctResponses(operation); + Assertions.assertEquals("response of 200", response.getDescription()); + } + + @Test + public void correctResponsesOperationNotChangeExistDescription() { + ApiResponse response = new ApiResponse(); + response.setDescription("description"); + + Operation operation = new Operation(); + operation.setResponses(new ApiResponses()); + operation.getResponses().addApiResponse("200", response); + + SwaggerUtils.correctResponses(operation); + Assertions.assertEquals("description", response.getDescription()); + } + + @Test + public void correctResponsesHavePaths() { + ApiResponse response = new ApiResponse(); + + Operation operation = new Operation(); + operation.setResponses(new ApiResponses()); + operation.getResponses().addApiResponse("200", response); + + PathItem path = new PathItem(); + path.get(operation); + + OpenAPI swagger = new OpenAPI(); + swagger.path("/base", path); + + SwaggerUtils.correctResponses(swagger); + + Assertions.assertEquals("response of 200", response.getDescription()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/introspector/JsonPropertyIntrospectorTest.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/introspector/JsonPropertyIntrospectorTest.java new file mode 100644 index 00000000000..6ab24facd76 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/introspector/JsonPropertyIntrospectorTest.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.swagger.extend.introspector; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class JsonPropertyIntrospectorTest { + + @Test + public void findEnumValue() { + JsonPropertyIntrospector introspector = new JsonPropertyIntrospector(); + + Assertions.assertEquals("AB", introspector.findEnumValue(TestEnum.AB)); + Assertions.assertEquals("C-D", introspector.findEnumValue(TestEnum.C_D)); + Assertions.assertEquals("E.F", introspector.findEnumValue(TestEnum.E_F)); + Assertions.assertEquals("HI", introspector.findEnumValue(TestEnum.HI)); + } + + public enum TestEnum { + AB, + @JsonProperty(value = "C-D") + C_D, + @JsonProperty(value = "E.F") + E_F, + @JsonProperty + HI + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/module/EnumModuleExtTest.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/module/EnumModuleExtTest.java new file mode 100644 index 00000000000..309f84f4b4a --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/module/EnumModuleExtTest.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.swagger.extend.module; + +import static org.hamcrest.Matchers.contains; + +import java.util.List; + +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.swagger.v3.core.util.Json; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class EnumModuleExtTest { + public enum TestEnum { + AB, + @JsonProperty(value = "C-D") + C_D, + @JsonProperty(value = "E.F") + E_F, + @JsonProperty + HI + } + + @Test + public void testEnumModule() throws JsonProcessingException { + ObjectMapper mapper = Json.mapper(); + String serializeValue = mapper.writeValueAsString(TestEnum.AB); + Assertions.assertEquals("\"AB\"", serializeValue); + serializeValue = mapper.writeValueAsString(TestEnum.C_D); + Assertions.assertEquals("\"C-D\"", serializeValue); + serializeValue = mapper.writeValueAsString(TestEnum.E_F); + Assertions.assertEquals("\"E.F\"", serializeValue); + serializeValue = mapper.writeValueAsString(TestEnum.HI); + Assertions.assertEquals("\"\"", serializeValue); + } + + @Test + public void testEnumModuleModel() { + OpenAPI openAPI = new OpenAPI(); + Schema schema = SwaggerUtils.resolveTypeSchemas(openAPI, TestEnum.class); + Assertions.assertEquals(schema.getType(), "string"); + MatcherAssert.assertThat((List) schema.getEnum(), contains("AB", "C-D", "E.F", "")); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/property/creator/TestPartPropertyCreator.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/property/creator/TestPartPropertyCreator.java new file mode 100644 index 00000000000..677e3772ebb --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/property/creator/TestPartPropertyCreator.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.swagger.extend.property.creator; + +import jakarta.servlet.http.Part; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import io.swagger.v3.oas.models.media.FileSchema; + + +public class TestPartPropertyCreator { + PartPropertyCreator creator = new PartPropertyCreator(); + + @SuppressWarnings("unchecked") + @Test + public void classes() { + MatcherAssert.assertThat(creator.classes(), Matchers.arrayContaining(Part.class)); + } + + @Test + public void createProperty() { + MatcherAssert.assertThat(creator.createProperty(), Matchers.instanceOf(FileSchema.class)); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestApiOperation.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestApiOperation.java new file mode 100644 index 00000000000..73f63e71425 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestApiOperation.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.swagger.generator.core; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNull.nullValue; + +import java.util.Arrays; + +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.extensions.Extension; +import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; +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.models.OpenAPI; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.responses.ApiResponses; +import jakarta.ws.rs.core.MediaType; + +@SuppressWarnings({"rawtypes", "unused"}) +public class TestApiOperation { + static SwaggerOperations swaggerOperations = SwaggerOperations.generate(ApiOperationAnnotation.class); + + @AfterAll + public static void teardown() { + swaggerOperations = null; + } + + interface ApiOperationAnnotation { + @Operation( + summary = "summary", + description = "notes", + tags = {"tag1", "tag2"}, + method = "GET", + operationId = "test", + responses = @ApiResponse(responseCode = "202", + content = @Content(mediaType = MediaType.APPLICATION_JSON, + schema = @Schema(implementation = String.class)), + headers = @Header(name = "h1", schema = @Schema(implementation = Integer.class))), + extensions = {@Extension( + name = "x-tagA", + properties = {@ExtensionProperty(name = "x-tagAExt", value = "value of tagAExt")})}) + void testBase(); + + @Operation(summary = "aaa") + @ApiResponse(responseCode = "202", content = @Content(schema = @Schema(implementation = String.class))) + int testPrimitive(); + + @Operation(summary = "aaa", hidden = true) + int testHidden(); + } + + + @Test + public void testApiOperation() { + OpenAPI swagger = swaggerOperations.getSwagger(); + testBase(swagger.getPaths().get("/test")); + testPrimitive(swagger.getPaths().get("/testPrimitive")); + MatcherAssert.assertThat(swagger.getPaths().get("/testHidden"), is(nullValue())); + } + + private void testPrimitive(PathItem path) { + io.swagger.v3.oas.models.Operation operation = path.getPost(); + + Assertions.assertEquals(2, operation.getResponses().size()); + + io.swagger.v3.oas.models.media.Schema result200 = + operation.getResponses().get("200").getContent().get(MediaType.APPLICATION_JSON).getSchema(); + Assertions.assertEquals("integer", result200.getType()); + Assertions.assertEquals("int32", result200.getFormat()); + + io.swagger.v3.oas.models.media.Schema result202 = + operation.getResponses().get("202").getContent().get(MediaType.APPLICATION_JSON).getSchema(); + Assertions.assertEquals("string", result202.getType()); + } + + private void testBase(PathItem path) { + Assertions.assertEquals(1, path.readOperations().size()); + + io.swagger.v3.oas.models.Operation operation = path.getGet(); + + Assertions.assertEquals("summary", operation.getSummary()); + Assertions.assertEquals("notes", operation.getDescription()); + Assertions.assertEquals(Arrays.asList("tag1", "tag2"), operation.getTags()); + + ApiResponses responseMap = operation.getResponses(); + Assertions.assertEquals(2, responseMap.size()); + + io.swagger.v3.oas.models.responses.ApiResponse response = responseMap.get("202"); + Assertions.assertNotNull(response); + Assertions.assertEquals("string", response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getType()); + + Assertions.assertEquals(1, response.getHeaders().size()); + Assertions.assertEquals("integer", response.getHeaders().get("h1").getSchema().getType()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestApiResponse.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestApiResponse.java new file mode 100644 index 00000000000..e39b3bbf858 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestApiResponse.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.swagger.generator.core; + +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; +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.junit.jupiter.api.TestInstance; + +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.core.MediaType; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class TestApiResponse { + + SwaggerOperations swaggerOperations; + + @BeforeAll + public void setUp() { + swaggerOperations = SwaggerOperations.generate(ApiResponseAnnotation.class); + } + + @AfterAll + public void tearDown() { + swaggerOperations = null; + } + + interface ApiResponseAnnotation { + @ApiResponse( + headers = {@Header(name = "k1", schema = @Schema(implementation = Integer.class)), + @Header(name = "k2", schema = @Schema(implementation = String.class))}, + responseCode = "200", + description = "") + void testApiResponseHeader(); + + @ApiResponse(responseCode = "200", + headers = {@Header(name = "k1", schema = @Schema(implementation = Integer.class))}) + void testResponseHeader(); + + @ApiResponse( + content = @Content(schema = @Schema(implementation = Integer.class)), + responseCode = "200", + description = "msg") + void testSingle(); + + @ApiResponses(value = { + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Integer.class)), description = "msg1"), + @ApiResponse(responseCode = "301", content = @Content(schema = @Schema(implementation = String.class)), description = "msg2")}) + void testMulti(); + } + + @Test + public void checkResponseHeader() { + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testResponseHeader"); + Assertions.assertEquals("/testResponseHeader", swaggerOperation.getPath()); + + io.swagger.v3.oas.models.responses.ApiResponse response = swaggerOperation.getOperation().getResponses().get("200"); + io.swagger.v3.oas.models.headers.Header property = response.getHeaders().get("k1"); + Assertions.assertEquals("integer", property.getSchema().getType()); + Assertions.assertEquals("int32", property.getSchema().getFormat()); + } + + @Test + public void checkResponseDesc() { + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testMulti"); + Assertions.assertEquals("/testMulti", swaggerOperation.getPath()); + + io.swagger.v3.oas.models.responses.ApiResponse response1 = swaggerOperation.getOperation().getResponses() + .get("200"); + io.swagger.v3.oas.models.responses.ApiResponse response2 = swaggerOperation.getOperation().getResponses() + .get("301"); + Assertions.assertEquals("msg1", response1.getDescription()); + Assertions.assertEquals("msg2", response2.getDescription()); + } + + @Test + public void checkApiResponseHeader() { + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testApiResponseHeader"); + Assertions.assertEquals("/testApiResponseHeader", swaggerOperation.getPath()); + + io.swagger.v3.oas.models.responses.ApiResponse response = swaggerOperation.getOperation().getResponses().get("200"); + io.swagger.v3.oas.models.headers.Header property = response.getHeaders().get("k1"); + Assertions.assertEquals("integer", property.getSchema().getType()); + Assertions.assertEquals("int32", property.getSchema().getFormat()); + + property = response.getHeaders().get("k2"); + Assertions.assertEquals("string", property.getSchema().getType()); + Assertions.assertEquals(null, property.getSchema().getFormat()); + } + + @Test + public void checkSingle() { + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testSingle"); + Assertions.assertEquals("/testSingle", swaggerOperation.getPath()); + + io.swagger.v3.oas.models.responses.ApiResponse response = swaggerOperation.getOperation().getResponses().get("200"); + Assertions.assertEquals("integer", response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getType()); + Assertions.assertEquals("int32", response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getFormat()); + } + + @Test + public void checkMulti() { + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testMulti"); + Assertions.assertEquals("/testMulti", swaggerOperation.getPath()); + + io.swagger.v3.oas.models.responses.ApiResponse response = swaggerOperation.getOperation().getResponses().get("200"); + Assertions.assertEquals("integer", response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getType()); + Assertions.assertEquals("int32", response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getFormat()); + + response = swaggerOperation.getOperation().getResponses().get("301"); + Assertions.assertEquals("string", response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getType()); + Assertions.assertEquals(null, response.getContent().get(MediaType.APPLICATION_JSON).getSchema().getFormat()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestArrayType.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestArrayType.java new file mode 100644 index 00000000000..931fe3da9e0 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestArrayType.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.swagger.generator.core; + +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; +import org.apache.servicecomb.swagger.generator.core.schema.ArrayType; +import org.junit.jupiter.api.Assertions; +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.ByteArraySchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.RequestBody; +import jakarta.ws.rs.core.MediaType; + +public class TestArrayType { + @Test + public void test() { + SwaggerOperations swaggerOperations = SwaggerOperations.generate(ArrayType.class); + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testBytes"); + RequestBody bodyParameter = swaggerOperation.getOperation().getRequestBody(); + Schema model = bodyParameter.getContent().get(MediaType.APPLICATION_JSON).getSchema(); + + Assertions.assertEquals(Components.COMPONENTS_SCHEMAS_REF + "testBytesBody", model.get$ref()); + OpenAPI openAPI = swaggerOperation.getSwagger(); + Schema schema = openAPI.getComponents().getSchemas().get("testBytesBody"); + Assertions.assertEquals(1, schema.getProperties().size()); + + ByteArraySchema arrayProperty = (ByteArraySchema) schema.getProperties().get("value"); + Assertions.assertEquals("string", arrayProperty.getType()); + Assertions.assertEquals("byte", arrayProperty.getFormat()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestClassUtils.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestClassUtils.java new file mode 100644 index 00000000000..646ab449b0e --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestClassUtils.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.models.parameters.RequestBody; +import jakarta.ws.rs.Path; + +@OpenAPIDefinition +public class TestClassUtils { + @Test + public void testHasAnnotation() { + Assertions.assertTrue(SwaggerUtils.hasAnnotation(TestClassUtils.class, OpenAPIDefinition.class)); + Assertions.assertTrue(SwaggerUtils.hasAnnotation(TestClassUtils.class, Test.class)); + + Assertions.assertFalse(SwaggerUtils.hasAnnotation(TestClassUtils.class, Path.class)); + } + + @Test + public void isRawJsonType() { + RequestBody param = new RequestBody(); + + Assertions.assertFalse(SwaggerUtils.isRawJsonType(param)); + + param.addExtension(SwaggerConst.EXT_RAW_JSON_TYPE, Boolean.FALSE); + Assertions.assertFalse(SwaggerUtils.isRawJsonType(param)); + + param.addExtension(SwaggerConst.EXT_RAW_JSON_TYPE, Boolean.TRUE); + Assertions.assertTrue(SwaggerUtils.isRawJsonType(param)); + } + + @Test + public void getClassName_noName() { + Assertions.assertNull(SwaggerUtils.getClassName(null)); + + Map vendorExtensions = new HashMap<>(); + Assertions.assertNull(SwaggerUtils.getClassName(vendorExtensions)); + } + + @Test + public void getClassName_normal() { + Map vendorExtensions = new HashMap<>(); + vendorExtensions.put(SwaggerConst.EXT_JAVA_CLASS, String.class.getName()); + + Assertions.assertSame(String.class.getName(), SwaggerUtils.getClassName(vendorExtensions)); + } + + @Test + public void getInterfaceName_noName() { + Map vendorExtensions = new HashMap<>(); + + Assertions.assertNull(SwaggerUtils.getInterfaceName(vendorExtensions)); + } + + @Test + public void getInterfaceName_normal() { + Map vendorExtensions = new HashMap<>(); + vendorExtensions.put(SwaggerConst.EXT_JAVA_INTF, String.class.getName()); + + Assertions.assertSame(String.class.getName(), SwaggerUtils.getInterfaceName(vendorExtensions)); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestOperationGenerator.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestOperationGenerator.java new file mode 100644 index 00000000000..b46b9bb8327 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestOperationGenerator.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.swagger.generator.core; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; + +import java.util.List; + +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; +import org.hamcrest.MatcherAssert; +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.junit.jupiter.api.TestInstance; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.extensions.Extension; +import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; +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.tags.Tag; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class TestOperationGenerator { + SwaggerOperations swaggerOperations; + + @BeforeAll + public void setUp() { + swaggerOperations = SwaggerOperations.generate(TestClass.class); + } + + @AfterAll + public void tearDown() { + swaggerOperations = null; + } + + @OpenAPIDefinition(tags = {@Tag(name = "default0"), @Tag(name = "default1")}) + private static class TestClass { + @ApiResponse(responseCode = "200", description = "200 is ok............", + content = @Content(mediaType = "application/json", schema = @Schema(name = "String")), + headers = @Header(name = "x-user-domain", schema = @Schema(implementation = String.class))) + @Operation(summary = "value1", tags = {"tag1", "tag2"}, + responses = { + @ApiResponse(responseCode = "200", headers = + {@Header(name = "x-user-name", schema = @Schema(implementation = String.class)), + @Header(name = "x-user-id", schema = @Schema(implementation = String.class))})}, + extensions = { + @Extension(name = "x-class-name", properties = @ExtensionProperty(value = "value", name = "key"))}) + public void responseThenApiOperation() { + } + + @Operation(summary = "value1", tags = {"tag1", "tag2"}, + responses = {@ApiResponse(responseCode = "200", headers = { + @Header(name = "x-user-name", schema = @Schema(implementation = String.class)), + @Header(name = "x-user-id", schema = @Schema(implementation = String.class))})}, + extensions = { + @Extension(name = "x-class-name", properties = { + @ExtensionProperty(value = "value", name = "key")})}) + @ApiResponse(responseCode = "200", description = "200 is ok............", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = String.class)), + headers = @Header(name = "x-user-domain", schema = @Schema(implementation = String.class))) + public void apiOperationThenResponse() { + } + + @Operation(summary = "value2") + public void apiOperationNoTag() { + } + + public void noApiOperation() { + } + } + + @Test + public void apiOperationNoTag() { + SwaggerOperation operation = swaggerOperations.findOperation("apiOperationNoTag"); + List tags = operation.getOperation().getTags(); + MatcherAssert.assertThat(tags, equalTo(null)); + Assertions.assertEquals("value2", operation.getOperation().getSummary()); + } + + @Test + public void noApiOperation() { + SwaggerOperation operation = swaggerOperations.findOperation("noApiOperation"); + List tags = operation.getOperation().getTags(); + MatcherAssert.assertThat(tags, equalTo(null)); + Assertions.assertNull(operation.getOperation().getSummary()); + } + + @Test + public void responseThenApiOperation() { + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("responseThenApiOperation"); + List tags = swaggerOperation.getOperation().getTags(); + MatcherAssert.assertThat(tags, contains("tag1", "tag2")); + + io.swagger.v3.oas.models.responses.ApiResponse response = swaggerOperation.getOperation().getResponses().get("200"); + Assertions.assertEquals("200 is ok............", response.getDescription()); + Assertions.assertNotNull(response.getHeaders().get("x-user-domain")); + Assertions.assertNotNull(response.getHeaders().get("x-user-name")); + Assertions.assertNotNull(swaggerOperation.getOperation().getExtensions().get("x-class-name")); + Assertions.assertNull(swaggerOperation.getOperation().getExtensions().get("x-not-exists")); + } + + @Test + public void apiOperationThenResponse() { + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("apiOperationThenResponse"); + List tags = swaggerOperation.getOperation().getTags(); + MatcherAssert.assertThat(tags, contains("tag1", "tag2")); + + io.swagger.v3.oas.models.responses.ApiResponse response = swaggerOperation.getOperation().getResponses().get("200"); + Assertions.assertEquals("200 is ok............", response.getDescription()); + Assertions.assertNotNull(response.getHeaders().get("x-user-domain")); + Assertions.assertNotNull(response.getHeaders().get("x-user-name")); + Assertions.assertNotNull(swaggerOperation.getOperation().getExtensions().get("x-class-name")); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestPojo.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestPojo.java new file mode 100644 index 00000000000..d733d025080 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestPojo.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.swagger.generator.core; + +import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; +import org.junit.jupiter.api.Test; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; + +public class TestPojo { + @SuppressWarnings("unused") + public static class ParameterAnnotation { + @Operation(summary = "differentName", operationId = "differentName") + public int operationIdAndParameter(@Parameter(name = "x") int a, @Parameter(name = "y") int b) { + return a * 2 + b; + } + } + + @Test + public void testResponseEntity() { + UnitTestSwaggerUtils.testSwagger("schemas/ParameterAnnotation.yaml", ParameterAnnotation.class); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestPojoExample.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestPojoExample.java new file mode 100644 index 00000000000..e9ad9c7ea20 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestPojoExample.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.swagger.generator.core; + +import org.apache.servicecomb.swagger.generator.core.pojo.PojoExample1; +import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; +import org.junit.jupiter.api.Test; + +public class TestPojoExample { + @Test + public void testPojoExample1() { + UnitTestSwaggerUtils.testSwagger("schemas/pojoExample1.yaml", PojoExample1.class); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerDefinition.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerDefinition.java new file mode 100644 index 00000000000..c7b2166889f --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerDefinition.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core; + +import java.util.Map; + +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.extensions.Extension; +import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; +import io.swagger.v3.oas.annotations.info.Contact; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.info.License; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.models.OpenAPI; + +@SuppressWarnings("unchecked") +public class TestSwaggerDefinition { + @OpenAPIDefinition( + servers = @Server(url = "host/base"), + tags = {@Tag( + name = "tagA", + description = "desc of tagA", + externalDocs = @ExternalDocumentation(description = "tagA ext docs", url = "url of tagA ext docs"), + extensions = {@Extension( + name = "x-tagA", + properties = {@ExtensionProperty(name = "x-tagAExt", value = "value of tagAExt")})})}, + info = @Info( + title = "title of SwaggerAnnotation", + version = "0.1", + termsOfService = "termsOfService", + description = "description of info for SwaggerAnnotation", + contact = @Contact(name = "contact", email = "contact@email.com", url = "http://contact"), + license = @License(name = "license ", url = "http://license"), + extensions = {@Extension( + name = "x-info", + properties = {@ExtensionProperty(name = "x-infoExt", value = "value of infoExt")})}), + externalDocs = @ExternalDocumentation( + description = "SwaggerAnnotation ext docs", + url = "url of SwaggerAnnotation ext docs")) + interface SwaggerAnnotation { + } + + interface SwaggerNoAnnotation { + } + + @Test + public void testSwaggerDefinition() { + OpenAPI swagger = SwaggerGenerator.generate(SwaggerAnnotation.class); + + Assertions.assertEquals("value of infoExt", + ((Map) swagger.getInfo().getExtensions().get("x-info")).get("x-infoExt")); + Assertions.assertEquals("3.0.1", swagger.getOpenapi()); + Assertions.assertEquals("host/base", swagger.getServers().get(0).getUrl()); + + Assertions.assertEquals(1, swagger.getTags().size()); + io.swagger.v3.oas.models.tags.Tag tagA = swagger.getTags().get(0); + Assertions.assertEquals("tagA", tagA.getName()); + Assertions.assertEquals("desc of tagA", tagA.getDescription()); + Assertions.assertEquals("tagA ext docs", tagA.getExternalDocs().getDescription()); + Assertions.assertEquals("url of tagA ext docs", tagA.getExternalDocs().getUrl()); + Assertions.assertEquals(1, tagA.getExtensions().size()); + + Map tagValue = (Map) tagA.getExtensions().get("x-tagA"); + Assertions.assertEquals("value of tagAExt", tagValue.get("x-tagAExt")); + + io.swagger.v3.oas.models.info.Info info = swagger.getInfo(); + Assertions.assertEquals("title of SwaggerAnnotation", info.getTitle()); + Assertions.assertEquals("0.1", info.getVersion()); + Assertions.assertEquals("termsOfService", info.getTermsOfService()); + Assertions.assertEquals("description of info for SwaggerAnnotation", info.getDescription()); + + Assertions.assertEquals("contact", info.getContact().getName()); + Assertions.assertEquals("contact@email.com", info.getContact().getEmail()); + Assertions.assertEquals("http://contact", info.getContact().getUrl()); + + Assertions.assertEquals("license ", info.getLicense().getName()); + Assertions.assertEquals("http://license", info.getLicense().getUrl()); + + Assertions.assertEquals("SwaggerAnnotation ext docs", swagger.getExternalDocs().getDescription()); + Assertions.assertEquals("url of SwaggerAnnotation ext docs", swagger.getExternalDocs().getUrl()); + } + + @Test + public void testFillDefault() { + OpenAPI swagger = SwaggerGenerator.generate(SwaggerNoAnnotation.class); + Assertions.assertEquals("3.0.1", swagger.getOpenapi()); + Assertions.assertEquals("/SwaggerNoAnnotation", swagger.getServers().get(0).getUrl()); + Assertions.assertEquals("swagger definition for " + SwaggerNoAnnotation.class.getName(), + swagger.getInfo().getTitle()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerGenerator.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerGenerator.java new file mode 100644 index 00000000000..a05bbce200c --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerGenerator.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.swagger.generator.core; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.pojo.PojoSwaggerGenerator; +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 TestSwaggerGenerator { + Environment environment = Mockito.mock(Environment.class); + + @BeforeEach + public void setUp() { + LegacyPropertyFactory.setEnvironment(environment); + } + + @Test + public void testBasePathPlaceHolder() { + Mockito.when(environment.getProperty("var")).thenReturn("varValue"); + + PojoSwaggerGenerator swaggerGenerator = new PojoSwaggerGenerator(null); + swaggerGenerator.setBasePath("/a/${var}/b"); + + Assertions.assertEquals("/a/varValue/b", SwaggerUtils.getBasePath(swaggerGenerator.getOpenAPI())); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerUtils.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerUtils.java new file mode 100644 index 00000000000..7611a3f80a7 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerUtils.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core; + +import static junit.framework.TestCase.fail; +import static org.hamcrest.Matchers.containsString; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import org.apache.servicecomb.foundation.common.utils.ReflectUtils; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils; +import org.apache.servicecomb.swagger.generator.core.pojo.TestTypeClass; +import org.apache.servicecomb.swagger.generator.core.pojo.TestTypeRecord; +import org.apache.servicecomb.swagger.generator.core.pojo.TestType1; +import org.apache.servicecomb.swagger.generator.core.pojo.TestType2; +import org.apache.servicecomb.swagger.generator.core.schema.RepeatOperation; +import org.apache.servicecomb.swagger.generator.core.schema.Schema; +import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.IntegerSchema; +import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.parameters.RequestBody; + +@SuppressWarnings("rawtypes") +public class TestSwaggerUtils { + Logger LOGGER = Logger.getLogger(TestSwaggerUtils.class.getName()); + + @Test + public void testSchemaMethod() { + UnitTestSwaggerUtils.testSwagger("schemas/Schema.yaml", + Schema.class); + } + + @Test + public void testRepeatOperation() { + UnitTestSwaggerUtils.testException( + "OperationId must be unique. method=org.apache.servicecomb.swagger.generator.core.schema.RepeatOperation:add.", + RepeatOperation.class); + } + + @Test + public void noParameterName() { + Method method = ReflectUtils.findMethod(Schema.class, "testint"); + Parameter parameter = Mockito.spy(method.getParameters()[0]); + Mockito.when(parameter.isNamePresent()).thenReturn(false); + + IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, + () -> SwaggerGeneratorUtils.collectParameterName(parameter)); + String expectedMsg = + "parameter name is not present, method=org.apache.servicecomb.swagger.generator.core.schema.Schema:testint\n" + + "solution:\n" + + " change pom.xml, add compiler argument: -parameters, for example:\n" + + " \n" + + " org.apache.maven.plugins\n" + + " maven-compiler-plugin\n" + + " \n" + + " -parameters\n" + + " \n" + + " "; + Assertions.assertEquals(expectedMsg, exception.getMessage()); + } + + @Test + public void testGetRawJsonType() { + RequestBody param = Mockito.mock(RequestBody.class); + Map extensions = new HashMap<>(); + when(param.getExtensions()).thenReturn(extensions); + + extensions.put(SwaggerConst.EXT_RAW_JSON_TYPE, true); + Assertions.assertTrue(SwaggerUtils.isRawJsonType(param)); + + extensions.put(SwaggerConst.EXT_RAW_JSON_TYPE, "test"); + Assertions.assertFalse(SwaggerUtils.isRawJsonType(param)); + } + + private static class AllTypeTest1 { + TestType1 t1; + + List t2; + + Map t3; + + TestType1[] t4; + } + + private static class AllTypeTest2 { + TestType2 t1; + + List t2; + + Map t3; + + TestType2[] t4; + + } + + private static class AllTypeTest3{ + TestTypeRecord t5; + } + private static class AllTypeTest4{ + TestTypeClass t5; + } + + @Test + public void testAddDefinitions() { + testAllType(AllTypeTest1.class, AllTypeTest2.class); + } + + private void testAllType(Class clazz1, Class clazz2) { + Field[] fields1 = clazz1.getDeclaredFields(); + Field[] fields2 = clazz2.getDeclaredFields(); + for (Field value : fields1) { + for (Field field : fields2) { + if (value.isSynthetic() || field.isSynthetic()) { + continue; + } + try { + testExcep(value.getGenericType(), field.getGenericType()); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + LOGGER.warning(value.getGenericType() + " " + field.getGenericType() + " " + e.getMessage()); + MatcherAssert.assertThat(e.getMessage(), containsString("duplicate param model:")); + } + } + } + } + + @Test + public void testAddDefinitionsWithRecord() { + testAllType(AllTypeTest3.class, AllTypeTest4.class); + } + + private void testExcep(Type f1, Type f2) { + OpenAPI swagger = new OpenAPI(); + SwaggerUtils.resolveTypeSchemas(swagger, f1); + SwaggerUtils.resolveTypeSchemas(swagger, f2); + } + + @Test + public void test_resolve_type_schemas_correct() { + OpenAPI openAPI = new OpenAPI(); + + io.swagger.v3.oas.models.media.Schema schema = SwaggerUtils.resolveTypeSchemas(openAPI, String.class); + Assertions.assertTrue(schema instanceof StringSchema); + + openAPI = new OpenAPI(); + schema = SwaggerUtils.resolveTypeSchemas(openAPI, Integer.class); + Assertions.assertTrue(schema instanceof IntegerSchema); + + openAPI = new OpenAPI(); + schema = SwaggerUtils.resolveTypeSchemas(openAPI, TestType1.class); + schema = SwaggerUtils.getSchema(openAPI, schema); // resolve reference + // should be ObjectSchema but swagger is not. + //
 Assertions.assertTrue(schema instanceof ObjectSchema) 
+ Assertions.assertEquals("object", schema.getType()); + + openAPI = new OpenAPI(); + schema = SwaggerUtils.getSchema(openAPI, SwaggerUtils.resolveTypeSchemas(openAPI, TestTypeClass.class)); // resolve reference + Assertions.assertEquals("object", schema.getType()); + + openAPI = new OpenAPI(); + schema = SwaggerUtils.getSchema(openAPI, SwaggerUtils.resolveTypeSchemas(openAPI, TestTypeRecord.class)); // resolve reference + Assertions.assertEquals("object", schema.getType()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/model/TestSwaggerOperations.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/model/TestSwaggerOperations.java new file mode 100644 index 00000000000..ac05836b2a0 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/model/TestSwaggerOperations.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.swagger.generator.core.model; + +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.generator.core.pojo.PojoExample1; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.parameters.RequestBody; + +public class TestSwaggerOperations { + + @Test + public void emptyOperationId() { + OpenAPI swagger = SwaggerUtils.parseAndValidateSwagger(this.getClass().getResource("/schemas/boolean.yaml")); + swagger.getPaths().values().stream() + .findFirst().get() + .getPost().setOperationId(""); + + IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, + () -> new SwaggerOperations(swagger)); + Assertions.assertEquals("OperationId can not be empty, path=/testboolean, httpMethod=POST.", + exception.getMessage()); + } + + @Test + public void testPojoExample1() { + SwaggerOperations swaggerOperations = SwaggerOperations.generate(PojoExample1.class); + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testMultiParameter"); + Assertions.assertEquals(null, swaggerOperation.getOperation().getParameters()); + RequestBody requestBody = swaggerOperation.getOperation().getRequestBody(); + Assertions.assertEquals(2, + SwaggerUtils.getSchema(swaggerOperation.getSwagger(), + requestBody.getContent().get(SwaggerConst.DEFAULT_MEDIA_TYPE).getSchema()).getProperties().size()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/PojoExample1.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/PojoExample1.java new file mode 100644 index 00000000000..d93beb010ca --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/PojoExample1.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.swagger.generator.core.pojo; + +public class PojoExample1 { + public TestType1 testOneParameter(TestType1 testType1) { + return null; + } + + public TestType1 testMultiParameter(TestType1 testType1, String testString) { + return null; + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestType1.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestType1.java new file mode 100644 index 00000000000..16120da045f --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestType1.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(name = "XXX") +public class TestType1 { + public Integer val1; +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestType2.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestType2.java new file mode 100644 index 00000000000..e21e9c31eaa --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestType2.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(name = "XXX") +public class TestType2 { + public String val2; +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeClass.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeClass.java new file mode 100644 index 00000000000..b00a4433480 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeClass.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.swagger.generator.core.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(name = "YYY") +public class TestTypeClass { + @Schema(description = "language") + private TestTypeEnumLang testTypeEnumLang; + + public TestTypeEnumLang getTestTypeEnumLang() { + return testTypeEnumLang; + } + + public void setTestTypeEnumLang(TestTypeEnumLang testTypeEnumLang) { + this.testTypeEnumLang = testTypeEnumLang; + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeEnumLang.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeEnumLang.java new file mode 100644 index 00000000000..515f45c7bb1 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeEnumLang.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.swagger.generator.core.pojo; + +public enum TestTypeEnumLang { + JAVA, CHINESE, UNKNOWN; +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeRecord.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeRecord.java new file mode 100644 index 00000000000..c61cc0913eb --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeRecord.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(name = "YYY") +public record TestTypeRecord( + @Schema(description = "language") TestTypeEnumLang testTypeEnumLang) { +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OpenAPIDefinitionProcessorTest.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OpenAPIDefinitionProcessorTest.java new file mode 100644 index 00000000000..2d07ded7f7f --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OpenAPIDefinitionProcessorTest.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.swagger.generator.core.processor.annotation; + +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.info.Contact; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.info.License; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.models.OpenAPI; + +public class OpenAPIDefinitionProcessorTest { + @OpenAPIDefinition(tags = { + @Tag(name = "testTag", description = "desc", externalDocs = @ExternalDocumentation(description = "testValue", url = "testUrl")) + }, + servers = {@Server(url = "127.0.0.1")}, + info = @Info(title = "title", version = "version", description = "desc", contact = @Contact(name = "contactName"), + license = @License(name = "licenseName"))) + private static class SwaggerTestTarget { + } + + @Test + public void testProcess() { + OpenAPI swagger = SwaggerGenerator.generate(SwaggerTestTarget.class); + + Assertions.assertEquals(1, swagger.getTags().size()); + io.swagger.v3.oas.models.tags.Tag tag = swagger.getTags().get(0); + Assertions.assertEquals("testTag", tag.getName()); + Assertions.assertEquals("desc", tag.getDescription()); + Assertions.assertEquals("testValue", tag.getExternalDocs().getDescription()); + Assertions.assertEquals("testUrl", tag.getExternalDocs().getUrl()); + Assertions.assertEquals("127.0.0.1", swagger.getServers().get(0).getUrl()); + + io.swagger.v3.oas.models.info.Info info = swagger.getInfo(); + Assertions.assertEquals("title", info.getTitle()); + Assertions.assertEquals("version", info.getVersion()); + Assertions.assertEquals("desc", info.getDescription()); + Assertions.assertEquals("contactName", info.getContact().getName()); + Assertions.assertEquals("licenseName", info.getLicense().getName()); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OperationMethodAnnotationProcessorTest.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OperationMethodAnnotationProcessorTest.java new file mode 100644 index 00000000000..fdc3cea273e --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OperationMethodAnnotationProcessorTest.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core.processor.annotation; + +import static org.hamcrest.Matchers.containsInAnyOrder; + +import java.util.Map; + +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +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.junit.jupiter.api.TestInstance; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.models.Components; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.ws.rs.core.MediaType; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class OperationMethodAnnotationProcessorTest { + SwaggerOperations swaggerOperations; + + @BeforeAll + public void setUp() { + swaggerOperations = SwaggerOperations.generate(TestClass.class); + } + + @AfterAll + public void tearDown() { + swaggerOperations = null; + } + + private static class TestClass { + @Operation(summary = "value1", tags = {"tag1", "tag2"}) + public void function() { + } + + @Operation(summary = "value2") + public void functionWithNoTag() { + } + + // mediaType will be ignored + @Operation(summary = "testSingleMediaType", + responses = {@ApiResponse(responseCode = "200", content = @Content( + mediaType = MediaType.TEXT_PLAIN, schema = @Schema(implementation = String.class)))}) + public String testSingleMediaType(String input) { + return input; + } + + @Operation(summary = "testMultiMediaType", + responses = { + @ApiResponse(responseCode = "200", content = { + @Content(mediaType = MediaType.APPLICATION_JSON), + @Content(mediaType = MediaType.TEXT_PLAIN) + })}) + public String testMultiMediaType(String input) { + return input; + } + + @Operation(summary = "testBlankMediaType", + responses = {@ApiResponse(responseCode = "200", content = @Content(mediaType = ""))}, + requestBody = @RequestBody(content = @Content(mediaType = "", + schema = @Schema(implementation = String.class)))) + public String testBlankMediaType(String input) { + return input; + } + + @Operation(summary = "testBodyParam") + public String testBodyParam(@RequestBody(content = @Content( + schema = @Schema( + implementation = TestBodyBean.class))) TestBodyBean user) { + return user.toString(); + } + } + + + private static class TestBodyBean { + + @NotBlank + private String age; + + @NotNull + private String name; + + @NotEmpty + private String sexes; + + public String getAge() { + return age; + } + + public void setAge(String age) { + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSexes() { + return sexes; + } + + public void setSexes(String sexes) { + this.sexes = sexes; + } + } + + @Test + public void testConvertTags() { + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("function"); + MatcherAssert.assertThat(swaggerOperation.getOperation().getTags(), containsInAnyOrder("tag1", "tag2")); + } + + @Test + public void testConvertTagsOnMethodWithNoTag() { + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("functionWithNoTag"); + Assertions.assertNull(swaggerOperation.getOperation().getTags()); + } + + @Test + public void testMultiMediaType() { + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testMultiMediaType"); + MatcherAssert.assertThat(swaggerOperation.getOperation().getRequestBody().getContent().keySet(), + Matchers.contains(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, MediaType.TEXT_PLAIN)); + MatcherAssert.assertThat(swaggerOperation.getOperation().getResponses().get("200").getContent().keySet(), + Matchers.contains(MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN)); + } + + @Test + public void testSingleMediaType() { + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testSingleMediaType"); + MatcherAssert.assertThat(swaggerOperation.getOperation() + .getRequestBody().getContent().get(MediaType.APPLICATION_JSON).getSchema().get$ref(), + Matchers.equalTo(Components.COMPONENTS_SCHEMAS_REF + "testSingleMediaTypeBody")); + MatcherAssert.assertThat(swaggerOperation.getOperation() + .getResponses().get("200").getContent().get(MediaType.TEXT_PLAIN).getSchema().getType(), + Matchers.equalTo("string")); + } + + @Test + public void testBlankMediaType() { + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testBlankMediaType"); + MatcherAssert.assertThat(swaggerOperation.getOperation().getRequestBody().getContent().keySet(), + Matchers.contains(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, MediaType.TEXT_PLAIN)); + MatcherAssert.assertThat(swaggerOperation.getOperation().getResponses() + .get(SwaggerConst.SUCCESS_KEY).getContent().keySet(), + Matchers.contains(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, MediaType.TEXT_PLAIN)); + } + + @Test + public void testBodyParam() { + SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testBodyParam"); + io.swagger.v3.oas.models.media.Schema schema = swaggerOperation.getSwagger() + .getPaths().get("/testBodyParam").getPost().getRequestBody().getContent() + .get(MediaType.APPLICATION_JSON).getSchema(); + Assertions.assertEquals(Components.COMPONENTS_SCHEMAS_REF + "TestBodyBean", schema.get$ref()); + schema = swaggerOperation.getSwagger().getComponents().getSchemas().get("TestBodyBean"); + Map properties = schema.getProperties(); + Assertions.assertEquals(properties.get("age").getType(), "string"); + Assertions.assertEquals(properties.get("sexes").getType(), "string"); + Assertions.assertEquals(properties.get("name").getType(), "string"); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/AllType.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/AllType.java new file mode 100644 index 00000000000..6314ed40ff0 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/AllType.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.swagger.generator.core.schema; + +import java.util.List; + +import org.apache.servicecomb.foundation.test.scaffolding.model.User; + +public class AllType { +// public boolean bValue; +// +// public byte byteValue; +// +// public Byte byteObjectValue; +// +// public short sValue; +// +// public Short sObjectValue; +// +// public int iValue; +// +// public Integer iObjectValue; +// +// public long lValue; +// +// public Long lObjectValue; +// +// public float fValue; +// +// public Float fObjectValue; +// +// public double dValue; +// +// public Double dObjectValue; +// +// public Color enumValue; +// +// public char cValue; +// +// public Character cObjectValue; +// +// public byte[] bytes; +// +// public String strValue; +// +// public Set set; + + public List list; + +// public Map map; +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/ArrayType.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/ArrayType.java new file mode 100644 index 00000000000..ea327c9c24b --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/ArrayType.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.swagger.generator.core.schema; + +import org.apache.servicecomb.foundation.test.scaffolding.model.User; + +public class ArrayType { + public byte[] testBytes(byte[] value) { + return null; + } + + public User[] testUsers(User[] value) { + return null; + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/RepeatOperation.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/RepeatOperation.java new file mode 100644 index 00000000000..6132316ab31 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/RepeatOperation.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.swagger.generator.core.schema; + +public class RepeatOperation { + public int add(int x, int y) { + return x + y; + } + + public int add(int x, int y, int z) { + return x + y + z; + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/Schema.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/Schema.java new file mode 100644 index 00000000000..de65caa81e7 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/schema/Schema.java @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core.schema; + +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; +import org.apache.servicecomb.foundation.test.scaffolding.model.User; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import jakarta.servlet.http.HttpServletRequest; + +@SuppressWarnings("all") +public class Schema { + @Operation(method = "", hidden = true) + public void hidden() { + + } + + @ApiResponse(headers = {@Header(name = "h", + schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = String.class))}, responseCode = "200", description = "") + public void testApiResponse() { + + } + + @ApiResponse(headers = {@Header(name = "h", + schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = String.class))}, description = "") + public void testApiOperation() { + + } + + @ApiResponse(headers = {@Header(name = "h", + schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = String.class))}) + public void testResponseHeader() { + + } + + public void testboolean(boolean value) { + + } + + public void testBoolean(Boolean value) { + + } + + public void testbyte(byte value) { + + } + + public void testByte(Byte value) { + + } + + public void testshort(short value) { + + } + + public void testShort(Short value) { + + } + + public void testint(int value) { + + } + + public void testInteger(Integer value) { + + } + + public void testlong(long value) { + + } + + public void testLong(Long value) { + + } + + public void testfloat(float value) { + + } + + public void testFloat(Float value) { + + } + + public void testdouble(double value) { + + } + + public void testDouble(Double value) { + + } + + public void testOneEnum(Color color) { + + } + + public void testEnum(Color color, Color color1) { + + } + + public void testchar(char value) { + + } + + public void testChar(Character value) { + + } + + public void testbytes(byte[] value) { + + } + + public void testBytes(Byte[] value) { + + } + + public void testString(String value) { + + } + + public void testObject(User user) { + + } + + public void testArray(String[] value) { + + } + + public void testSet(Set value) { + + } + + public List> nestedListString(List> param) { + return param; + } + + public void testList(List value) { + + } + + public void testMap(Map value) { + + } + + public Date testDate() { + return null; + } + + public void testMapList(Map> value) { + + } + + public CompletableFuture testCompletableFuture() { + return null; + } + + public Optional testOptional() { + return Optional.empty(); + } + + public CompletableFuture> testCompletableFutureOptional() { + return null; + } + + public void testAllType(AllType obj) { + } + + public List testMultiParam(AllType obj, + boolean bValue, + byte byteValue, + short sValue, + int iValue, + long lValue, + float fValue, + double dValue, + Color enumValue, + char cValue, + byte[] bytes, + String strValue, + String[] strArray, + Set set, + List list, + Map map) { + return Collections.emptyList(); + } + + public void wrapToBodyWithDesc(@Parameter(name = "desc") int value) { + + } + + public void ignoreRequest(HttpServletRequest request, int value) { + + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/TestMethodUtils.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/TestMethodUtils.java new file mode 100644 index 00000000000..873b9bb8e5f --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/TestMethodUtils.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.swagger.generator.core.utils; + +import java.lang.reflect.Method; +import java.util.List; + +import org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel.AbstractBaseClass; +import org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel.BaseInterface; +import org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel.Hello2Endpoint; +import org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel.HelloEndpoint; +import org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel.ServiceInterface; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestMethodUtils { + @Test + public void testGetClassMethods() throws Exception { + List methods = MethodUtils.findSwaggerMethods(Hello2Endpoint.class); + Assertions.assertEquals(3, methods.size()); + Assertions.assertEquals(Hello2Endpoint.class, methods.get(0).getDeclaringClass()); + Assertions.assertEquals(Hello2Endpoint.class, methods.get(1).getDeclaringClass()); + Assertions.assertEquals(Hello2Endpoint.class, methods.get(2).getDeclaringClass()); + + methods = MethodUtils.findSwaggerMethods(HelloEndpoint.class); + Assertions.assertEquals(2, methods.size()); + Assertions.assertEquals(HelloEndpoint.class, methods.get(0).getDeclaringClass()); // get + Assertions.assertEquals(AbstractBaseClass.class, methods.get(1).getDeclaringClass()); // getBase + + methods = MethodUtils.findSwaggerMethods(ServiceInterface.class); + Assertions.assertEquals(3, methods.size()); + Assertions.assertEquals(BaseInterface.class, methods.get(0).getDeclaringClass()); // get + Assertions.assertEquals(BaseInterface.class, methods.get(1).getDeclaringClass()); // getArray + Assertions.assertEquals(ServiceInterface.class, methods.get(2).getDeclaringClass()); // getBase + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/AbstractBaseClass.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/AbstractBaseClass.java new file mode 100644 index 00000000000..f5d4f56a858 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/AbstractBaseClass.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.swagger.generator.core.utils.methodUtilsModel; + +public abstract class AbstractBaseClass { + abstract T get(T param); + + public T getBase(T param) { + return param; + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/AbstractBean.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/AbstractBean.java new file mode 100644 index 00000000000..521f5594a6d --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/AbstractBean.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.swagger.generator.core.utils.methodUtilsModel; + +public abstract class AbstractBean { + private String type; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/BaseInterface.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/BaseInterface.java new file mode 100644 index 00000000000..fd7b60f828c --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/BaseInterface.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.swagger.generator.core.utils.methodUtilsModel; + +public interface BaseInterface { + T get(T param); + + T[] getArray(T[] param); +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/Hello2Endpoint.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/Hello2Endpoint.java new file mode 100644 index 00000000000..42ec7d8e92e --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/Hello2Endpoint.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.swagger.generator.core.utils.methodUtilsModel; + +public class Hello2Endpoint implements ServiceInterface { + @Override + public HelloBean get(HelloBean param) { + return param; + } + + @Override + public HelloBean[] getArray(HelloBean[] param) { + return new HelloBean[0]; + } + + @Override + public HelloBean getBase(HelloBean param) { + return param; + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/HelloBean.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/HelloBean.java new file mode 100644 index 00000000000..e8adb9cd400 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/HelloBean.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.swagger.generator.core.utils.methodUtilsModel; + +public class HelloBean extends AbstractBean { + private String hello; + + public String getHello() { + return hello; + } + + public void setHello(String hello) { + this.hello = hello; + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/HelloEndpoint.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/HelloEndpoint.java new file mode 100644 index 00000000000..1736e044ec7 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/HelloEndpoint.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core.utils.methodUtilsModel; + +public class HelloEndpoint extends AbstractBaseClass { + @Override + public HelloBean get(HelloBean param) { + return param; + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/ServiceInterface.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/ServiceInterface.java new file mode 100644 index 00000000000..5145876fd2c --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/methodUtilsModel/ServiceInterface.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.swagger.generator.core.utils.methodUtilsModel; + +public interface ServiceInterface extends BaseInterface { + HelloBean getBase(HelloBean param); +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/AbstractBaseService.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/AbstractBaseService.java new file mode 100644 index 00000000000..66bc3b59331 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/AbstractBaseService.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.swagger.generator.core.utils.paramUtilsModel; + +import java.util.List; + +public class AbstractBaseService implements IBaseService { + private final IBaseService target; + + protected AbstractBaseService(IBaseService t) { + target = t; + } + + @Override + public T hello(T a) { + return target.hello(a); + } + + @Override + public T[] helloBody(T[] a) { + return target.helloBody(a); + } + + @Override + public List helloList(List a) { + return a; + } + + @Override + public PersonBean actual(PersonBean bean) { + return target.actual(bean); + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/AbstractBean.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/AbstractBean.java new file mode 100644 index 00000000000..9e3dc72b2b8 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/AbstractBean.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.swagger.generator.core.utils.paramUtilsModel; + +public abstract class AbstractBean { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IBaseService.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IBaseService.java new file mode 100644 index 00000000000..d2e0ec4736c --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IBaseService.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.swagger.generator.core.utils.paramUtilsModel; + +import java.util.List; + +public interface IBaseService { + T hello(T a); + + T[] helloBody(T[] a); + + List helloList(List a); + + PersonBean actual(PersonBean bean); +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IMyService.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IMyService.java new file mode 100644 index 00000000000..47cc70e6993 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IMyService.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.swagger.generator.core.utils.paramUtilsModel; + +import java.util.List; + +import org.springframework.web.multipart.MultipartFile; + +public interface IMyService extends IBaseService { + List parentHello(List bean); +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IMyServiceChild.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IMyServiceChild.java new file mode 100644 index 00000000000..d6025ce3a6a --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IMyServiceChild.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.swagger.generator.core.utils.paramUtilsModel; + +public interface IMyServiceChild extends IMyService { +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IMyServiceChild2.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IMyServiceChild2.java new file mode 100644 index 00000000000..7f8c758a6cd --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/IMyServiceChild2.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.swagger.generator.core.utils.paramUtilsModel; + +public interface IMyServiceChild2 extends IMyService, IBaseService { +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/MyEndpoint.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/MyEndpoint.java new file mode 100644 index 00000000000..98ff0123997 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/MyEndpoint.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.swagger.generator.core.utils.paramUtilsModel; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.multipart.MultipartFile; + +public class MyEndpoint extends AbstractBaseService implements IMyService { + public MyEndpoint(@Autowired IMyService other) { + super(other); + } + + @Override + public List parentHello(List bean) { + return null; + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/MyEndpoint2.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/MyEndpoint2.java new file mode 100644 index 00000000000..f8fef7d660b --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/MyEndpoint2.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.swagger.generator.core.utils.paramUtilsModel; + +import java.util.List; + +import org.springframework.web.multipart.MultipartFile; + +public class MyEndpoint2 implements IMyService { + @Override + public PersonBean hello(PersonBean a) { + return null; + } + + @Override + public PersonBean[] helloBody(PersonBean[] a) { + return new PersonBean[0]; + } + + @Override + public List helloList(List a) { + return null; + } + + @Override + public PersonBean actual(PersonBean bean) { + return null; + } + + @Override + public List parentHello(List bean) { + return null; + } +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/PersonBean.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/PersonBean.java new file mode 100644 index 00000000000..508d302fa51 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/utils/paramUtilsModel/PersonBean.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.swagger.generator.core.utils.paramUtilsModel; + +public class PersonBean extends AbstractBean { +} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/log4j.properties b/swagger/swagger-generator/generator-core/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/swagger/swagger-generator/generator-core/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/ParameterAnnotation.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/ParameterAnnotation.yaml new file mode 100644 index 00000000000..af5f0a2e9de --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/ParameterAnnotation.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.core.TestPojo$ParameterAnnotation + version: 1.0.0 +servers: +- url: /ParameterAnnotation +paths: + /differentName: + post: + summary: differentName + operationId: differentName + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/differentNameBody" + application/protobuf: + schema: + $ref: "#/components/schemas/differentNameBody" + text/plain: + schema: + $ref: "#/components/schemas/differentNameBody" + x-name: differentNameBody + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: integer + format: int32 + application/protobuf: + schema: + type: integer + format: int32 + text/plain: + schema: + type: integer + format: int32 +components: + schemas: + differentNameBody: + type: object + properties: + x: + type: integer + format: int32 + "y": + type: integer + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/Schema.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/Schema.yaml new file mode 100644 index 00000000000..0894deeb672 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/Schema.yaml @@ -0,0 +1,1012 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema +paths: + /ignoreRequest: + post: + operationId: ignoreRequest + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ignoreRequestBody" + application/protobuf: + schema: + $ref: "#/components/schemas/ignoreRequestBody" + text/plain: + schema: + $ref: "#/components/schemas/ignoreRequestBody" + x-name: ignoreRequestBody + responses: + "200": + description: response of 200 + /nestedListString: + post: + operationId: nestedListString + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/nestedListStringBody" + application/protobuf: + schema: + $ref: "#/components/schemas/nestedListStringBody" + text/plain: + schema: + $ref: "#/components/schemas/nestedListStringBody" + x-name: nestedListStringBody + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + type: array + items: + type: string + application/protobuf: + schema: + type: array + items: + type: array + items: + type: string + text/plain: + schema: + type: array + items: + type: array + items: + type: string + /testAllType: + post: + operationId: testAllType + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/AllType" + application/protobuf: + schema: + $ref: "#/components/schemas/AllType" + text/plain: + schema: + $ref: "#/components/schemas/AllType" + x-name: obj + responses: + "200": + description: response of 200 + /testApiOperation: + post: + operationId: testApiOperation + responses: + "200": + description: response of 200 + headers: + h: + schema: + type: string + description: "" + nullable: false + example: "" + /testApiResponse: + post: + operationId: testApiResponse + responses: + "200": + description: response of 200 + headers: + h: + schema: + type: string + description: "" + nullable: false + example: "" + /testArray: + post: + operationId: testArray + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testArrayBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testArrayBody" + text/plain: + schema: + $ref: "#/components/schemas/testArrayBody" + x-name: testArrayBody + responses: + "200": + description: response of 200 + /testBoolean: + post: + operationId: testBoolean + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testBooleanBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testBooleanBody" + text/plain: + schema: + $ref: "#/components/schemas/testBooleanBody" + x-name: testBooleanBody + responses: + "200": + description: response of 200 + /testByte: + post: + operationId: testByte + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testByteBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testByteBody" + text/plain: + schema: + $ref: "#/components/schemas/testByteBody" + x-name: testByteBody + responses: + "200": + description: response of 200 + /testBytes: + post: + operationId: testBytes + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testBytesBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testBytesBody" + text/plain: + schema: + $ref: "#/components/schemas/testBytesBody" + x-name: testBytesBody + responses: + "200": + description: response of 200 + /testChar: + post: + operationId: testChar + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testCharBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testCharBody" + text/plain: + schema: + $ref: "#/components/schemas/testCharBody" + x-name: testCharBody + responses: + "200": + description: response of 200 + /testCompletableFuture: + post: + operationId: testCompletableFuture + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /testCompletableFutureOptional: + post: + operationId: testCompletableFutureOptional + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /testDate: + post: + operationId: testDate + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + format: date-time + application/protobuf: + schema: + type: string + format: date-time + text/plain: + schema: + type: string + format: date-time + /testDouble: + post: + operationId: testDouble + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testDoubleBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testDoubleBody" + text/plain: + schema: + $ref: "#/components/schemas/testDoubleBody" + x-name: testDoubleBody + responses: + "200": + description: response of 200 + /testEnum: + post: + operationId: testEnum + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testEnumBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testEnumBody" + text/plain: + schema: + $ref: "#/components/schemas/testEnumBody" + x-name: testEnumBody + responses: + "200": + description: response of 200 + /testFloat: + post: + operationId: testFloat + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testFloatBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testFloatBody" + text/plain: + schema: + $ref: "#/components/schemas/testFloatBody" + x-name: testFloatBody + responses: + "200": + description: response of 200 + /testInteger: + post: + operationId: testInteger + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testIntegerBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testIntegerBody" + text/plain: + schema: + $ref: "#/components/schemas/testIntegerBody" + x-name: testIntegerBody + responses: + "200": + description: response of 200 + /testList: + post: + operationId: testList + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testListBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testListBody" + text/plain: + schema: + $ref: "#/components/schemas/testListBody" + x-name: testListBody + responses: + "200": + description: response of 200 + /testLong: + post: + operationId: testLong + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testLongBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testLongBody" + text/plain: + schema: + $ref: "#/components/schemas/testLongBody" + x-name: testLongBody + responses: + "200": + description: response of 200 + /testMap: + post: + operationId: testMap + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testMapBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testMapBody" + text/plain: + schema: + $ref: "#/components/schemas/testMapBody" + x-name: testMapBody + responses: + "200": + description: response of 200 + /testMapList: + post: + operationId: testMapList + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testMapListBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testMapListBody" + text/plain: + schema: + $ref: "#/components/schemas/testMapListBody" + x-name: testMapListBody + responses: + "200": + description: response of 200 + /testMultiParam: + post: + operationId: testMultiParam + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testMultiParamBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testMultiParamBody" + text/plain: + schema: + $ref: "#/components/schemas/testMultiParamBody" + x-name: testMultiParamBody + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + type: string + application/protobuf: + schema: + type: array + items: + type: string + text/plain: + schema: + type: array + items: + type: string + /testObject: + post: + operationId: testObject + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + application/protobuf: + schema: + $ref: "#/components/schemas/User" + text/plain: + schema: + $ref: "#/components/schemas/User" + x-name: user + responses: + "200": + description: response of 200 + /testOneEnum: + post: + operationId: testOneEnum + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testOneEnumBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testOneEnumBody" + text/plain: + schema: + $ref: "#/components/schemas/testOneEnumBody" + x-name: testOneEnumBody + responses: + "200": + description: response of 200 + /testOptional: + post: + operationId: testOptional + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /testResponseHeader: + post: + operationId: testResponseHeader + responses: + "200": + description: response of 200 + headers: + h: + schema: + type: string + description: "" + nullable: false + example: "" + /testSet: + post: + operationId: testSet + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testSetBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testSetBody" + text/plain: + schema: + $ref: "#/components/schemas/testSetBody" + x-name: testSetBody + responses: + "200": + description: response of 200 + /testShort: + post: + operationId: testShort + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testShortBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testShortBody" + text/plain: + schema: + $ref: "#/components/schemas/testShortBody" + x-name: testShortBody + responses: + "200": + description: response of 200 + /testString: + post: + operationId: testString + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testStringBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testStringBody" + text/plain: + schema: + $ref: "#/components/schemas/testStringBody" + x-name: testStringBody + responses: + "200": + description: response of 200 + /testboolean: + post: + operationId: testboolean + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testbooleanBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testbooleanBody" + text/plain: + schema: + $ref: "#/components/schemas/testbooleanBody" + x-name: testbooleanBody + responses: + "200": + description: response of 200 + /testbyte: + post: + operationId: testbyte + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testbyteBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testbyteBody" + text/plain: + schema: + $ref: "#/components/schemas/testbyteBody" + x-name: testbyteBody + responses: + "200": + description: response of 200 + /testbytes: + post: + operationId: testbytes + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testbytesBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testbytesBody" + text/plain: + schema: + $ref: "#/components/schemas/testbytesBody" + x-name: testbytesBody + responses: + "200": + description: response of 200 + /testchar: + post: + operationId: testchar + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testcharBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testcharBody" + text/plain: + schema: + $ref: "#/components/schemas/testcharBody" + x-name: testcharBody + responses: + "200": + description: response of 200 + /testdouble: + post: + operationId: testdouble + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testdoubleBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testdoubleBody" + text/plain: + schema: + $ref: "#/components/schemas/testdoubleBody" + x-name: testdoubleBody + responses: + "200": + description: response of 200 + /testfloat: + post: + operationId: testfloat + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testfloatBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testfloatBody" + text/plain: + schema: + $ref: "#/components/schemas/testfloatBody" + x-name: testfloatBody + responses: + "200": + description: response of 200 + /testint: + post: + operationId: testint + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testintBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testintBody" + text/plain: + schema: + $ref: "#/components/schemas/testintBody" + x-name: testintBody + responses: + "200": + description: response of 200 + /testlong: + post: + operationId: testlong + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testlongBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testlongBody" + text/plain: + schema: + $ref: "#/components/schemas/testlongBody" + x-name: testlongBody + responses: + "200": + description: response of 200 + /testshort: + post: + operationId: testshort + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testshortBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testshortBody" + text/plain: + schema: + $ref: "#/components/schemas/testshortBody" + x-name: testshortBody + responses: + "200": + description: response of 200 + /wrapToBodyWithDesc: + post: + operationId: wrapToBodyWithDesc + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/wrapToBodyWithDescBody" + application/protobuf: + schema: + $ref: "#/components/schemas/wrapToBodyWithDescBody" + text/plain: + schema: + $ref: "#/components/schemas/wrapToBodyWithDescBody" + x-name: wrapToBodyWithDescBody + responses: + "200": + description: response of 200 +components: + schemas: + ignoreRequestBody: + type: object + properties: + value: + type: integer + format: int32 + nestedListStringBody: + type: object + properties: + param: + type: array + items: + type: array + items: + type: string + AllType: + type: object + properties: + list: + type: array + items: + $ref: "#/components/schemas/User" + x-java-class: org.apache.servicecomb.swagger.generator.core.schema.AllType + User: + type: object + properties: + name: + type: string + friends: + type: array + items: + $ref: "#/components/schemas/User" + x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User + testArrayBody: + type: object + properties: + value: + type: array + items: + type: string + testBooleanBody: + type: object + properties: + value: + type: boolean + testByteBody: + type: object + properties: + value: + type: integer + format: int32 + testBytesBody: + type: object + properties: + value: + type: string + format: byte + testCharBody: + type: object + properties: + value: + type: string + testDoubleBody: + type: object + properties: + value: + type: number + format: double + testEnumBody: + type: object + properties: + color: + type: string + enum: + - RED + - YELLOW + - BLUE + color1: + type: string + enum: + - RED + - YELLOW + - BLUE + testFloatBody: + type: object + properties: + value: + type: number + format: float + testIntegerBody: + type: object + properties: + value: + type: integer + format: int32 + testListBody: + type: object + properties: + value: + type: array + items: + $ref: "#/components/schemas/User" + testLongBody: + type: object + properties: + value: + type: integer + format: int64 + testMapBody: + type: object + properties: + value: + type: object + additionalProperties: + $ref: "#/components/schemas/User" + testMapListBody: + type: object + properties: + value: + type: object + additionalProperties: + type: array + items: + $ref: "#/components/schemas/User" + testMultiParamBody: + type: object + properties: + obj: + $ref: "#/components/schemas/AllType" + bValue: + type: boolean + byteValue: + type: integer + format: int32 + sValue: + type: integer + format: int32 + iValue: + type: integer + format: int32 + lValue: + type: integer + format: int64 + fValue: + type: number + format: float + dValue: + type: number + format: double + enumValue: + type: string + enum: + - RED + - YELLOW + - BLUE + cValue: + type: string + bytes: + type: string + format: byte + strValue: + type: string + strArray: + type: array + items: + type: string + set: + uniqueItems: true + type: array + items: + type: string + list: + type: array + items: + $ref: "#/components/schemas/User" + map: + type: object + additionalProperties: + $ref: "#/components/schemas/User" + testOneEnumBody: + type: object + properties: + color: + type: string + enum: + - RED + - YELLOW + - BLUE + testSetBody: + type: object + properties: + value: + uniqueItems: true + type: array + items: + type: string + testShortBody: + type: object + properties: + value: + type: integer + format: int32 + testStringBody: + type: object + properties: + value: + type: string + testbooleanBody: + type: object + properties: + value: + type: boolean + testbyteBody: + type: object + properties: + value: + type: integer + format: int32 + testbytesBody: + type: object + properties: + value: + type: string + format: byte + testcharBody: + type: object + properties: + value: + type: string + testdoubleBody: + type: object + properties: + value: + type: number + format: double + testfloatBody: + type: object + properties: + value: + type: number + format: float + testintBody: + type: object + properties: + value: + type: integer + format: int32 + testlongBody: + type: object + properties: + value: + type: integer + format: int64 + testshortBody: + type: object + properties: + value: + type: integer + format: int32 + wrapToBodyWithDescBody: + type: object + properties: + desc: + type: integer + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/allMethod.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/allMethod.yaml index 047f93fcd15..809dc9ccec8 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/allMethod.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/allMethod.yaml @@ -1,691 +1,850 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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 io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: - /testBoolean: - post: - operationId: "testBoolean" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testBooleanBody" - responses: - 200: - description: "response of 200" - /testInteger: + /ignoreRequest: + post: + operationId: ignoreRequest + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ignoreRequestBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /nestedListString: + post: + operationId: nestedListString + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/nestedListStringBody' + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + type: array + items: + type: string + /part: + post: + operationId: part + requestBody: + content: + multipart/form-data: + schema: + type: string + format: binary + responses: + "200": + description: response of 200 + content: + application/json: {} + /partArray: + post: + operationId: partArray + requestBody: + content: + multipart/form-data: + schema: + type: array + items: + type: string + format: binary + responses: + "200": + description: response of 200 + content: + application/json: {} + /partList: + post: + operationId: partList + requestBody: + content: + multipart/form-data: + schema: + type: array + items: + type: string + format: binary + responses: + "200": + description: response of 200 + content: + application/json: {} + /testAllType: post: - operationId: "testInteger" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testIntegerBody" + operationId: testAllType responses: - 200: - description: "response of 200" - /testchar: + "200": + description: response of 200 + content: + application/json: {} + /testApiOperation: post: - operationId: "testchar" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testcharBody" + operationId: testApiOperation responses: - 200: - description: "response of 200" - /testSet: + default: + description: response of default + headers: + h: + description: "" + schema: + type: string + description: "" + format: "" + content: {} + "200": + description: response of 200 + content: + application/json: {} + /testApiResponse: post: - operationId: "testSet" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testSetBody" + operationId: testApiResponse responses: - 200: - description: "response of 200" - /testbytes: + "200": + description: response of 200 + headers: + h: + description: "" + schema: + type: string + description: "" + format: "" + content: + application/json: {} + /testArray: post: - operationId: "testbytes" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testbytesBody" - responses: - 200: - description: "response of 200" - /testfloat: + operationId: testArray + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testArrayBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testBoolean: post: - operationId: "testfloat" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testfloatBody" - responses: - 200: - description: "response of 200" - /testLong: + operationId: testBoolean + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testBooleanBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testByte: post: - operationId: "testLong" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testLongBody" - responses: - 200: - description: "response of 200" - /testApiResponse: + operationId: testByte + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testByteBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testBytes: post: - operationId: "testApiResponse" - parameters: [] - responses: - 200: - description: "response of 200" - headers: - h: - type: "string" + operationId: testBytes + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testBytesBody' + responses: + "200": + description: response of 200 + content: + application/json: {} /testChar: post: - operationId: "testChar" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testCharBody" + operationId: testChar + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testCharBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testCompletableFuture: + post: + operationId: testCompletableFuture + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /testCompletableFutureOptional: + post: + operationId: testCompletableFutureOptional + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /testDate: + post: + operationId: testDate responses: - 200: - description: "response of 200" - /testMultiParam: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + format: date-time + /testDouble: post: - operationId: "testMultiParam" - parameters: - - in: "body" - name: "testMultiParamBody" - required: false - schema: - $ref: "#/definitions/testMultiParamBody" - responses: - 200: - description: "response of 200" - schema: - type: "array" - items: - type: "string" - /testObject: + operationId: testDouble + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testDoubleBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testEnum: post: - operationId: "testObject" - parameters: - - in: "body" - name: "user" - required: false - schema: - $ref: "#/definitions/User" - responses: - 200: - description: "response of 200" - /testshort: + operationId: testEnum + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testEnumBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testFloat: post: - operationId: "testshort" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testshortBody" - responses: - 200: - description: "response of 200" - /testdouble: + operationId: testFloat + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testFloatBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testInteger: post: - operationId: "testdouble" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testdoubleBody" - responses: - 200: - description: "response of 200" + operationId: testInteger + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testIntegerBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testList: + post: + operationId: testList + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testListBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testLong: + post: + operationId: testLong + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testLongBody' + responses: + "200": + description: response of 200 + content: + application/json: {} /testMap: post: - operationId: "testMap" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testMapBody" - responses: - 200: - description: "response of 200" + operationId: testMap + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testMapBody' + responses: + "200": + description: response of 200 + content: + application/json: {} /testMapList: post: - operationId: "testMapList" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testMapListBody" - responses: - 200: - description: "response of 200" - /testDouble: + operationId: testMapList + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testMapListBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testMultiParam: post: - operationId: "testDouble" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testDoubleBody" - responses: - 200: - description: "response of 200" + operationId: testMultiParam + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testMultiParamBody' + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + type: string + /testObject: + post: + operationId: testObject + responses: + "200": + description: response of 200 + content: + application/json: {} + /testOneEnum: + post: + operationId: testOneEnum + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testOneEnumBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testOptional: + post: + operationId: testOptional + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string /testResponseHeader: post: - operationId: "testResponseHeader" - parameters: [] + operationId: testResponseHeader responses: - 200: - description: "response of 200" + default: + description: response of default headers: h: - type: "string" - /testApiOperation: + description: "" + schema: + type: string + description: "" + format: "" + content: {} + "200": + description: response of 200 + content: + application/json: {} + /testSet: post: - operationId: "testApiOperation" - parameters: [] - responses: - 200: - description: "response of 200" - headers: - h: - type: "string" - /testArray: + operationId: testSet + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testSetBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testShort: post: - operationId: "testArray" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testArrayBody" - responses: - 200: - description: "response of 200" - /testByte: + operationId: testShort + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testShortBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testString: post: - operationId: "testByte" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testByteBody" - responses: - 200: - description: "response of 200" + operationId: testString + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testStringBody' + responses: + "200": + description: response of 200 + content: + application/json: {} /testboolean: post: - operationId: "testboolean" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testbooleanBody" - responses: - 200: - description: "response of 200" - /testint: - post: - operationId: "testint" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testintBody" - responses: - 200: - description: "response of 200" - /testBytes: - post: - operationId: "testBytes" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testBytesBody" - responses: - 200: - description: "response of 200" - /testEnum: - post: - operationId: "testEnum" - parameters: - - in: "body" - name: "testEnumBody" - required: false - schema: - $ref: "#/definitions/testEnumBody" - responses: - 200: - description: "response of 200" - /testFloat: - post: - operationId: "testFloat" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testFloatBody" - responses: - 200: - description: "response of 200" - /testShort: + operationId: testboolean + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testbooleanBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testbyte: post: - operationId: "testShort" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testShortBody" - responses: - 200: - description: "response of 200" - /testList: + operationId: testbyte + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testbyteBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testbytes: post: - operationId: "testList" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testListBody" - responses: - 200: - description: "response of 200" - /testAllType: + operationId: testbytes + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testbytesBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testchar: post: - operationId: "testAllType" - parameters: - - in: "body" - name: "obj" - required: false - schema: - $ref: "#/definitions/AllType" - responses: - 200: - description: "response of 200" - /testbyte: + operationId: testchar + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testcharBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testdouble: post: - operationId: "testbyte" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testbyteBody" - responses: - 200: - description: "response of 200" - /testString: + operationId: testdouble + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testdoubleBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testfloat: post: - operationId: "testString" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testStringBody" - responses: - 200: - description: "response of 200" - /testDate: + operationId: testfloat + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testfloatBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testint: post: - operationId: "testDate" - parameters: [] - responses: - 200: - description: "response of 200" - schema: - type: "string" - format: "date-time" + operationId: testint + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testintBody' + responses: + "200": + description: response of 200 + content: + application/json: {} /testlong: post: - operationId: "testlong" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testlongBody" - responses: - 200: - description: "response of 200" -definitions: - AllType: - type: "object" - properties: - bValue: - type: "boolean" - byteValue: - type: "integer" - format: "int8" - byteObjectValue: - type: "integer" - format: "int8" - sValue: - type: "integer" - format: "int16" - sObjectValue: - type: "integer" - format: "int16" - iValue: - type: "integer" - format: "int32" - iObjectValue: - type: "integer" - format: "int32" - lValue: - type: "integer" - format: "int64" - lObjectValue: - type: "integer" - format: "int64" - fValue: - type: "number" - format: "float" - fObjectValue: - type: "number" - format: "float" - dValue: - type: "number" - format: "double" - dObjectValue: - type: "number" - format: "double" - enumValue: - type: "string" - enum: - - "WHITE" - - "BLACK" - x-java-class: "io.servicecomb.swagger.generator.core.schema.Color" - cValue: - type: "string" - cObjectValue: - type: "string" - bytes: - type: "string" - format: "byte" - strValue: - type: "string" - set: - type: "array" - uniqueItems: true - items: - type: "string" - list: - type: "array" - items: - $ref: "#/definitions/User" - map: - type: "object" - additionalProperties: - $ref: "#/definitions/User" - x-java-class: "io.servicecomb.swagger.generator.core.schema.AllType" - User: - type: "object" - properties: - name: - type: "string" - age: - type: "integer" - format: "int32" - x-java-class: "io.servicecomb.swagger.generator.core.schema.User" - testArrayBody: - type: "object" - properties: - value: - type: "array" - items: - type: "string" - testBooleanBody: - type: "object" - properties: - value: - type: "boolean" - testByteBody: - type: "object" - properties: - value: - type: "integer" - format: "int8" - testBytesBody: - type: "object" - properties: - value: - type: "string" - format: "byte" - testCharBody: - type: "object" - properties: - value: - type: "string" - testDoubleBody: - type: "object" - properties: - value: - type: "number" - format: "double" - testEnumBody: - type: "object" - properties: - color: - type: "string" - enum: - - "WHITE" - - "BLACK" - x-java-class: "io.servicecomb.swagger.generator.core.schema.Color" - color1: - type: "string" - enum: - - "WHITE" - - "BLACK" - x-java-class: "io.servicecomb.swagger.generator.core.schema.Color" - x-java-class: "gen.cse.ms.ut.testEnumBody" - testFloatBody: - type: "object" - properties: - value: - type: "number" - format: "float" - testIntegerBody: - type: "object" - properties: - value: - type: "integer" - format: "int32" - testListBody: - type: "object" - properties: - value: - type: "array" - items: - $ref: "#/definitions/User" - testLongBody: - type: "object" - properties: - value: - type: "integer" - format: "int64" - testMapBody: - type: "object" - properties: - value: - type: "object" - additionalProperties: - $ref: "#/definitions/User" - testMapListBody: - type: "object" - properties: - value: - type: "object" - additionalProperties: - type: "array" + operationId: testlong + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testlongBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /testshort: + post: + operationId: testshort + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testshortBody' + responses: + "200": + description: response of 200 + content: + application/json: {} + /wrapToBodyWithDesc: + post: + operationId: wrapToBodyWithDesc + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/wrapToBodyWithDescBody' + responses: + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + ignoreRequestBody: + type: object + properties: + value: + type: integer + format: int32 + nestedListStringBody: + type: object + properties: + param: + type: array + items: + type: array + items: + type: string + AllType: + type: object + properties: + list: + type: array + items: + $ref: '#/components/schemas/User' + User: + type: object + properties: + name: + type: string + friends: + type: array + items: + $ref: '#/components/schemas/User' + testArrayBody: + type: object + properties: + value: + type: array + items: + type: string + testBooleanBody: + type: object + properties: + value: + type: boolean + testByteBody: + type: object + properties: + value: + type: string + format: byte + testBytesBody: + type: object + properties: + value: + type: array + items: + type: string + format: byte + testCharBody: + type: object + properties: + value: + type: string + testDoubleBody: + type: object + properties: + value: + type: number + format: double + testEnumBody: + type: object + properties: + color: + type: string + enum: + - RED + - YELLOW + - BLUE + color1: + type: string + enum: + - RED + - YELLOW + - BLUE + testFloatBody: + type: object + properties: + value: + type: number + format: float + testIntegerBody: + type: object + properties: + value: + type: integer + format: int32 + testListBody: + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/User' + testLongBody: + type: object + properties: + value: + type: integer + format: int64 + testMapBody: + type: object + properties: + value: + type: object + additionalProperties: + $ref: '#/components/schemas/User' + testMapListBody: + type: object + properties: + value: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/User' + testMultiParamBody: + type: object + properties: + obj: + $ref: '#/components/schemas/AllType' + bValue: + type: boolean + byteValue: + type: string + format: byte + sValue: + type: integer + format: int32 + iValue: + type: integer + format: int32 + lValue: + type: integer + format: int64 + fValue: + type: number + format: float + dValue: + type: number + format: double + enumValue: + type: string + enum: + - RED + - YELLOW + - BLUE + cValue: + type: string + bytes: + type: array + items: + type: string + format: byte + strValue: + type: string + strArray: + type: array + items: + type: string + set: + uniqueItems: true + type: array + items: + type: string + list: + type: array + items: + $ref: '#/components/schemas/User' + map: + type: object + additionalProperties: + $ref: '#/components/schemas/User' + testOneEnumBody: + type: object + properties: + color: + type: string + enum: + - RED + - YELLOW + - BLUE + testSetBody: + type: object + properties: + value: + uniqueItems: true + type: array + items: + type: string + testShortBody: + type: object + properties: + value: + type: integer + format: int32 + testStringBody: + type: object + properties: + value: + type: string + testbooleanBody: + type: object + properties: + value: + type: boolean + testbyteBody: + type: object + properties: + value: + type: string + format: byte + testbytesBody: + type: object + properties: + value: + type: array items: - $ref: "#/definitions/User" - testMultiParamBody: - type: "object" - properties: - obj: - $ref: "#/definitions/AllType" - bValue: - type: "boolean" - byteValue: - type: "integer" - format: "int8" - sValue: - type: "integer" - format: "int16" - iValue: - type: "integer" - format: "int32" - lValue: - type: "integer" - format: "int64" - fValue: - type: "number" - format: "float" - dValue: - type: "number" - format: "double" - enumValue: - type: "string" - enum: - - "WHITE" - - "BLACK" - x-java-class: "io.servicecomb.swagger.generator.core.schema.Color" - cValue: - type: "string" - bytes: - type: "string" - format: "byte" - strValue: - type: "string" - strArray: - type: "array" - items: - type: "string" - set: - type: "array" - uniqueItems: true - items: - type: "string" - list: - type: "array" - items: - $ref: "#/definitions/User" - map: - type: "object" - additionalProperties: - $ref: "#/definitions/User" - x-java-class: "gen.cse.ms.ut.testMultiParamBody" - testSetBody: - type: "object" - properties: - value: - type: "array" - uniqueItems: true - items: - type: "string" - testShortBody: - type: "object" - properties: - value: - type: "integer" - format: "int16" - testStringBody: - type: "object" - properties: - value: - type: "string" - testbooleanBody: - type: "object" - properties: - value: - type: "boolean" - testbyteBody: - type: "object" - properties: - value: - type: "integer" - format: "int8" - testbytesBody: - type: "object" - properties: - value: - type: "string" - format: "byte" - testcharBody: - type: "object" - properties: - value: - type: "string" - testdoubleBody: - type: "object" - properties: - value: - type: "number" - format: "double" - testfloatBody: - type: "object" - properties: - value: - type: "number" - format: "float" - testintBody: - type: "object" - properties: - value: - type: "integer" - format: "int32" - testlongBody: - type: "object" - properties: - value: - type: "integer" - format: "int64" - testshortBody: - type: "object" - properties: - value: - type: "integer" - format: "int16" + type: string + format: byte + testcharBody: + type: object + properties: + value: + type: string + testdoubleBody: + type: object + properties: + value: + type: number + format: double + testfloatBody: + type: object + properties: + value: + type: number + format: float + testintBody: + type: object + properties: + value: + type: integer + format: int32 + testlongBody: + type: object + properties: + value: + type: integer + format: int64 + testshortBody: + type: object + properties: + value: + type: integer + format: int32 + wrapToBodyWithDescBody: + type: object + properties: + desc: + type: integer + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/allType.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/allType.yaml index 8f0a6706949..b5b5102ae2a 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/allType.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/allType.yaml @@ -1,104 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testAllType: post: - operationId: "testAllType" - parameters: - - in: "body" - name: "obj" - required: false - schema: - $ref: "#/definitions/AllType" + operationId: testAllType responses: - 200: - description: "response of 200" -definitions: - AllType: - type: "object" - properties: - bValue: - type: "boolean" - byteValue: - type: "integer" - format: "int8" - byteObjectValue: - type: "integer" - format: "int8" - sValue: - type: "integer" - format: "int16" - sObjectValue: - type: "integer" - format: "int16" - iValue: - type: "integer" - format: "int32" - iObjectValue: - type: "integer" - format: "int32" - lValue: - type: "integer" - format: "int64" - lObjectValue: - type: "integer" - format: "int64" - fValue: - type: "number" - format: "float" - fObjectValue: - type: "number" - format: "float" - dValue: - type: "number" - format: "double" - dObjectValue: - type: "number" - format: "double" - enumValue: - type: "string" - enum: - - "WHITE" - - "BLACK" - x-java-class: "io.servicecomb.swagger.generator.core.schema.Color" - cValue: - type: "string" - cObjectValue: - type: "string" - bytes: - type: "string" - format: "byte" - strValue: - type: "string" - set: - type: "array" - uniqueItems: true - items: - type: "string" - list: - type: "array" - items: - $ref: "#/definitions/User" - map: - type: "object" - additionalProperties: - $ref: "#/definitions/User" - x-java-class: "io.servicecomb.swagger.generator.core.schema.AllType" - User: - type: "object" - properties: - name: - type: "string" - age: - type: "integer" - format: "int32" - x-java-class: "io.servicecomb.swagger.generator.core.schema.User" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + AllType: + type: object + properties: + list: + type: array + items: + $ref: '#/components/schemas/User' + User: + type: object + properties: + name: + type: string + friends: + type: array + items: + $ref: '#/components/schemas/User' diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiOperation.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiOperation.yaml index 097a2962ecb..d2262b48918 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiOperation.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiOperation.yaml @@ -1,22 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testApiOperation: post: - operationId: "testApiOperation" - parameters: [] + operationId: testApiOperation responses: - 200: - description: "response of 200" + default: + description: response of default headers: h: - type: "string" + description: "" + schema: + type: string + description: "" + format: "" + content: {} + "200": + description: response of 200 + content: + application/json: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiResponse.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiResponse.yaml index 25f127fbbd3..e4b523415d7 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiResponse.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiResponse.yaml @@ -1,22 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testApiResponse: post: - operationId: "testApiResponse" - parameters: [] + operationId: testApiResponse responses: - 200: - description: "response of 200" + "200": + description: response of 200 headers: h: - type: "string" + description: "" + schema: + type: string + description: "" + format: "" + content: + application/json: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/array.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/array.yaml index afda051e3f2..ed2755cde79 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/array.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/array.yaml @@ -1,32 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testArray: post: - operationId: "testArray" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testArrayBody" + operationId: testArray + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testArrayBody' responses: - 200: - description: "response of 200" -definitions: - testArrayBody: - type: "object" - properties: - value: - type: "array" - items: - type: "string" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testArrayBody: + type: object + properties: + value: + type: array + items: + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/boolean.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/boolean.yaml index ccdcdd6ed1d..d2dd735f18c 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/boolean.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/boolean.yaml @@ -1,30 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testboolean: post: - operationId: "testboolean" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testbooleanBody" + operationId: testboolean + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testbooleanBody' + x-name: name responses: - 200: - description: "response of 200" -definitions: - testbooleanBody: - type: "object" - properties: - value: - type: "boolean" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testbooleanBody: + type: object + properties: + value: + type: boolean diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/booleanObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/booleanObject.yaml index fee81fc9a95..f31f4e6fbe8 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/booleanObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/booleanObject.yaml @@ -1,30 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testBoolean: post: - operationId: "testBoolean" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testBooleanBody" + operationId: testBoolean + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testBooleanBody' responses: - 200: - description: "response of 200" -definitions: - testBooleanBody: - type: "object" - properties: - value: - type: "boolean" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testBooleanBody: + type: object + properties: + value: + type: boolean diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/byte.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/byte.yaml index 566d0038e9e..c12584a8cda 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/byte.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/byte.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testbyte: post: - operationId: "testbyte" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testbyteBody" + operationId: testbyte + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testbyteBody' responses: - 200: - description: "response of 200" -definitions: - testbyteBody: - type: "object" - properties: - value: - type: "integer" - format: "int8" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testbyteBody: + type: object + properties: + value: + type: string + format: byte diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/byteObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/byteObject.yaml index ab3b812e3af..3e57a34434a 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/byteObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/byteObject.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testByte: post: - operationId: "testByte" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testByteBody" + operationId: testByte + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testByteBody' responses: - 200: - description: "response of 200" -definitions: - testByteBody: - type: "object" - properties: - value: - type: "integer" - format: "int8" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testByteBody: + type: object + properties: + value: + type: string + format: byte diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytes.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytes.yaml index 5d880eb76d8..76180f8753c 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytes.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytes.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testbytes: post: - operationId: "testbytes" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testbytesBody" + operationId: testbytes + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testbytesBody' responses: - 200: - description: "response of 200" -definitions: - testbytesBody: - type: "object" - properties: - value: - type: "string" - format: "byte" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testbytesBody: + type: object + properties: + value: + type: array + items: + type: string + format: byte diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytesObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytesObject.yaml index f700ac7c739..ed55ed0ca2a 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytesObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytesObject.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testBytes: post: - operationId: "testBytes" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testBytesBody" + operationId: testBytes + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testBytesBody' responses: - 200: - description: "response of 200" -definitions: - testBytesBody: - type: "object" - properties: - value: - type: "string" - format: "byte" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testBytesBody: + type: object + properties: + value: + type: array + items: + type: string + format: byte diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/char.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/char.yaml index 33213463d3b..5e1ce2a0132 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/char.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/char.yaml @@ -1,30 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testchar: post: - operationId: "testchar" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testcharBody" + operationId: testchar + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testcharBody' responses: - 200: - description: "response of 200" -definitions: - testcharBody: - type: "object" - properties: - value: - type: "string" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testcharBody: + type: object + properties: + value: + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/charObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/charObject.yaml index 297e2609ed9..22e2ced3400 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/charObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/charObject.yaml @@ -1,30 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testChar: post: - operationId: "testChar" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testCharBody" + operationId: testChar + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testCharBody' responses: - 200: - description: "response of 200" -definitions: - testCharBody: - type: "object" - properties: - value: - type: "string" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testCharBody: + type: object + properties: + value: + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/completableFuture.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/completableFuture.yaml new file mode 100644 index 00000000000..449ec2cda52 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/completableFuture.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema +paths: + /testCompletableFuture: + post: + operationId: testCompletableFuture + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string +components: + schemas: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/date.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/date.yaml index 4532772e0e2..52572264ec5 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/date.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/date.yaml @@ -1,22 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testDate: post: - operationId: "testDate" - parameters: [] + operationId: testDate responses: - 200: - description: "response of 200" - schema: - type: "string" - format: "date-time" + "200": + description: response of 200 + content: + application/json: + schema: + type: string + format: date-time +components: + schemas: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/double.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/double.yaml index 00f161d2d3f..65584bbf279 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/double.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/double.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testdouble: post: - operationId: "testdouble" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testdoubleBody" + operationId: testdouble + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testdoubleBody' responses: - 200: - description: "response of 200" -definitions: - testdoubleBody: - type: "object" - properties: - value: - type: "number" - format: "double" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testdoubleBody: + type: object + properties: + value: + type: number + format: double diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/doubleObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/doubleObject.yaml index 27c499d2741..e9ff78b1434 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/doubleObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/doubleObject.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testDouble: post: - operationId: "testDouble" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testDoubleBody" + operationId: testDouble + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testDoubleBody' responses: - 200: - description: "response of 200" -definitions: - testDoubleBody: - type: "object" - properties: - value: - type: "number" - format: "double" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testDoubleBody: + type: object + properties: + value: + type: number + format: double diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/enum.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/enum.yaml index efb5dc59513..b7a6d5248e5 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/enum.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/enum.yaml @@ -1,41 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testEnum: post: - operationId: "testEnum" - parameters: - - in: "body" - name: "testEnumBody" - required: false - schema: - $ref: "#/definitions/testEnumBody" + operationId: testEnum + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testEnumBody' responses: - 200: - description: "response of 200" -definitions: - testEnumBody: - type: "object" - properties: - color: - type: "string" - enum: - - "WHITE" - - "BLACK" - x-java-class: "io.servicecomb.swagger.generator.core.schema.Color" - color1: - type: "string" - enum: - - "WHITE" - - "BLACK" - x-java-class: "io.servicecomb.swagger.generator.core.schema.Color" - x-java-class: "gen.cse.ms.ut.testEnumBody" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testEnumBody: + type: object + properties: + color: + type: string + enum: + - RED + - YELLOW + - BLUE + color1: + type: string + enum: + - RED + - YELLOW + - BLUE diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/float.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/float.yaml index 065739fbd68..6839634c7c9 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/float.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/float.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testfloat: post: - operationId: "testfloat" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testfloatBody" + operationId: testfloat + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testfloatBody' responses: - 200: - description: "response of 200" -definitions: - testfloatBody: - type: "object" - properties: - value: - type: "number" - format: "float" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testfloatBody: + type: object + properties: + value: + type: number + format: float diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/floatObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/floatObject.yaml index 718fb0f67e8..112958f2281 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/floatObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/floatObject.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testFloat: post: - operationId: "testFloat" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testFloatBody" + operationId: testFloat + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testFloatBody' responses: - 200: - description: "response of 200" -definitions: - testFloatBody: - type: "object" - properties: - value: - type: "number" - format: "float" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testFloatBody: + type: object + properties: + value: + type: number + format: float diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/ignoreRequest.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/ignoreRequest.yaml new file mode 100644 index 00000000000..091b0168add --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/ignoreRequest.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema +paths: + /ignoreRequest: + post: + operationId: ignoreRequest + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ignoreRequestBody' + responses: + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + ignoreRequestBody: + type: object + properties: + value: + type: integer + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/int.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/int.yaml index 9ee0810b0c4..19bd7678928 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/int.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/int.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testint: post: - operationId: "testint" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testintBody" + operationId: testint + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testintBody' responses: - 200: - description: "response of 200" -definitions: - testintBody: - type: "object" - properties: - value: - type: "integer" - format: "int32" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testintBody: + type: object + properties: + value: + type: integer + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/intObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/intObject.yaml index ee1608b136c..da11ea13e2e 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/intObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/intObject.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testInteger: post: - operationId: "testInteger" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testIntegerBody" + operationId: testInteger + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testIntegerBody' responses: - 200: - description: "response of 200" -definitions: - testIntegerBody: - type: "object" - properties: - value: - type: "integer" - format: "int32" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testIntegerBody: + type: object + properties: + value: + type: integer + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/list.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/list.yaml index 3fbc1c30a5b..e53bdd0ed49 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/list.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/list.yaml @@ -1,41 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testList: post: - operationId: "testList" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testListBody" + operationId: testList + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testListBody' responses: - 200: - description: "response of 200" -definitions: - User: - type: "object" - properties: - name: - type: "string" - age: - type: "integer" - format: "int32" - x-java-class: "io.servicecomb.swagger.generator.core.schema.User" - testListBody: - type: "object" - properties: - value: - type: "array" - items: - $ref: "#/definitions/User" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + User: + type: object + properties: + name: + type: string + friends: + type: array + items: + $ref: '#/components/schemas/User' + testListBody: + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/User' diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/long.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/long.yaml index 9428d62884f..17f716eecac 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/long.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/long.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testlong: post: - operationId: "testlong" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testlongBody" + operationId: testlong + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testlongBody' responses: - 200: - description: "response of 200" -definitions: - testlongBody: - type: "object" - properties: - value: - type: "integer" - format: "int64" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testlongBody: + type: object + properties: + value: + type: integer + format: int64 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/longObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/longObject.yaml index 9a96dbc44a0..9916d768c52 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/longObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/longObject.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testLong: post: - operationId: "testLong" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testLongBody" + operationId: testLong + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testLongBody' responses: - 200: - description: "response of 200" -definitions: - testLongBody: - type: "object" - properties: - value: - type: "integer" - format: "int64" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testLongBody: + type: object + properties: + value: + type: integer + format: int64 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/map.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/map.yaml index a3a23a716f1..918305a1b2c 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/map.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/map.yaml @@ -1,41 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testMap: post: - operationId: "testMap" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testMapBody" + operationId: testMap + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testMapBody' responses: - 200: - description: "response of 200" -definitions: - User: - type: "object" - properties: - name: - type: "string" - age: - type: "integer" - format: "int32" - x-java-class: "io.servicecomb.swagger.generator.core.schema.User" - testMapBody: - type: "object" - properties: - value: - type: "object" - additionalProperties: - $ref: "#/definitions/User" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + User: + type: object + properties: + name: + type: string + friends: + type: array + items: + $ref: '#/components/schemas/User' + testMapBody: + type: object + properties: + value: + type: object + additionalProperties: + $ref: '#/components/schemas/User' diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/mapList.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/mapList.yaml index 990213ae6a6..17cc9c53029 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/mapList.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/mapList.yaml @@ -1,43 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testMapList: post: - operationId: "testMapList" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testMapListBody" + operationId: testMapList + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testMapListBody' responses: - 200: - description: "response of 200" -definitions: - User: - type: "object" - properties: - name: - type: "string" - age: - type: "integer" - format: "int32" - x-java-class: "io.servicecomb.swagger.generator.core.schema.User" - testMapListBody: - type: "object" - properties: - value: - type: "object" - additionalProperties: - type: "array" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + User: + type: object + properties: + name: + type: string + friends: + type: array items: - $ref: "#/definitions/User" + $ref: '#/components/schemas/User' + testMapListBody: + type: object + properties: + value: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/User' diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/multiParam.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/multiParam.yaml index 75bd68f8311..79f69bcdbd7 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/multiParam.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/multiParam.yaml @@ -1,164 +1,117 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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 io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testMultiParam: post: - operationId: "testMultiParam" - parameters: - - in: "body" - name: "testMultiParamBody" - required: false - schema: - $ref: "#/definitions/testMultiParamBody" + operationId: testMultiParam + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testMultiParamBody' responses: - 200: - description: "response of 200" - schema: - type: "array" - items: - type: "string" -definitions: - AllType: - type: "object" - properties: - bValue: - type: "boolean" - byteValue: - type: "integer" - format: "int8" - byteObjectValue: - type: "integer" - format: "int8" - sValue: - type: "integer" - format: "int16" - sObjectValue: - type: "integer" - format: "int16" - iValue: - type: "integer" - format: "int32" - iObjectValue: - type: "integer" - format: "int32" - lValue: - type: "integer" - format: "int64" - lObjectValue: - type: "integer" - format: "int64" - fValue: - type: "number" - format: "float" - fObjectValue: - type: "number" - format: "float" - dValue: - type: "number" - format: "double" - dObjectValue: - type: "number" - format: "double" - enumValue: - type: "string" - enum: - - "WHITE" - - "BLACK" - x-java-class: "io.servicecomb.swagger.generator.core.schema.Color" - cValue: - type: "string" - cObjectValue: - type: "string" - bytes: - type: "string" - format: "byte" - strValue: - type: "string" - set: - type: "array" - uniqueItems: true - items: - type: "string" - list: - type: "array" - items: - $ref: "#/definitions/User" - map: - type: "object" - additionalProperties: - $ref: "#/definitions/User" - x-java-class: "io.servicecomb.swagger.generator.core.schema.AllType" - User: - type: "object" - properties: - name: - type: "string" - age: - type: "integer" - format: "int32" - x-java-class: "io.servicecomb.swagger.generator.core.schema.User" - testMultiParamBody: - type: "object" - properties: - obj: - $ref: "#/definitions/AllType" - bValue: - type: "boolean" - byteValue: - type: "integer" - format: "int8" - sValue: - type: "integer" - format: "int16" - iValue: - type: "integer" - format: "int32" - lValue: - type: "integer" - format: "int64" - fValue: - type: "number" - format: "float" - dValue: - type: "number" - format: "double" - enumValue: - type: "string" - enum: - - "WHITE" - - "BLACK" - x-java-class: "io.servicecomb.swagger.generator.core.schema.Color" - cValue: - type: "string" - bytes: - type: "string" - format: "byte" - strValue: - type: "string" - strArray: - type: "array" - items: - type: "string" - set: - type: "array" - uniqueItems: true - items: - type: "string" - list: - type: "array" - items: - $ref: "#/definitions/User" - map: - type: "object" - additionalProperties: - $ref: "#/definitions/User" - x-java-class: "gen.cse.ms.ut.testMultiParamBody" + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + type: string +components: + schemas: + AllType: + type: object + properties: + list: + type: array + items: + $ref: '#/components/schemas/User' + User: + type: object + properties: + name: + type: string + friends: + type: array + items: + $ref: '#/components/schemas/User' + testMultiParamBody: + type: object + properties: + obj: + $ref: '#/components/schemas/AllType' + bValue: + type: boolean + byteValue: + type: string + format: byte + sValue: + type: integer + format: int32 + iValue: + type: integer + format: int32 + lValue: + type: integer + format: int64 + fValue: + type: number + format: float + dValue: + type: number + format: double + enumValue: + type: string + enum: + - RED + - YELLOW + - BLUE + cValue: + type: string + bytes: + type: array + items: + type: string + format: byte + strValue: + type: string + strArray: + type: array + items: + type: string + set: + uniqueItems: true + type: array + items: + type: string + list: + type: array + items: + $ref: '#/components/schemas/User' + map: + type: object + additionalProperties: + $ref: '#/components/schemas/User' diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/nestedListString.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/nestedListString.yaml new file mode 100644 index 00000000000..a4f39dc9ccf --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/nestedListString.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema +paths: + /nestedListString: + post: + operationId: nestedListString + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/nestedListStringBody' + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + type: array + items: + type: string +components: + schemas: + nestedListStringBody: + type: object + properties: + param: + type: array + items: + type: array + items: + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/object.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/object.yaml index 2e1f41f65a6..b64684d360c 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/object.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/object.yaml @@ -1,34 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testObject: post: - operationId: "testObject" - parameters: - - in: "body" - name: "user" - required: false - schema: - $ref: "#/definitions/User" + operationId: testObject responses: - 200: - description: "response of 200" -definitions: - User: - type: "object" - properties: - name: - type: "string" - age: - type: "integer" - format: "int32" - x-java-class: "io.servicecomb.swagger.generator.core.schema.User" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + User: + type: object + properties: + name: + type: string + friends: + type: array + items: + $ref: '#/components/schemas/User' diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/oneEnum.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/oneEnum.yaml new file mode 100644 index 00000000000..e678748c974 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/oneEnum.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema +paths: + /testOneEnum: + post: + operationId: testOneEnum + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testOneEnumBody' + responses: + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testOneEnumBody: + type: object + properties: + color: + type: string + enum: + - RED + - YELLOW + - BLUE diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/part.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/part.yaml new file mode 100644 index 00000000000..dcc3438e6cf --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/part.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema +paths: + /part: + post: + operationId: part + requestBody: + content: + multipart/form-data: + schema: + type: string + format: binary + responses: + "200": + description: response of 200 + content: + application/json: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/partArray.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/partArray.yaml new file mode 100644 index 00000000000..b308b794694 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/partArray.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.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema +paths: + /partArray: + post: + operationId: partArray + requestBody: + content: + multipart/form-data: + schema: + type: array + items: + type: string + format: binary + responses: + "200": + description: response of 200 + content: + application/json: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/partList.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/partList.yaml new file mode 100644 index 00000000000..359f1f00341 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/partList.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.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema +paths: + /partList: + post: + operationId: partList + requestBody: + content: + multipart/form-data: + schema: + type: array + items: + type: string + format: binary + responses: + "200": + description: response of 200 + content: + application/json: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/pojoExample1.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/pojoExample1.yaml new file mode 100644 index 00000000000..9dcf23ee148 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/pojoExample1.yaml @@ -0,0 +1,97 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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.swagger.generator.core.pojo.PojoExample1 + version: 1.0.0 +servers: +- url: /PojoExample1 +paths: + /testMultiParameter: + post: + operationId: testMultiParameter + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/testMultiParameterBody" + application/protobuf: + schema: + $ref: "#/components/schemas/testMultiParameterBody" + text/plain: + schema: + $ref: "#/components/schemas/testMultiParameterBody" + x-name: testMultiParameterBody + responses: + "200": + description: response of 200 + content: + application/json: + schema: + $ref: "#/components/schemas/XXX" + application/protobuf: + schema: + $ref: "#/components/schemas/XXX" + text/plain: + schema: + $ref: "#/components/schemas/XXX" + /testOneParameter: + post: + operationId: testOneParameter + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/XXX" + application/protobuf: + schema: + $ref: "#/components/schemas/XXX" + text/plain: + schema: + $ref: "#/components/schemas/XXX" + x-name: testType1 + responses: + "200": + description: response of 200 + content: + application/json: + schema: + $ref: "#/components/schemas/XXX" + application/protobuf: + schema: + $ref: "#/components/schemas/XXX" + text/plain: + schema: + $ref: "#/components/schemas/XXX" +components: + schemas: + XXX: + type: object + properties: + val1: + type: integer + format: int32 + x-java-class: org.apache.servicecomb.swagger.generator.core.pojo.TestType1 + testMultiParameterBody: + type: object + properties: + testType1: + $ref: "#/components/schemas/XXX" + testString: + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/responseHeader.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/responseHeader.yaml index babe88b8f52..a6c0498f54e 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/responseHeader.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/responseHeader.yaml @@ -1,22 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testResponseHeader: post: - operationId: "testResponseHeader" - parameters: [] + operationId: testResponseHeader responses: - 200: - description: "response of 200" + default: + description: response of default headers: h: - type: "string" + description: "" + schema: + type: string + description: "" + format: "" + content: {} + "200": + description: response of 200 + content: + application/json: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/set.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/set.yaml index 3027870862b..b1286c94782 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/set.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/set.yaml @@ -1,33 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testSet: post: - operationId: "testSet" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testSetBody" + operationId: testSet + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testSetBody' responses: - 200: - description: "response of 200" -definitions: - testSetBody: - type: "object" - properties: - value: - type: "array" - uniqueItems: true - items: - type: "string" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testSetBody: + type: object + properties: + value: + uniqueItems: true + type: array + items: + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/short.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/short.yaml index 2701d318e18..794f5988caa 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/short.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/short.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testshort: post: - operationId: "testshort" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testshortBody" + operationId: testshort + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testshortBody' responses: - 200: - description: "response of 200" -definitions: - testshortBody: - type: "object" - properties: - value: - type: "integer" - format: "int16" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testshortBody: + type: object + properties: + value: + type: integer + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/shortObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/shortObject.yaml index 3b44e187f05..9fe72b7a15f 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/shortObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/shortObject.yaml @@ -1,31 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testShort: post: - operationId: "testShort" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testShortBody" + operationId: testShort + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testShortBody' responses: - 200: - description: "response of 200" -definitions: - testShortBody: - type: "object" - properties: - value: - type: "integer" - format: "int16" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testShortBody: + type: object + properties: + value: + type: integer + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/string.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/string.yaml index 8d78a04d44b..4a6fc326df8 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/string.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/string.yaml @@ -1,30 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.core.schema.Schema" - x-java-interface: "gen.cse.ms.ut.SchemaIntf" -basePath: "/Schema" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema paths: /testString: post: - operationId: "testString" - parameters: - - in: "body" - name: "value" - required: false - schema: - $ref: "#/definitions/testStringBody" + operationId: testString + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testStringBody' responses: - 200: - description: "response of 200" -definitions: - testStringBody: - type: "object" - properties: - value: - type: "string" + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + testStringBody: + type: object + properties: + value: + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/testCompletableFutureOptional.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/testCompletableFutureOptional.yaml new file mode 100644 index 00000000000..f2cbe41c1df --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/testCompletableFutureOptional.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema +paths: + /testCompletableFutureOptional: + post: + operationId: testCompletableFutureOptional + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string +components: + schemas: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/testOptional.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/testOptional.yaml new file mode 100644 index 00000000000..dcad961f2a9 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/testOptional.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema +paths: + /testOptional: + post: + operationId: testOptional + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string +components: + schemas: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/wrapToBodyWithDesc.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/wrapToBodyWithDesc.yaml new file mode 100644 index 00000000000..7897158842a --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/wrapToBodyWithDesc.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.core.schema.Schema + version: 1.0.0 +servers: +- url: /Schema +paths: + /wrapToBodyWithDesc: + post: + operationId: wrapToBodyWithDesc + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/wrapToBodyWithDescBody' + responses: + "200": + description: response of 200 + content: + application/json: {} +components: + schemas: + wrapToBodyWithDescBody: + type: object + properties: + desc: + type: integer + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/swagger1.yaml b/swagger/swagger-generator/generator-core/src/test/resources/swagger1.yaml new file mode 100644 index 00000000000..add83d161c5 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/swagger1.yaml @@ -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. +## --------------------------------------------------------------------------- + +swagger: '2.0' +info: + title: rest test + version: 1.0.0 +basePath: /springmvc/controller +produces: + - application/json + +paths: + /sayhello: + post: + operationId: sayHello + parameters: + - name: name + in: body + required: true + type: string + responses: + "200": + description: say hello + schema: + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/swagger2.yaml b/swagger/swagger-generator/generator-core/src/test/resources/swagger2.yaml new file mode 100644 index 00000000000..628b6582558 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/resources/swagger2.yaml @@ -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. +## --------------------------------------------------------------------------- + +swagger: '2.0' +info: + title: rest test + version: 1.0.0 +basePath: /springmvc/controller +produces: + - application/json + +paths: + /sayhello: + post: + operationId: sayHello + parameters: + - name: name + in: body + required: true + schema: + type: string + responses: + "200": + description: say hello + schema: + type: string diff --git a/swagger/swagger-generator/generator-jaxrs/pom.xml b/swagger/swagger-generator/generator-jaxrs/pom.xml index e07c12f78d1..1a82b059f8a 100644 --- a/swagger/swagger-generator/generator-jaxrs/pom.xml +++ b/swagger/swagger-generator/generator-jaxrs/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - swagger-generator - 0.3.0-SNAPSHOT - - swagger-generator-jaxrs + 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 + swagger-generator + 3.4.0-SNAPSHOT + + swagger-generator-jaxrs + Java Chassis::Swagger::Generator::Jaxrs - - - io.servicecomb - swagger-generator-core - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + + + org.apache.servicecomb + swagger-generator-core + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.servicecomb + foundation-test-scaffolding + + diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorContext.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorContext.java deleted file mode 100644 index 8f45301cadf..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorContext.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; - -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.HttpMethod; -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.Response; - -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.servicecomb.swagger.generator.jaxrs.processor.annotation.ConsumesAnnotationProcessor; -import io.servicecomb.swagger.generator.jaxrs.processor.annotation.CookieParamAnnotationProcessor; -import io.servicecomb.swagger.generator.jaxrs.processor.annotation.FormParamAnnotationProcessor; -import io.servicecomb.swagger.generator.jaxrs.processor.annotation.HeaderParamAnnotationProcessor; -import io.servicecomb.swagger.generator.jaxrs.processor.annotation.HttpMethodAnnotationProcessor; -import io.servicecomb.swagger.generator.jaxrs.processor.annotation.PathClassAnnotationProcessor; -import io.servicecomb.swagger.generator.jaxrs.processor.annotation.PathMethodAnnotationProcessor; -import io.servicecomb.swagger.generator.jaxrs.processor.annotation.PathParamAnnotationProcessor; -import io.servicecomb.swagger.generator.jaxrs.processor.annotation.ProducesAnnotationProcessor; -import io.servicecomb.swagger.generator.jaxrs.processor.annotation.QueryParamAnnotationProcessor; -import io.servicecomb.swagger.generator.jaxrs.processor.parameter.JaxrsDefaultParameterProcessor; -import io.servicecomb.swagger.generator.jaxrs.processor.response.ResponseProcessor; -import io.servicecomb.swagger.generator.rest.RestSwaggerGeneratorContext; - -public class JaxrsSwaggerGeneratorContext extends RestSwaggerGeneratorContext { - private static final int ORDER = 2000; - - @Override - public int getOrder() { - return ORDER; - } - - @Override - public boolean canProcess(Class cls) { - return ClassUtils.hasAnnotation(cls, Path.class); - } - - @Override - public boolean canProcess(Method method) { - for (Annotation annotation : method.getAnnotations()) { - HttpMethod httpMethod = annotation.annotationType().getAnnotation(HttpMethod.class); - if (httpMethod != null) { - return true; - } - } - - return false; - } - - @Override - protected void initDefaultParameterProcessor() { - defaultParameterProcessor = new JaxrsDefaultParameterProcessor(); - } - - @Override - protected void initClassAnnotationMgr() { - super.initClassAnnotationMgr(); - - classAnnotationMgr.register(Path.class, new PathClassAnnotationProcessor()); - } - - @Override - protected void initMethodAnnotationMgr() { - super.initMethodAnnotationMgr(); - - methodAnnotationMgr.register(Path.class, new PathMethodAnnotationProcessor()); - methodAnnotationMgr.register(Produces.class, new ProducesAnnotationProcessor()); - methodAnnotationMgr.register(Consumes.class, new ConsumesAnnotationProcessor()); - - HttpMethodAnnotationProcessor httpMethodProcessor = new HttpMethodAnnotationProcessor(); - methodAnnotationMgr.register(GET.class, httpMethodProcessor); - methodAnnotationMgr.register(POST.class, httpMethodProcessor); - methodAnnotationMgr.register(PUT.class, httpMethodProcessor); - methodAnnotationMgr.register(DELETE.class, httpMethodProcessor); - } - - @Override - protected void initParameterAnnotationMgr() { - super.initParameterAnnotationMgr(); - - parameterAnnotationMgr.register(PathParam.class, new PathParamAnnotationProcessor()); - parameterAnnotationMgr.register(FormParam.class, new FormParamAnnotationProcessor()); - parameterAnnotationMgr.register(CookieParam.class, new CookieParamAnnotationProcessor()); - - parameterAnnotationMgr.register(HeaderParam.class, new HeaderParamAnnotationProcessor()); - parameterAnnotationMgr.register(QueryParam.class, new QueryParamAnnotationProcessor()); - } - - @Override - protected void initResponseTypeProcessorMgr() { - super.initResponseTypeProcessorMgr(); - - responseTypeProcessorMgr.register(Response.class, new ResponseProcessor()); - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/ConsumesAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/ConsumesAnnotationProcessor.java deleted file mode 100644 index a6be7778a97..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/ConsumesAnnotationProcessor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs.processor.annotation; - -import javax.ws.rs.Consumes; - -import org.springframework.util.StringUtils; - -import io.servicecomb.swagger.generator.core.MethodAnnotationProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; - -public class ConsumesAnnotationProcessor implements MethodAnnotationProcessor { - @Override - public void process(Object annotation, OperationGenerator operationGenerator) { - Consumes consumes = (Consumes) annotation; - - for (String consume : consumes.value()) { - if (!StringUtils.isEmpty(consume)) { - operationGenerator.getOperation().addConsumes(consume); - } - } - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/CookieParamAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/CookieParamAnnotationProcessor.java deleted file mode 100644 index b93cfb0f092..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/CookieParamAnnotationProcessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs.processor.annotation; - -import javax.ws.rs.CookieParam; - -import io.servicecomb.swagger.generator.core.processor.parameter.AbstractParameterProcessor; -import io.swagger.models.parameters.CookieParameter; - -public class CookieParamAnnotationProcessor extends AbstractParameterProcessor { - @Override - protected String getAnnotationParameterName(Object annotation) { - return ((CookieParam) annotation).value(); - } - - @Override - protected CookieParameter createParameter() { - return new CookieParameter(); - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/FormParamAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/FormParamAnnotationProcessor.java deleted file mode 100644 index bdc67463ff4..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/FormParamAnnotationProcessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs.processor.annotation; - -import javax.ws.rs.FormParam; - -import io.servicecomb.swagger.generator.core.processor.parameter.AbstractParameterProcessor; -import io.swagger.models.parameters.FormParameter; - -public class FormParamAnnotationProcessor extends AbstractParameterProcessor { - @Override - protected String getAnnotationParameterName(Object annotation) { - return ((FormParam) annotation).value(); - } - - @Override - protected FormParameter createParameter() { - return new FormParameter(); - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/HeaderParamAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/HeaderParamAnnotationProcessor.java deleted file mode 100644 index 68f5f8a4adc..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/HeaderParamAnnotationProcessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs.processor.annotation; - -import javax.ws.rs.HeaderParam; - -import io.servicecomb.swagger.generator.core.processor.parameter.AbstractParameterProcessor; -import io.swagger.models.parameters.HeaderParameter; - -public class HeaderParamAnnotationProcessor extends AbstractParameterProcessor { - @Override - protected HeaderParameter createParameter() { - return new HeaderParameter(); - } - - @Override - protected String getAnnotationParameterName(Object annotation) { - return ((HeaderParam) annotation).value(); - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/HttpMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/HttpMethodAnnotationProcessor.java deleted file mode 100644 index ca373ef377a..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/HttpMethodAnnotationProcessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs.processor.annotation; - -import java.lang.annotation.Annotation; - -import javax.ws.rs.HttpMethod; - -import io.servicecomb.swagger.generator.core.MethodAnnotationProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; - -public class HttpMethodAnnotationProcessor implements MethodAnnotationProcessor { - @Override - public void process(Object annotation, OperationGenerator operationGenerator) { - Annotation httpMethodAnnotation = (Annotation) annotation; - HttpMethod httpMethod = httpMethodAnnotation.annotationType().getAnnotation(HttpMethod.class); - - operationGenerator.setHttpMethod(httpMethod.value()); - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/PathClassAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/PathClassAnnotationProcessor.java deleted file mode 100644 index 55aa266e0cd..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/PathClassAnnotationProcessor.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs.processor.annotation; - -import javax.ws.rs.Path; - -import io.servicecomb.swagger.generator.core.ClassAnnotationProcessor; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; - -public class PathClassAnnotationProcessor implements ClassAnnotationProcessor { - - @Override - public void process(Object annotation, SwaggerGenerator swaggerGenerator) { - Path path = (Path) annotation; - - swaggerGenerator.setBasePath(path.value()); - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/PathMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/PathMethodAnnotationProcessor.java deleted file mode 100644 index 7150eb17a6f..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/PathMethodAnnotationProcessor.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs.processor.annotation; - -import javax.ws.rs.Path; - -import io.servicecomb.swagger.generator.core.MethodAnnotationProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; - -public class PathMethodAnnotationProcessor implements MethodAnnotationProcessor { - @Override - public void process(Object annotation, OperationGenerator operationGenerator) { - Path path = (Path) annotation; - - operationGenerator.setPath(path.value()); - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/PathParamAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/PathParamAnnotationProcessor.java deleted file mode 100644 index bf3387e2420..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/PathParamAnnotationProcessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs.processor.annotation; - -import javax.ws.rs.PathParam; - -import io.servicecomb.swagger.generator.core.processor.parameter.AbstractParameterProcessor; -import io.swagger.models.parameters.PathParameter; - -public class PathParamAnnotationProcessor extends AbstractParameterProcessor { - @Override - protected PathParameter createParameter() { - return new PathParameter(); - } - - @Override - protected String getAnnotationParameterName(Object annotation) { - return ((PathParam) annotation).value(); - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/ProducesAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/ProducesAnnotationProcessor.java deleted file mode 100644 index 61bd6eb2079..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/ProducesAnnotationProcessor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs.processor.annotation; - -import javax.ws.rs.Produces; - -import org.apache.commons.lang3.StringUtils; - -import io.servicecomb.swagger.generator.core.MethodAnnotationProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; - -public class ProducesAnnotationProcessor implements MethodAnnotationProcessor { - @Override - public void process(Object annotation, OperationGenerator operationGenerator) { - Produces produces = (Produces) annotation; - - for (String produce : produces.value()) { - if (!StringUtils.isEmpty(produce)) { - operationGenerator.getOperation().addProduces(produce); - } - } - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/QueryParamAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/QueryParamAnnotationProcessor.java deleted file mode 100644 index ac2c4e2ae11..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/annotation/QueryParamAnnotationProcessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs.processor.annotation; - -import javax.ws.rs.QueryParam; - -import io.servicecomb.swagger.generator.core.processor.parameter.AbstractParameterProcessor; -import io.swagger.models.parameters.QueryParameter; - -public class QueryParamAnnotationProcessor extends AbstractParameterProcessor { - @Override - protected QueryParameter createParameter() { - return new QueryParameter(); - } - - @Override - protected String getAnnotationParameterName(Object annotation) { - return ((QueryParam) annotation).value(); - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/parameter/JaxrsDefaultParameterProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/parameter/JaxrsDefaultParameterProcessor.java deleted file mode 100644 index dbd2ff34d36..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/parameter/JaxrsDefaultParameterProcessor.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs.processor.parameter; - -import io.servicecomb.swagger.generator.core.DefaultParameterProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.core.utils.ParamUtils; -import io.swagger.models.parameters.BodyParameter; - -public class JaxrsDefaultParameterProcessor implements DefaultParameterProcessor { - - @Override - public void process(OperationGenerator operationGenerator, int paramIndex) { - BodyParameter bodyParameter = ParamUtils.createBodyParameter(operationGenerator, paramIndex); - operationGenerator.addProviderParameter(bodyParameter); - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/response/ResponseProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/response/ResponseProcessor.java deleted file mode 100644 index 3b01723563f..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/java/io/servicecomb/swagger/generator/jaxrs/processor/response/ResponseProcessor.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.jaxrs.processor.response; - -import java.lang.reflect.Type; -import java.util.List; - -import javax.ws.rs.core.MediaType; - -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.core.ResponseTypeProcessor; -import io.servicecomb.swagger.generator.core.utils.ParamUtils; -import io.swagger.converter.ModelConverters; -import io.swagger.models.properties.Property; - -public class ResponseProcessor implements ResponseTypeProcessor { - - @Override - public Property process(OperationGenerator operationGenerator) { - // Response完全表达应答类型 - // 如果produces是text,那么可以假设是string,否则只能报错 - List produces = operationGenerator.getOperation().getProduces(); - if (produces == null) { - produces = operationGenerator.getSwagger().getProduces(); - } - if (produces != null) { - if (produces.contains(MediaType.TEXT_PLAIN)) { - Type responseType = String.class; - ParamUtils.addDefinitions(operationGenerator.getSwagger(), responseType); - return ModelConverters.getInstance().readAsProperty(responseType); - } - } - - throw new Error("Use ApiOperation or ApiResponses to declare response type"); - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsOperationGenerator.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsOperationGenerator.java new file mode 100644 index 00000000000..829b64a4478 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsOperationGenerator.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator.jaxrs; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; +import org.apache.servicecomb.swagger.generator.rest.RestOperationGenerator; + +import jakarta.ws.rs.BeanParam; + +public class JaxrsOperationGenerator extends RestOperationGenerator { + public JaxrsOperationGenerator(AbstractSwaggerGenerator swaggerGenerator, Method method) { + super(swaggerGenerator, method); + } + + @Override + protected void initMethodParameterGenerators(Map> methodAnnotationMap) { + super.initMethodParameterGenerators(methodAnnotationMap); + + parameterGenerators.stream() + .filter(pg -> pg.getHttpParameterType() == null) + .forEach(pg -> pg.setHttpParameterType(HttpParameterType.BODY)); + } + + @Override + protected boolean isAggregatedParameter(ParameterGenerator parameterGenerator, Parameter methodParameter) { + return methodParameter.getAnnotation(BeanParam.class) != null; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGenerator.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGenerator.java new file mode 100644 index 00000000000..6646941d3c8 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGenerator.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.swagger.generator.jaxrs; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +import jakarta.ws.rs.HttpMethod; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.rest.RestSwaggerGenerator; + +public class JaxrsSwaggerGenerator extends RestSwaggerGenerator { + public JaxrsSwaggerGenerator(Class cls) { + super(cls); + } + + @Override + protected boolean isSkipMethod(Method method) { + if (super.isSkipMethod(method)) { + return true; + } + + for (Annotation annotation : method.getAnnotations()) { + HttpMethod httpMethod = annotation.annotationType().getAnnotation(HttpMethod.class); + if (httpMethod != null) { + return false; + } + } + + return true; + } + + @SuppressWarnings("unchecked") + @Override + public T createOperationGenerator(Method method) { + return (T) new JaxrsOperationGenerator(this, method); + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorFactory.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorFactory.java new file mode 100644 index 00000000000..8c60a85671c --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/JaxrsSwaggerGeneratorFactory.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.swagger.generator.jaxrs; + +import jakarta.ws.rs.Path; + +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory; + +public class JaxrsSwaggerGeneratorFactory implements SwaggerGeneratorFactory { + private static final int ORDER = 2000; + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public boolean canProcess(Class cls) { + return SwaggerUtils.hasAnnotation(cls, Path.class); + } + + @Override + public SwaggerGenerator create(Class cls) { + return new JaxrsSwaggerGenerator(cls); + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ConsumesClassAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ConsumesClassAnnotationProcessor.java new file mode 100644 index 00000000000..72ae4d20fc4 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ConsumesClassAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; +import java.util.Arrays; + +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import jakarta.ws.rs.Consumes; + +public class ConsumesClassAnnotationProcessor extends JaxrsClassAnnotationProcessor { + @Override + public Type getProcessType() { + return Consumes.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, Consumes consumes) { + if (consumes.value() != null && consumes.value().length > 0) { + swaggerGenerator.getSwaggerGeneratorContext().updateConsumes(Arrays.asList(consumes.value())); + } + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ConsumesMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ConsumesMethodAnnotationProcessor.java new file mode 100644 index 00000000000..2cfdb26dcda --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ConsumesMethodAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; +import java.util.Arrays; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import jakarta.ws.rs.Consumes; + +public class ConsumesMethodAnnotationProcessor extends JaxrsMethodAnnotationProcessor { + @Override + public Type getProcessType() { + return Consumes.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Consumes consumes) { + if (consumes.value() != null && consumes.value().length > 0) { + operationGenerator.getOperationGeneratorContext().updateConsumes(Arrays.asList(consumes.value())); + } + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/CookieParamParameterAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/CookieParamParameterAnnotationProcessor.java new file mode 100644 index 00000000000..e1987510970 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/CookieParamParameterAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; + +import jakarta.ws.rs.CookieParam; + +public class CookieParamParameterAnnotationProcessor extends + JaxrsParameterAnnotationProcessor { + @Override + public Type getProcessType() { + return CookieParam.class; + } + + @Override + public String getParameterName(CookieParam annotation) { + if (StringUtils.isEmpty(annotation.value())) { + return null; + } + return annotation.value(); + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, CookieParam annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.COOKIE); + if (StringUtils.isNotEmpty(getParameterName(annotation))) { + parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); + } + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/DeleteMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/DeleteMethodAnnotationProcessor.java new file mode 100644 index 00000000000..41df1001556 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/DeleteMethodAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; + +import jakarta.ws.rs.DELETE; + +public class DeleteMethodAnnotationProcessor extends GetMethodAnnotationProcessor { + @Override + public Type getProcessType() { + return DELETE.class; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/FormParamParameterAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/FormParamParameterAnnotationProcessor.java new file mode 100644 index 00000000000..42f5bab47c5 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/FormParamParameterAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; + +import jakarta.ws.rs.FormParam; + +public class FormParamParameterAnnotationProcessor extends JaxrsParameterAnnotationProcessor { + @Override + public Type getProcessType() { + return FormParam.class; + } + + @Override + public String getParameterName(FormParam annotation) { + if (StringUtils.isNotEmpty(annotation.value())) { + return annotation.value(); + } + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, FormParam annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.FORM); + if (StringUtils.isNotEmpty(getParameterName(annotation))) { + parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); + } + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/GetMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/GetMethodAnnotationProcessor.java new file mode 100644 index 00000000000..e8051063a28 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/GetMethodAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HttpMethod; + +public class GetMethodAnnotationProcessor extends JaxrsMethodAnnotationProcessor { + @Override + public Type getProcessType() { + return GET.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Annotation annotation) { + HttpMethod httpMethod = annotation.annotationType().getAnnotation(HttpMethod.class); + + operationGenerator.setHttpMethod(httpMethod.value()); + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/HeaderParamParameterAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/HeaderParamParameterAnnotationProcessor.java new file mode 100644 index 00000000000..41e3b68c82c --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/HeaderParamParameterAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; + +import jakarta.ws.rs.HeaderParam; + +public class HeaderParamParameterAnnotationProcessor extends + JaxrsParameterAnnotationProcessor { + @Override + public Type getProcessType() { + return HeaderParam.class; + } + + @Override + public String getParameterName(HeaderParam annotation) { + if (StringUtils.isNotEmpty(annotation.value())) { + return annotation.value(); + } + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, HeaderParam annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.HEADER); + if (StringUtils.isNotEmpty(getParameterName(annotation))) { + parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); + } + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/JaxrsClassAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/JaxrsClassAnnotationProcessor.java new file mode 100644 index 00000000000..f1f1eb48bbb --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/JaxrsClassAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; + +/** + * A generic class to help classify implementations. + */ +public abstract class JaxrsClassAnnotationProcessor + implements ClassAnnotationProcessor { +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/JaxrsMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/JaxrsMethodAnnotationProcessor.java new file mode 100644 index 00000000000..cb8bd64e1ed --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/JaxrsMethodAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; + +/** + * A generic class to help classify implementations. + */ +public abstract class JaxrsMethodAnnotationProcessor + implements MethodAnnotationProcessor { +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/JaxrsParameterAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/JaxrsParameterAnnotationProcessor.java new file mode 100644 index 00000000000..126f48b31d5 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/JaxrsParameterAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor; + +/** + * A generic class to help classify implementations. + */ +public abstract class JaxrsParameterAnnotationProcessor implements + ParameterAnnotationProcessor { +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PatchMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PatchMethodAnnotationProcessor.java new file mode 100644 index 00000000000..807abd6574d --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PatchMethodAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; + +import jakarta.ws.rs.PATCH; + +public class PatchMethodAnnotationProcessor extends GetMethodAnnotationProcessor { + @Override + public Type getProcessType() { + return PATCH.class; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PathClassAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PathClassAnnotationProcessor.java new file mode 100644 index 00000000000..376ef712832 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PathClassAnnotationProcessor.java @@ -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. + */ + +package org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import jakarta.ws.rs.Path; + +public class PathClassAnnotationProcessor extends JaxrsClassAnnotationProcessor { + @Override + public Type getProcessType() { + return Path.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, Path path) { + swaggerGenerator.setBasePath(path.value()); + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PathMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PathMethodAnnotationProcessor.java new file mode 100644 index 00000000000..2eb1fd39e2b --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PathMethodAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import jakarta.ws.rs.Path; + +public class PathMethodAnnotationProcessor extends JaxrsMethodAnnotationProcessor { + @Override + public Type getProcessType() { + return Path.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Path path) { + operationGenerator.setPath(path.value()); + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PathParamParameterAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PathParamParameterAnnotationProcessor.java new file mode 100644 index 00000000000..9102ee32190 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PathParamParameterAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; + +import jakarta.ws.rs.PathParam; + +public class PathParamParameterAnnotationProcessor extends JaxrsParameterAnnotationProcessor { + @Override + public Type getProcessType() { + return PathParam.class; + } + + @Override + public String getParameterName(PathParam annotation) { + if (StringUtils.isNotEmpty(annotation.value())) { + return annotation.value(); + } + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, PathParam annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.PATH); + parameterGenerator.getParameterGeneratorContext().setRequired(true); + if (StringUtils.isNotEmpty(getParameterName(annotation))) { + parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); + } + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PostMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PostMethodAnnotationProcessor.java new file mode 100644 index 00000000000..e70d2727548 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PostMethodAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; + +import jakarta.ws.rs.POST; + +public class PostMethodAnnotationProcessor extends GetMethodAnnotationProcessor { + @Override + public Type getProcessType() { + return POST.class; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ProducesClassAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ProducesClassAnnotationProcessor.java new file mode 100644 index 00000000000..9450504ce2f --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ProducesClassAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; +import java.util.Arrays; + +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import jakarta.ws.rs.Produces; + +public class ProducesClassAnnotationProcessor extends JaxrsClassAnnotationProcessor { + @Override + public Type getProcessType() { + return Produces.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, Produces produces) { + if (produces.value() != null && produces.value().length > 0) { + swaggerGenerator.getSwaggerGeneratorContext().updateProduces(Arrays.asList(produces.value())); + } + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ProducesMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ProducesMethodAnnotationProcessor.java new file mode 100644 index 00000000000..a148413efd2 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/ProducesMethodAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; +import java.util.Arrays; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import jakarta.ws.rs.Produces; + +public class ProducesMethodAnnotationProcessor extends JaxrsMethodAnnotationProcessor { + @Override + public Type getProcessType() { + return Produces.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Produces produces) { + if (produces.value() != null && produces.value().length > 0) { + operationGenerator.getOperationGeneratorContext().updateProduces(Arrays.asList(produces.value())); + } + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PutMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PutMethodAnnotationProcessor.java new file mode 100644 index 00000000000..9756b05feb1 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/PutMethodAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; + +import jakarta.ws.rs.PUT; + +public class PutMethodAnnotationProcessor extends GetMethodAnnotationProcessor { + @Override + public Type getProcessType() { + return PUT.class; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/QueryParamParameterAnnotationProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/QueryParamParameterAnnotationProcessor.java new file mode 100644 index 00000000000..9f5cb7fc3b6 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/annotation/QueryParamParameterAnnotationProcessor.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.swagger.generator.jaxrs.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; + +import jakarta.ws.rs.QueryParam; + +public class QueryParamParameterAnnotationProcessor extends JaxrsParameterAnnotationProcessor { + @Override + public Type getProcessType() { + return QueryParam.class; + } + + @Override + public String getParameterName(QueryParam annotation) { + if (StringUtils.isNotEmpty(annotation.value())) { + return annotation.value(); + } + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, QueryParam annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.QUERY); + if (StringUtils.isNotEmpty(getParameterName(annotation))) { + parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); + } + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/response/JaxrsResponseProcessor.java b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/response/JaxrsResponseProcessor.java new file mode 100644 index 00000000000..57b1381bd70 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/java/org/apache/servicecomb/swagger/generator/jaxrs/processor/response/JaxrsResponseProcessor.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.swagger.generator.jaxrs.processor.response; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.processor.response.DefaultResponseTypeProcessor; + +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +public class JaxrsResponseProcessor extends DefaultResponseTypeProcessor { + @Override + public Class getProcessType() { + return Response.class; + } + + @Override + public Type extractResponseType(Type genericResponseType) { + return null; + } + + @Override + public Type extractResponseType(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + Type genericResponseType) { + ApiResponses responses = operationGenerator.getOperation().getResponses(); + if (responses != null) { + ApiResponse response = responses.get(SwaggerConst.SUCCESS_KEY); + if (response != null && response.getContent() != null) { + if (response.getContent().get(MediaType.TEXT_PLAIN) != null) { + return String.class; + } + } + } + + throw new IllegalStateException("Use ApiOperation or ApiResponses to declare response type"); + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.SwaggerGeneratorContext b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.SwaggerGeneratorContext deleted file mode 100644 index 665bd1b87de..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.SwaggerGeneratorContext +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -io.servicecomb.swagger.generator.jaxrs.JaxrsSwaggerGeneratorContext \ No newline at end of file diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor new file mode 100644 index 00000000000..bb469664ebd --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PathClassAnnotationProcessor +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.ConsumesClassAnnotationProcessor +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.ProducesClassAnnotationProcessor diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor new file mode 100644 index 00000000000..647fe860ace --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor @@ -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. +# + +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.ConsumesMethodAnnotationProcessor +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.ProducesMethodAnnotationProcessor +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PathMethodAnnotationProcessor +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.GetMethodAnnotationProcessor +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PostMethodAnnotationProcessor +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PutMethodAnnotationProcessor +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.DeleteMethodAnnotationProcessor +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PatchMethodAnnotationProcessor diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor new file mode 100644 index 00000000000..e20efd576e7 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor @@ -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.swagger.generator.jaxrs.processor.annotation.PathParamParameterAnnotationProcessor +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.QueryParamParameterAnnotationProcessor +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.HeaderParamParameterAnnotationProcessor +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.CookieParamParameterAnnotationProcessor +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.FormParamParameterAnnotationProcessor diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor new file mode 100644 index 00000000000..137ee9359bc --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor @@ -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.swagger.generator.jaxrs.processor.response.JaxrsResponseProcessor diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory new file mode 100644 index 00000000000..c96c1805872 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory @@ -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.swagger.generator.jaxrs.JaxrsSwaggerGeneratorFactory diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/ClassMethodNoPath.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/ClassMethodNoPath.java deleted file mode 100644 index 4ee1e0191f9..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/ClassMethodNoPath.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.jaxrs; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; - -@Path("") -public class ClassMethodNoPath { - @GET - public void p1() { - - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/Echo.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/Echo.java deleted file mode 100644 index 7d3193c33b6..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/Echo.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs; - -import java.util.List; - -import javax.ws.rs.Consumes; -import javax.ws.rs.CookieParam; -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.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Response; - -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; - -@Path(value = "Echo") -public class Echo { - @POST - @ApiResponse(response = int.class, code = 200, message = "") - public Response response() { - return null; - } - - @GET - public Response invalidResponse() { - return null; - } - - @POST - @Produces("") - @Consumes("") - @ApiOperation(value = "") - public void emptyPath() { - - } - - @Path(value = "echo/{targetName}") - @Consumes(value = {"json", "xml"}) - @Produces(value = {"json", "xml"}) - @POST - public String echo(User srcUser, @HeaderParam(value = "header") String header, - @PathParam(value = "targetName") String targetName, - @QueryParam(value = "word") String word) { - return String.format("%s %s %s %s", srcUser.name, header, targetName, word); - } - - @Path(value = "cookie") - @POST - public String cookie(@CookieParam(value = "cookie") String cookie) { - return String.format("%s", cookie); - } - - @Path(value = "form") - @POST - public String form(@FormParam(value = "form") String form) { - return String.format("%s", form); - } - - @Path(value = "query") - @GET - public String query(@QueryParam(value = "query") String query) { - return String.format("%s", query); - } - - @Path(value = "query") - @GET - public String queryComplex(@QueryParam(value = "querys") List querys) { - return String.format("%s", querys); - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/MultiDefaultPath.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/MultiDefaultPath.java deleted file mode 100644 index 4f4711afe94..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/MultiDefaultPath.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.jaxrs; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; - -@Path("/abc") -public class MultiDefaultPath { - @GET - public void p1() { - - } - - @GET - public void p2() { - - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/TestJaxrs.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/TestJaxrs.java deleted file mode 100644 index d6a600baa74..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/TestJaxrs.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.swagger.generator.core.CompositeSwaggerGeneratorContext; -import io.servicecomb.swagger.generator.core.SwaggerGeneratorContext; -import io.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; - -public class TestJaxrs { - SwaggerGeneratorContext context = new JaxrsSwaggerGeneratorContext(); - - @Test - public void testMultiDefaultPath() { - UnitTestSwaggerUtils.testException( - "Only allowed one default path. io.servicecomb.swagger.generator.jaxrs.MultiDefaultPath:p2", - context, - MultiDefaultPath.class); - } - - @Test - public void testResponse() throws Exception { - UnitTestSwaggerUtils.testSwagger("schemas/response.yaml", context, Echo.class, "response"); - } - - @Test - public void testInvalidResponse() throws Exception { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.jaxrs.Echo:invalidResponse", - "Use ApiOperation or ApiResponses to declare response type", - context, - Echo.class, - "invalidResponse"); - } - - @Test - public void testEcho() throws Exception { - UnitTestSwaggerUtils.testSwagger("schemas/echo.yaml", context, Echo.class, "echo"); - } - - @Test - public void testForm() throws Exception { - UnitTestSwaggerUtils.testSwagger("schemas/form.yaml", context, Echo.class, "form"); - } - - @Test - public void testQuery() throws Exception { - UnitTestSwaggerUtils.testSwagger("schemas/query.yaml", context, Echo.class, "query"); - } - - @Test - public void testQueryComplex() throws Exception { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.jaxrs.Echo:queryComplex", - "not allow complex type for query parameter, method=io.servicecomb.swagger.generator.jaxrs.Echo:queryComplex, paramIdx=0, type=java.util.List", - context, - Echo.class, - "queryComplex"); - } - - @Test - public void testCookie() throws Exception { - UnitTestSwaggerUtils.testSwagger("schemas/cookie.yaml", context, Echo.class, "cookie"); - } - - @Test - public void testEmptyPath() throws Exception { - UnitTestSwaggerUtils.testSwagger("schemas/emptyPath.yaml", context, Echo.class, "emptyPath"); - } - - @Test - public void testClassMethodNoPath() throws Exception { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.jaxrs.ClassMethodNoPath:p1", - "Path must not both be empty in class and method", - context, - ClassMethodNoPath.class); - } - - @Test - public void testComposite() { - CompositeSwaggerGeneratorContext composite = new CompositeSwaggerGeneratorContext(); - SwaggerGeneratorContext context = composite.selectContext(Echo.class); - - Assert.assertEquals(JaxrsSwaggerGeneratorContext.class, context.getClass()); - } -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/User.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/User.java deleted file mode 100644 index ca1cebd1b7a..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/test/java/io/servicecomb/swagger/generator/jaxrs/User.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.jaxrs; - -public class User { - public String name; -} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/ClassAnnotation.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/ClassAnnotation.java new file mode 100644 index 00000000000..60e1db6880a --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/ClassAnnotation.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.swagger.generator.jaxrs; + +import org.apache.servicecomb.foundation.test.scaffolding.model.User; + +import jakarta.servlet.http.Part; +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; + +@Path("/class") +@Consumes(value = {MediaType.APPLICATION_JSON}) +@Produces(value = {MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) +@SuppressWarnings("unused") +public class ClassAnnotation { + @Path(value = "testBean") + @POST + public User testBean(User user) { + return null; + } + + @Path(value = "testString") + @POST + public String testString(String user) { + return null; + } + + // This case should cause error. Must implicitly specify consumes. + // For JAX RS, @FormParam can be url-encoded-form or multipart. + @Path(value = "testFormWrong") + @POST + public String testFormWrong(@FormParam("param") int param) { + return null; + } + + // This case should cause error. Must implicitly specify consumes + // For JAX RS, @FormParam can be url-encoded-form or multipart. + @Path(value = "testUploadWrong") + @POST + public String testUploadWrong(@FormParam("part") Part part) { + return null; + } + + @Path(value = "testForm") + @POST + @Consumes(value = MediaType.APPLICATION_FORM_URLENCODED) + public String testForm(@FormParam("param") int param) { + return null; + } + + @Path(value = "testUpload") + @POST + @Consumes(value = MediaType.MULTIPART_FORM_DATA) + public String testUpload(@FormParam("part") Part part) { + return null; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/ClassMethodNoPath.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/ClassMethodNoPath.java new file mode 100644 index 00000000000..fc4d2358124 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/ClassMethodNoPath.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.swagger.generator.jaxrs; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +@Path("") +public class ClassMethodNoPath { + @GET + public void p1() { + + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/Echo.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/Echo.java new file mode 100644 index 00000000000..68820b8c6b9 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/Echo.java @@ -0,0 +1,181 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.jaxrs; + +import java.util.List; + +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; +import org.apache.servicecomb.foundation.test.scaffolding.model.User; +import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; +import org.apache.servicecomb.swagger.generator.jaxrs.model.AggregatedParam; +import org.apache.servicecomb.swagger.generator.jaxrs.model.BeanParamComplexField; +import org.apache.servicecomb.swagger.generator.jaxrs.model.BeanParamComplexSetter; +import org.apache.servicecomb.swagger.generator.jaxrs.model.BeanParamDefaultBody; +import org.apache.servicecomb.swagger.generator.jaxrs.model.BeanParamWithJsonIgnoredTagged; +import org.apache.servicecomb.swagger.generator.jaxrs.model.BeanParamWithPart; +import org.apache.servicecomb.swagger.generator.jaxrs.model.enums.DynamicStatus; +import org.apache.servicecomb.swagger.generator.jaxrs.model.enums.DynamicStatusBeanParam; +import org.apache.servicecomb.swagger.generator.jaxrs.model.enums.DynamicStatusModel; +import org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatus; +import org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatusBeanParam; +import org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatusModel; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +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 jakarta.ws.rs.BeanParam; +import jakarta.ws.rs.CookieParam; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.PATCH; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Response; + +@Path(value = "Echo") +public class Echo { + @PATCH + public void patch() { + + } + + @POST + @ApiResponse(content = { + @Content(schema = @Schema(implementation = Integer.class))}, responseCode = "200", description = "") + @Path("response") + public Response response() { + return null; + } + + @POST + @Operation(summary = "") + @Path("emptyPath") + public void emptyPath() { + + } + + @Path(value = "echo/{targetName}") + @POST + public String echo(User srcUser, @HeaderParam(value = "header") String header, + @PathParam(value = "targetName") String targetName, + @QueryParam(value = "word") String word) { + return String.format("%s %s %s %s", srcUser.name, header, targetName, word); + } + + @Path(value = "cookie") + @POST + public String cookie(@CookieParam(value = "cookie") String cookie) { + return String.format("%s", cookie); + } + + @Path(value = "form") + @POST + public String form(@FormParam(value = "form") String form) { + return String.format("%s", form); + } + + @Path(value = "query") + @GET + public String query(@QueryParam(value = "query") String query) { + return String.format("%s", query); + } + + @Path(value = "queryComplex") + @GET + public String queryComplex(@QueryParam(value = "queries") List queries) { + return String.format("%s", queries); + } + + @Operation(summary = "") + public void ignoredNonRestful() { + + } + + @Path(value = "testRawJson") + @POST + public void rawJsonStringMethod(@RawJsonRequestBody String jsonInput) { + } + + @Path(value = "enumBody") + @POST + public void enumBody(Color color) { + } + + @Path("aggregatedParam") + @POST + public void aggregatedParam(@BeanParam AggregatedParam aggregatedParam) { + + } + + @Path("beanParamWithPart") + @POST + public void beanParamWithPart(@BeanParam BeanParamWithPart beanParamWithPart) { + + } + + @Path("beanParamComplexField") + @POST + public void beanParamComplexField(@BeanParam BeanParamComplexField beanParamComplexField) { + + } + + @Path("beanParamComplexSetter") + @POST + public void beanParamComplexSetter(@BeanParam BeanParamComplexSetter beanParamComplexSetter) { + + } + + @Path("beanParamDefaultBody") + @POST + public void beanParamDefaultBody(@BeanParam BeanParamDefaultBody beanParamDefaultBody) { + + } + + @Path("beanParamWithJsonIgnoredTaggedBody") + @POST + public void beanParamWithJsonIgnoredTagged(@BeanParam BeanParamWithJsonIgnoredTagged beanParamWithJsonIgnoredTagged) { + + } + + @Path("nestedListString") + @POST + public List> nestedListString(List> param) { + return param; + } + + @Path("/dynamicStatusEnum") + @POST + public DynamicStatus dynamicStatusEnum(@BeanParam DynamicStatusBeanParam statusBeanParam, + @QueryParam("status") @Parameter(description = "dynamic desc direct") DynamicStatus status, + DynamicStatusModel model) { + return null; + } + + @Path("/jdkStatusEnum") + @POST + public JdkStatus jdkStatusEnum(@BeanParam JdkStatusBeanParam statusBeanParam, + @QueryParam("status") @Parameter(description = "jdk desc direct") JdkStatus status, + JdkStatusModel model) { + return null; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/FullSwaggerService.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/FullSwaggerService.java new file mode 100644 index 00000000000..d8cb38fa481 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/FullSwaggerService.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator.jaxrs; + +import java.io.File; +import java.io.IOException; +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.servlet.http.Part; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.ws.rs.DefaultValue; +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.QueryParam; +import jakarta.ws.rs.core.MediaType; + +@Path(value = "/FullSwaggerService") +public class FullSwaggerService { + @Path("/fileUpload") + @POST + @Produces(MediaType.TEXT_PLAIN) + public String fileUpload(@FormParam("file1") Part file1, @FormParam("file2") Part file2) throws IOException { + return null; + } + + @Path("/queryListMULTI") + @GET + 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; + } + + @Path("/defaultValue") + @GET + public String defaultValue(@QueryParam("e") int e, @DefaultValue("20") @QueryParam("a") int a, + @DefaultValue("bobo") @QueryParam("b") String b, + @DefaultValue("40") @QueryParam("c") Integer c, @Min(value = 20) @Max(value = 30) @QueryParam("d") int d) { + return "Hello " + a + b + c + d + e; + } + + @Path("/textPlain") + @GET + @Produces(MediaType.TEXT_PLAIN) + public String textPlain() { + return null; + } + + @Path("/fileDownload") + @GET + public File fileDownload() { + return null; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/MultiDefaultPath.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/MultiDefaultPath.java new file mode 100644 index 00000000000..ab18732e479 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/MultiDefaultPath.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.swagger.generator.jaxrs; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +@Path("/abc") +public class MultiDefaultPath { + @GET + public void p1() { + + } + + @GET + public void p2() { + + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/TestClassAnnotation.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/TestClassAnnotation.java new file mode 100644 index 00000000000..b17d04f7077 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/TestClassAnnotation.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.swagger.generator.jaxrs; + +import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; +import org.junit.jupiter.api.Test; + +public class TestClassAnnotation { + @Test + public void test_generate_swagger_correct() { + UnitTestSwaggerUtils.testSwagger("schemas/ClassAnnotation.yaml", ClassAnnotation.class, + "testBean", "testString", "testForm", "testUpload"); + } + + @Test + public void test_form_wrong() { + UnitTestSwaggerUtils.testException("Generate swagger operation failed, " + + "method=ClassAnnotation:testFormWrong, cause=Consumes not provided for FORM parameter, " + + "or is empty by annotations rule.", + ClassAnnotation.class, + "testFormWrong"); + } + + @Test + public void test_upload_wrong() { + UnitTestSwaggerUtils.testException("Generate swagger operation failed, " + + "method=ClassAnnotation:testUploadWrong, " + + "cause=Part type must declare consumes multipart/form-data", + ClassAnnotation.class, + "testUploadWrong"); + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/TestJaxrs.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/TestJaxrs.java new file mode 100644 index 00000000000..0b31ba695c5 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/TestJaxrs.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.swagger.generator.jaxrs; + +import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; +import org.apache.servicecomb.swagger.generator.jaxrs.model.ConsumesAndProduces; +import org.junit.jupiter.api.Test; + +public class TestJaxrs { + @Test + public void testMultiDefaultPath() { + UnitTestSwaggerUtils.testException( + "Duplicate operation path detected. method=org.apache.servicecomb.swagger.generator.jaxrs.MultiDefaultPath:p2.", + MultiDefaultPath.class); + } + + @Test + public void testEcho() { + UnitTestSwaggerUtils.testSwagger("schemas/echo.yaml", Echo.class); + } + + + @Test + public void testClassMethodNoPath() { + UnitTestSwaggerUtils.testException( + "Generate swagger operation failed, " + + "method=ClassMethodNoPath:p1, cause=Path must not both be empty in class and method", + ClassMethodNoPath.class); + } + + @Test + public void testFullSwaggerService() { + UnitTestSwaggerUtils.testSwagger("schemas/FullSwaggerService.yaml", FullSwaggerService.class); + } + + @Test + public void consumesAndProduces_exception_testSingleMediaType() { + UnitTestSwaggerUtils.testException("Generate swagger operation failed, " + + "method=ConsumesAndProduces:testSingleMediaType, " + + "cause=Not support media type application/xml", + ConsumesAndProduces.class, "testSingleMediaType"); + } + + @Test + public void consumesAndProduces_exception_testMultipleMediaType() { + UnitTestSwaggerUtils.testException("Generate swagger operation failed, " + + "method=ConsumesAndProduces:testMultipleMediaType, " + + "cause=Not support media type application/xml", + ConsumesAndProduces.class, "testMultipleMediaType"); + } + + @Test + public void consumesAndProduces_exception_testBlankMediaType() { + UnitTestSwaggerUtils.testException("Generate swagger operation failed, " + + "method=ConsumesAndProduces:testBlankMediaType, cause=Not support media type ", + ConsumesAndProduces.class, "testBlankMediaType"); + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/AggregatedParam.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/AggregatedParam.java new file mode 100644 index 00000000000..f4a5b6312d4 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/AggregatedParam.java @@ -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. + */ +package org.apache.servicecomb.swagger.generator.jaxrs.model; + +import java.util.List; + +import jakarta.ws.rs.CookieParam; +import jakarta.ws.rs.DefaultValue; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.QueryParam; + +public class AggregatedParam { + @DefaultValue("pa") + @PathParam("path0") + private String strVal; + + @QueryParam("query1") + private int intVal; + + private long longVal; + + private long cookieVal; + + @HeaderParam("header2") + private String headerVal; + + @QueryParam("query-array") + private String[] queryArray; + + @QueryParam("query-list") + private List queryList; + + public String getStrVal() { + return strVal; + } + + public void setStrVal(String strVal) { + this.strVal = strVal; + } + + public int getIntVal() { + return intVal; + } + + public void setIntVal(int intVal) { + this.intVal = intVal; + } + + public long getLongVal() { + return longVal; + } + + @DefaultValue("12") + @FormParam("form3") + public void setLongVal(long longVal) { + this.longVal = longVal; + } + + public long getCookieVal() { + return cookieVal; + } + + @CookieParam("cookie4") + public void setCookieVal(long cookieVal) { + this.cookieVal = cookieVal; + } + + public String getHeaderVal() { + return headerVal; + } + + public void setHeaderVal(String headerVal) { + this.headerVal = headerVal; + } + + public String[] getQueryArray() { + return queryArray; + } + + public AggregatedParam setQueryArray(String[] queryArray) { + this.queryArray = queryArray; + return this; + } + + public List getQueryList() { + return queryList; + } + + public AggregatedParam setQueryList(List queryList) { + this.queryList = queryList; + return this; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamComplexField.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamComplexField.java new file mode 100644 index 00000000000..db6eb377943 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamComplexField.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.swagger.generator.jaxrs.model; + +import jakarta.ws.rs.QueryParam; + +public class BeanParamComplexField { + @QueryParam("q") + private AggregatedParam complex; + + public AggregatedParam getComplex() { + return complex; + } + + public void setComplex(AggregatedParam complex) { + this.complex = complex; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamComplexSetter.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamComplexSetter.java new file mode 100644 index 00000000000..c2c6b537a41 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamComplexSetter.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.swagger.generator.jaxrs.model; + +import jakarta.ws.rs.HeaderParam; + +public class BeanParamComplexSetter { + private AggregatedParam complex; + + public AggregatedParam getComplex() { + return complex; + } + + @HeaderParam("h") + public void setComplex(AggregatedParam complex) { + this.complex = complex; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamDefaultBody.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamDefaultBody.java new file mode 100644 index 00000000000..7f5eb465045 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamDefaultBody.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.swagger.generator.jaxrs.model; + +public class BeanParamDefaultBody { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamInvalidDefaultBody.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamInvalidDefaultBody.java new file mode 100644 index 00000000000..5960b88d3ca --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamInvalidDefaultBody.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.swagger.generator.jaxrs.model; + +public class BeanParamInvalidDefaultBody { + private String name; + + private int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamWithJsonIgnoredTagged.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamWithJsonIgnoredTagged.java new file mode 100644 index 00000000000..5acea6d419e --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamWithJsonIgnoredTagged.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.swagger.generator.jaxrs.model; + +import jakarta.ws.rs.QueryParam; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +public class BeanParamWithJsonIgnoredTagged { + @QueryParam("name") + private String name; + + @JsonIgnore + private AggregatedParam ignored; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public AggregatedParam getIgnored() { + return ignored; + } + + public void setIgnored( + AggregatedParam ignored) { + this.ignored = ignored; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamWithPart.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamWithPart.java new file mode 100644 index 00000000000..85305b1a61b --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/BeanParamWithPart.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.swagger.generator.jaxrs.model; + +import jakarta.servlet.http.Part; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.QueryParam; + +public class BeanParamWithPart { + @QueryParam("queryStr") + private boolean queryStr; + + @FormParam("up0") + private Part up0; + + private Part up1; + + public boolean isQueryStr() { + return queryStr; + } + + public void setQueryStr(boolean queryStr) { + this.queryStr = queryStr; + } + + public Part getUp0() { + return up0; + } + + public void setUp0(Part up0) { + this.up0 = up0; + } + + public Part getUp1() { + return up1; + } + + @FormParam("up1") + public void setUp1(Part up1) { + this.up1 = up1; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/ConsumesAndProduces.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/ConsumesAndProduces.java new file mode 100644 index 00000000000..b39ea589b5d --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/ConsumesAndProduces.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.swagger.generator.jaxrs.model; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +public class ConsumesAndProduces { + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_XML) + public String testSingleMediaType(String input) { + return input; + } + + @Consumes({MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON}) + @Produces({MediaType.TEXT_PLAIN, MediaType.APPLICATION_XML}) + public String testMultipleMediaType(String input) { + return input; + } + + @Consumes("") + @Produces("") + public String testBlankMediaType(String input) { + return input; + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/DynamicStatus.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/DynamicStatus.java new file mode 100644 index 00000000000..b8323664f4f --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/DynamicStatus.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.swagger.generator.jaxrs.model.enums; + +import org.apache.servicecomb.foundation.common.base.DynamicEnum; +import org.apache.servicecomb.foundation.common.base.DynamicEnumCache; + +import com.fasterxml.jackson.annotation.JsonCreator; + +import io.swagger.v3.oas.annotations.Parameter; + +public class DynamicStatus extends DynamicEnum { + @Parameter(description = "dynamic bad request") + public static final DynamicStatus BAD_REQUEST = new DynamicStatus(400); + + @Parameter(description = "dynamic not found") + public static final DynamicStatus NOT_FOUND = new DynamicStatus(404); + + private static final DynamicEnumCache CACHE = new DynamicEnumCache<>(DynamicStatus.class); + + public DynamicStatus(Integer value) { + super(value); + } + + @JsonCreator + public static DynamicStatus fromValue(int value) { + return CACHE.fromValue(value); + } +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/DynamicStatusBeanParam.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/DynamicStatusBeanParam.java new file mode 100644 index 00000000000..ecbcf2b217e --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/DynamicStatusBeanParam.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.swagger.generator.jaxrs.model.enums; + +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.ws.rs.QueryParam; + +public class DynamicStatusBeanParam { + @Parameter(description = "dynamic desc aggr") + @QueryParam("status-aggr") + public DynamicStatus queryStatus; +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/DynamicStatusModel.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/DynamicStatusModel.java new file mode 100644 index 00000000000..83222c1ce06 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/DynamicStatusModel.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.swagger.generator.jaxrs.model.enums; + +import io.swagger.v3.oas.annotations.Parameter; + +public class DynamicStatusModel { + @Parameter(description = "dynamic status model") + public DynamicStatus status; +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/JdkStatus.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/JdkStatus.java new file mode 100644 index 00000000000..c3a549cde39 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/JdkStatus.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.swagger.generator.jaxrs.model.enums; + +import io.swagger.v3.oas.annotations.Parameter; + +public enum JdkStatus { + @Parameter(description = "jdk bad request") + BAD_REQUEST, + + @Parameter(description = "jdk not found") + NOT_FOUND +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/JdkStatusBeanParam.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/JdkStatusBeanParam.java new file mode 100644 index 00000000000..6462040cbb6 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/JdkStatusBeanParam.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.swagger.generator.jaxrs.model.enums; + +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.ws.rs.QueryParam; + +public class JdkStatusBeanParam { + @Parameter(description = "jdk desc aggr") + @QueryParam("status-aggr") + public JdkStatus queryStatus; +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/JdkStatusModel.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/JdkStatusModel.java new file mode 100644 index 00000000000..013adea499d --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/model/enums/JdkStatusModel.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.swagger.generator.jaxrs.model.enums; + +import io.swagger.v3.oas.annotations.Parameter; + +public class JdkStatusModel { + @Parameter(description = "jdk status model") + public JdkStatus status; +} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/log4j.properties b/swagger/swagger-generator/generator-jaxrs/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/swagger/swagger-generator/generator-jaxrs/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/ClassAnnotation.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/ClassAnnotation.yaml new file mode 100644 index 00000000000..5f8df8369d6 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/ClassAnnotation.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.ClassAnnotation + version: 1.0.0 +servers: +- url: /class +paths: + /testBean: + post: + operationId: testBean + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + x-name: user + responses: + "200": + description: response of 200 + content: + application/json: + schema: + $ref: "#/components/schemas/User" + text/plain: + schema: + $ref: "#/components/schemas/User" + /testForm: + post: + operationId: testForm + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + param: + type: integer + format: int32 + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + text/plain: + schema: + type: string + /testString: + post: + operationId: testString + requestBody: + content: + application/json: + schema: + type: string + x-name: user + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + text/plain: + schema: + type: string + /testUpload: + post: + operationId: testUpload + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + part: + type: string + format: binary + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + text/plain: + schema: + type: string +components: + schemas: + User: + type: object + properties: + name: + type: string + friends: + type: array + items: + $ref: "#/components/schemas/User" + x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/FullSwaggerService.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/FullSwaggerService.yaml new file mode 100644 index 00000000000..39ae7960507 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/FullSwaggerService.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.FullSwaggerService + version: 1.0.0 +servers: +- url: /FullSwaggerService +paths: + /defaultValue: + get: + operationId: defaultValue + parameters: + - name: e + in: query + schema: + type: integer + format: int32 + - name: a + in: query + schema: + type: integer + format: int32 + default: 20 + - name: b + in: query + schema: + type: string + default: bobo + - name: c + in: query + schema: + type: integer + format: int32 + default: 40 + - name: d + in: query + schema: + maximum: 30 + minimum: 20 + type: integer + format: int32 + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /fileDownload: + get: + operationId: fileDownload + responses: + "200": + description: response of 200 + content: + '*/*': + schema: + type: string + format: binary + /fileUpload: + post: + operationId: fileUpload + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + file1: + type: string + format: binary + file2: + type: string + format: binary + responses: + "200": + description: response of 200 + content: + text/plain: + schema: + type: string + /queryListMULTI: + get: + operationId: queryListMULTI + parameters: + - name: queryList + in: query + required: false + style: form + explode: true + schema: + type: array + items: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /textPlain: + get: + operationId: textPlain + responses: + "200": + description: response of 200 + content: + text/plain: + schema: + type: string +components: {} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/aggregatedParam.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/aggregatedParam.yaml new file mode 100644 index 00000000000..a074d0c78c7 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/aggregatedParam.yaml @@ -0,0 +1,77 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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.swagger.generator.jaxrs.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /aggregatedParam: + post: + operationId: "aggregatedParam" + parameters: + - name: "path0" + in: "path" + required: true + type: "string" + default: "pa" + - name: "query1" + in: "query" + required: false + type: "integer" + default: 0 + format: "int32" + - name: "form3" + in: "formData" + required: false + type: "integer" + default: 12 + format: "int64" + - name: "cookie4" + in: "cookie" + required: false + type: "integer" + default: 0 + format: "int64" + - name: "header2" + in: "header" + required: false + type: "string" + - name: "query-array" + in: "query" + required: false + type: "array" + items: + type: "string" + collectionFormat: "multi" + - name: "query-list" + in: "query" + required: false + type: "array" + items: + type: "string" + collectionFormat: "multi" + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/beanParamDefaultBody.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/beanParamDefaultBody.yaml new file mode 100644 index 00000000000..768741ed1af --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/beanParamDefaultBody.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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /beanParamDefaultBody: + post: + operationId: "beanParamDefaultBody" + parameters: + - in: "body" + name: "name" + required: false + schema: + type: "string" + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/beanParamWithJsonIgnoredTagged.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/beanParamWithJsonIgnoredTagged.yaml new file mode 100644 index 00000000000..6ce1e66e5b0 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/beanParamWithJsonIgnoredTagged.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /beanParamWithJsonIgnoredTaggedBody: + post: + operationId: "beanParamWithJsonIgnoredTagged" + parameters: + - name: "name" + in: "query" + required: false + type: "string" + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/beanParamWithPart.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/beanParamWithPart.yaml new file mode 100644 index 00000000000..4423f07ec6f --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/beanParamWithPart.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /beanParamWithPart: + post: + operationId: "beanParamWithPart" + consumes: + - "multipart/form-data" + parameters: + - name: "queryStr" + in: "query" + required: false + type: "boolean" + default: false + - name: "up0" + in: "formData" + required: false + type: "file" + - name: "up1" + in: "formData" + required: false + type: "file" + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/consumes.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/consumes.yaml new file mode 100644 index 00000000000..8da5da55a28 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/consumes.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.model.ConsumesAndProduces + version: 1.0.0 +servers: +- url: /ConsumesAndProduces +paths: + /testBlankMediaType: + post: + operationId: testBlankMediaType + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testBlankMediaTypeBody' + x-name: testBlankMediaTypeBody + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /testMultipleMediaType: + post: + operationId: testMultipleMediaType + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testMultipleMediaTypeBody' + x-name: testMultipleMediaTypeBody + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /testSingleMediaType: + post: + operationId: testSingleMediaType + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/testSingleMediaTypeBody' + x-name: testSingleMediaTypeBody + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string +components: + schemas: + testBlankMediaTypeBody: + type: object + properties: + input: + type: string + testMultipleMediaTypeBody: + type: object + properties: + input: + type: string + testSingleMediaTypeBody: + type: object + properties: + input: + type: string diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/cookie.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/cookie.yaml index dbf55143543..896793e188c 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/cookie.yaml +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/cookie.yaml @@ -1,8 +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. +## --------------------------------------------------------------------------- + --- swagger: "2.0" info: version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.jaxrs.Echo" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: @@ -19,7 +36,7 @@ paths: required: false type: "string" responses: - 200: + "200": description: "response of 200" schema: type: "string" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/dynamicStatusEnum.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/dynamicStatusEnum.yaml new file mode 100644 index 00000000000..c3c5263661d --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/dynamicStatusEnum.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.swagger.generator.jaxrs.Echo + version: 1.0.0 +servers: +- url: Echo +paths: + /dynamicStatusEnum: + post: + operationId: dynamicStatusEnum + parameters: + - name: status-aggr + in: query + description: | + dynamic desc aggr + - 400: dynamic bad request + - 404: dynamic not found + - name: status + in: query + description: | + dynamic desc direct + - 400: dynamic bad request + - 404: dynamic not found + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DynamicStatusModel' + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: integer + description: | + - 400: dynamic bad request + - 404: dynamic not found + format: int32 +components: + schemas: + DynamicStatusModel: + type: object + properties: + status: + type: integer + format: int32 diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/echo.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/echo.yaml index 3ced9822741..7e0fe3b59c4 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/echo.yaml +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/echo.yaml @@ -1,51 +1,563 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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 io.servicecomb.swagger.generator.jaxrs.Echo" - x-java-interface: "gen.cse.ms.ut.EchoIntf" -basePath: "/Echo" -consumes: -- "application/json" -produces: -- "application/json" + title: swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo + version: 1.0.0 +servers: +- url: Echo paths: - /echo/{targetName}: + /aggregatedParam: + post: + operationId: aggregatedParam + parameters: + - name: path0 + in: path + required: true + schema: + type: string + default: pa + - name: query1 + in: query + schema: + type: integer + format: int32 + - name: cookie4 + in: cookie + schema: + type: integer + format: int64 + - name: header2 + in: header + schema: + type: string + - name: query-array + in: query + schema: + type: array + items: + type: string + - name: query-list + in: query + schema: + type: array + items: + type: string + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + form3: + type: integer + format: int64 + responses: + "200": + description: response of 200 + /beanParamComplexField: + post: + operationId: beanParamComplexField + parameters: + - name: q + in: query + schema: + $ref: "#/components/schemas/AggregatedParam" + responses: + "200": + description: response of 200 + /beanParamComplexSetter: + post: + operationId: beanParamComplexSetter + parameters: + - name: h + in: header + schema: + $ref: "#/components/schemas/AggregatedParam" + responses: + "200": + description: response of 200 + /beanParamDefaultBody: post: - operationId: "echo" - consumes: - - "json" - - "xml" - produces: - - "json" - - "xml" + operationId: beanParamDefaultBody + requestBody: + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + x-name: name + responses: + "200": + description: response of 200 + /beanParamWithJsonIgnoredTaggedBody: + post: + operationId: beanParamWithJsonIgnoredTagged parameters: - - in: "body" - name: "srcUser" + - name: name + in: query + schema: + type: string + responses: + "200": + description: response of 200 + /beanParamWithPart: + post: + operationId: beanParamWithPart + parameters: + - name: queryStr + in: query + schema: + type: boolean + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + up0: + type: string + format: binary + up1: + type: string + format: binary + responses: + "200": + description: response of 200 + /cookie: + post: + operationId: cookie + parameters: + - name: cookie + in: cookie + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /dynamicStatusEnum: + post: + operationId: dynamicStatusEnum + parameters: + - name: status-aggr + in: query + description: dynamic desc aggr required: false schema: - $ref: "#/definitions/User" - - name: "header" - in: "header" + type: integer + format: int32 + - name: status + in: query + description: dynamic desc direct required: false - type: "string" - - name: "targetName" - in: "path" + schema: + type: integer + format: int32 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DynamicStatusModel" + application/protobuf: + schema: + $ref: "#/components/schemas/DynamicStatusModel" + text/plain: + schema: + $ref: "#/components/schemas/DynamicStatusModel" + x-name: model + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: integer + format: int32 + application/protobuf: + schema: + type: integer + format: int32 + text/plain: + schema: + type: integer + format: int32 + /echo/{targetName}: + post: + operationId: echo + parameters: + - name: header + in: header + schema: + type: string + - name: targetName + in: path required: true - type: "string" - - name: "word" - in: "query" + schema: + type: string + - name: word + in: query + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + application/protobuf: + schema: + $ref: "#/components/schemas/User" + text/plain: + schema: + $ref: "#/components/schemas/User" + x-name: srcUser + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /emptyPath: + post: + operationId: emptyPath + responses: + "200": + description: response of 200 + /enumBody: + post: + operationId: enumBody + requestBody: + content: + application/json: + schema: + type: string + enum: + - RED + - YELLOW + - BLUE + application/protobuf: + schema: + type: string + enum: + - RED + - YELLOW + - BLUE + text/plain: + schema: + type: string + enum: + - RED + - YELLOW + - BLUE + x-name: color + responses: + "200": + description: response of 200 + /form: + post: + operationId: form + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + form: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /jdkStatusEnum: + post: + operationId: jdkStatusEnum + parameters: + - name: status-aggr + in: query + description: jdk desc aggr + required: false + schema: + type: string + enum: + - BAD_REQUEST + - NOT_FOUND + - name: status + in: query + description: jdk desc direct required: false - type: "string" - responses: - 200: - description: "response of 200" - schema: - type: "string" -definitions: - User: - type: "object" - properties: - name: - type: "string" - x-java-class: "io.servicecomb.swagger.generator.jaxrs.User" + schema: + type: string + enum: + - BAD_REQUEST + - NOT_FOUND + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/JdkStatusModel" + application/protobuf: + schema: + $ref: "#/components/schemas/JdkStatusModel" + text/plain: + schema: + $ref: "#/components/schemas/JdkStatusModel" + x-name: model + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + enum: + - BAD_REQUEST + - NOT_FOUND + application/protobuf: + schema: + type: string + enum: + - BAD_REQUEST + - NOT_FOUND + text/plain: + schema: + type: string + enum: + - BAD_REQUEST + - NOT_FOUND + /nestedListString: + post: + operationId: nestedListString + requestBody: + content: + application/json: + schema: + type: array + items: + type: array + items: + type: string + application/protobuf: + schema: + type: array + items: + type: array + items: + type: string + text/plain: + schema: + type: array + items: + type: array + items: + type: string + x-name: param + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + type: array + items: + type: string + application/protobuf: + schema: + type: array + items: + type: array + items: + type: string + text/plain: + schema: + type: array + items: + type: array + items: + type: string + /: + patch: + operationId: patch + responses: + "200": + description: response of 200 + /query: + get: + operationId: query + parameters: + - name: query + in: query + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /queryComplex: + get: + operationId: queryComplex + parameters: + - name: queries + in: query + schema: + type: array + items: + $ref: "#/components/schemas/User" + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /testRawJson: + post: + operationId: rawJsonStringMethod + requestBody: + content: + application/json: + schema: + type: string + text/plain: + schema: + type: string + required: true + x-raw-json: true + x-name: jsonInput + responses: + "200": + description: response of 200 + /response: + post: + operationId: response + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: integer + format: int32 + application/protobuf: + schema: + type: integer + format: int32 + text/plain: + schema: + type: integer + format: int32 +components: + schemas: + AggregatedParam: + type: object + properties: + strVal: + type: string + intVal: + type: integer + format: int32 + longVal: + type: integer + format: int64 + cookieVal: + type: integer + format: int64 + headerVal: + type: string + queryArray: + type: array + items: + type: string + queryList: + type: array + items: + type: string + x-java-class: org.apache.servicecomb.swagger.generator.jaxrs.model.AggregatedParam + DynamicStatusModel: + type: object + properties: + status: + type: integer + format: int32 + x-java-class: org.apache.servicecomb.swagger.generator.jaxrs.model.enums.DynamicStatusModel + User: + type: object + properties: + name: + type: string + friends: + type: array + items: + $ref: "#/components/schemas/User" + x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User + JdkStatusModel: + type: object + properties: + status: + type: string + enum: + - BAD_REQUEST + - NOT_FOUND + x-java-class: org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatusModel diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/emptyContract.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/emptyContract.yaml new file mode 100644 index 00000000000..11bcbbaa586 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/emptyContract.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "application/json" +produces: +- "application/json" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/emptyPath.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/emptyPath.yaml index f43753df03d..f76bd99fdf8 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/emptyPath.yaml +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/emptyPath.yaml @@ -1,8 +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. +## --------------------------------------------------------------------------- + --- swagger: "2.0" info: version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.jaxrs.Echo" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: @@ -15,5 +32,5 @@ paths: operationId: "emptyPath" parameters: [] responses: - 200: + "200": description: "response of 200" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/enumBody.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/enumBody.yaml new file mode 100644 index 00000000000..b5b78b0232a --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/enumBody.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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /enumBody: + post: + operationId: "enumBody" + parameters: + - in: "body" + name: "color" + required: false + schema: + type: "string" + enum: + - "RED" + - "YELLOW" + - "BLUE" + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/form.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/form.yaml index c100e459da4..911269c20de 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/form.yaml +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/form.yaml @@ -1,8 +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. +## --------------------------------------------------------------------------- + --- swagger: "2.0" info: version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.jaxrs.Echo" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: @@ -19,7 +36,7 @@ paths: required: false type: "string" responses: - 200: + "200": description: "response of 200" schema: type: "string" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/jdkStatusEnum.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/jdkStatusEnum.yaml new file mode 100644 index 00000000000..67efb0def4c --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/jdkStatusEnum.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /jdkStatusEnum: + post: + operationId: "jdkStatusEnum" + parameters: + - name: "status-aggr" + in: "query" + description: "jdk desc aggr\n- BAD_REQUEST: jdk bad request\n- NOT_FOUND:\ + \ jdk not found\n" + required: false + type: "string" + enum: + - "BAD_REQUEST" + - "NOT_FOUND" + - name: "status" + in: "query" + description: "jdk desc direct\n- BAD_REQUEST: jdk bad request\n- NOT_FOUND:\ + \ jdk not found\n" + required: false + type: "string" + enum: + - "BAD_REQUEST" + - "NOT_FOUND" + - in: "body" + name: "model" + required: false + schema: + $ref: "#/definitions/JdkStatusModel" + responses: + "200": + description: "response of 200" + schema: + type: "string" + description: "- BAD_REQUEST: jdk bad request\n- NOT_FOUND: jdk not found\n" + enum: + - "BAD_REQUEST" + - "NOT_FOUND" + x-java-class: "org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatus" +definitions: + JdkStatusModel: + type: "object" + properties: + status: + type: "string" + description: "jdk status model\n- BAD_REQUEST: jdk bad request\n- NOT_FOUND:\ + \ jdk not found\n" + enum: + - "BAD_REQUEST" + - "NOT_FOUND" + x-java-class: "org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatusModel" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/nestedListString.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/nestedListString.yaml new file mode 100644 index 00000000000..338ccdb8d67 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/nestedListString.yaml @@ -0,0 +1,50 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo + version: 1.0.0 +servers: +- url: Echo +paths: + /nestedListString: + post: + operationId: nestedListString + requestBody: + content: + application/json: + schema: + type: array + items: + type: array + items: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + type: array + items: + type: string +components: + schemas: {} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/patch.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/patch.yaml new file mode 100644 index 00000000000..6a89b056f59 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/patch.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /: + patch: + operationId: "patch" + parameters: [] + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/query.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/query.yaml index 4fbdf75ad3c..be256d1d65a 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/query.yaml +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/query.yaml @@ -1,8 +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. +## --------------------------------------------------------------------------- + --- swagger: "2.0" info: version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.jaxrs.Echo" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: @@ -19,7 +36,7 @@ paths: required: false type: "string" responses: - 200: + "200": description: "response of 200" schema: type: "string" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/rawJsonStringMethod.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/rawJsonStringMethod.yaml new file mode 100644 index 00000000000..a482f6c6625 --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/rawJsonStringMethod.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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /testRawJson: + post: + operationId: "rawJsonStringMethod" + parameters: + - in: "body" + name: "jsonInput" + required: true + schema: + type: "string" + x-raw-json: true + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/response.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/response.yaml index 1fe038ff42a..b4db5ae2b39 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/response.yaml +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/response.yaml @@ -1,8 +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. +## --------------------------------------------------------------------------- + --- swagger: "2.0" info: version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.jaxrs.Echo" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: @@ -15,7 +32,7 @@ paths: operationId: "response" parameters: [] responses: - 200: + "200": description: "response of 200" schema: type: "integer" diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/responseText.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/responseText.yaml new file mode 100644 index 00000000000..c596de61aac --- /dev/null +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/responseText.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.jaxrs.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /: + get: + operationId: "responseText" + produces: + - "text/plain" + parameters: [] + responses: + "200": + description: "response of 200" + schema: + type: "string" diff --git a/swagger/swagger-generator/generator-spring-data/pom.xml b/swagger/swagger-generator/generator-spring-data/pom.xml new file mode 100644 index 00000000000..13c8923cd57 --- /dev/null +++ b/swagger/swagger-generator/generator-spring-data/pom.xml @@ -0,0 +1,42 @@ + + + + + + org.apache.servicecomb + swagger-generator + 3.4.0-SNAPSHOT + + 4.0.0 + + swagger-generator-spring-data + Java Chassis::Swagger::Generator::Spring data + + + + org.apache.servicecomb + swagger-generator-core + + + org.springframework.data + spring-data-commons + + + diff --git a/swagger/swagger-generator/generator-spring-data/src/main/java/org/apache/servicecomb/swagger/generator/springdata/SpringDataConcreteTypeRegister.java b/swagger/swagger-generator/generator-spring-data/src/main/java/org/apache/servicecomb/swagger/generator/springdata/SpringDataConcreteTypeRegister.java new file mode 100644 index 00000000000..90b82e2bef8 --- /dev/null +++ b/swagger/swagger-generator/generator-spring-data/src/main/java/org/apache/servicecomb/swagger/generator/springdata/SpringDataConcreteTypeRegister.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.swagger.generator.springdata; + +import java.lang.reflect.Type; +import java.util.Set; + +import org.apache.servicecomb.swagger.extend.ConcreteTypeRegister; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public class SpringDataConcreteTypeRegister implements ConcreteTypeRegister { + @Override + public void register(Set types) { + types.add(Page.class); + types.add(Pageable.class); + } +} diff --git a/swagger/swagger-generator/generator-spring-data/src/main/java/org/apache/servicecomb/swagger/generator/springdata/SpringDataModule.java b/swagger/swagger-generator/generator-spring-data/src/main/java/org/apache/servicecomb/swagger/generator/springdata/SpringDataModule.java new file mode 100644 index 00000000000..b2fb8ccdbaa --- /dev/null +++ b/swagger/swagger-generator/generator-spring-data/src/main/java/org/apache/servicecomb/swagger/generator/springdata/SpringDataModule.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.swagger.generator.springdata; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.foundation.common.utils.SPIOrder; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.Converter; + +public class SpringDataModule extends SimpleModule implements SPIOrder { + private static final long serialVersionUID = 1L; + + @JsonDeserialize(as = PageImpl.class) + @JsonPropertyOrder(alphabetic = true) + public static class PageMixin { + @JsonCreator + public PageMixin(@JsonProperty(value = "content") List content, + @JsonProperty("pageable") Pageable pageable, + @JsonProperty("total") long total) { + } + } + + @JsonDeserialize(as = PageRequest.class) + @JsonPropertyOrder(alphabetic = true) + public static class PageableMixin { + @JsonCreator + public PageableMixin(@JsonProperty(value = "pageNumber") int page, + @JsonProperty("pageSize") int size, @JsonProperty(value = "sort") Sort sort) { + } + } + + public static class SortConverter implements Converter { + @Override + public Sort convert(SortMixin value) { + return Sort.by(value.getProperties()); + } + + @Override + public JavaType getInputType(TypeFactory typeFactory) { + return typeFactory.constructType(SortMixin.class); + } + + @Override + public JavaType getOutputType(TypeFactory typeFactory) { + return typeFactory.constructType(Sort.class); + } + } + + public static class SortMixinConverter implements Converter { + @Override + public SortMixin convert(Sort value) { + List properties = new ArrayList<>(); + for (Order order : value) { + properties.add(order.getProperty()); + } + SortMixin result = new SortMixin(); + result.setProperties(properties.toArray(new String[0])); + return result; + } + + @Override + public JavaType getInputType(TypeFactory typeFactory) { + return typeFactory.constructType(Sort.class); + } + + @Override + public JavaType getOutputType(TypeFactory typeFactory) { + return typeFactory.constructType(SortMixin.class); + } + } + + @JsonPropertyOrder(alphabetic = true) + @JsonDeserialize(converter = SortConverter.class) + @JsonSerialize(converter = SortMixinConverter.class) + public static class SortMixin { + private String[] properties; + + @JsonCreator + public SortMixin() { + } + + public void setProperties(String[] properties) { + this.properties = properties; + } + + public String[] getProperties() { + return this.properties; + } + } + + public SpringDataModule() { + super("springData"); + + setMixInAnnotation(Page.class, PageMixin.class); + setMixInAnnotation(Pageable.class, PageableMixin.class); + setMixInAnnotation(Sort.class, SortMixin.class); + + setMixInAnnotation(PageImpl.class, PageMixin.class); + setMixInAnnotation(PageRequest.class, PageableMixin.class); + } + + @Override + public Object getTypeId() { + return getModuleName(); + } + + @Override + public int getOrder() { + return Short.MAX_VALUE; + } +} diff --git a/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module b/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module new file mode 100644 index 00000000000..161ee4f9e12 --- /dev/null +++ b/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module @@ -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.swagger.generator.springdata.SpringDataModule diff --git a/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.ConcreteTypeRegister b/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.ConcreteTypeRegister new file mode 100644 index 00000000000..42f7ef4bb1a --- /dev/null +++ b/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.ConcreteTypeRegister @@ -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.swagger.generator.springdata.SpringDataConcreteTypeRegister diff --git a/swagger/swagger-generator/generator-spring-data/src/test/java/org/apache/servicecomb/swagger/generator/springdata/TestPageResponseTypeProcessor.java b/swagger/swagger-generator/generator-spring-data/src/test/java/org/apache/servicecomb/swagger/generator/springdata/TestPageResponseTypeProcessor.java new file mode 100644 index 00000000000..2a856ce3d89 --- /dev/null +++ b/swagger/swagger-generator/generator-spring-data/src/test/java/org/apache/servicecomb/swagger/generator/springdata/TestPageResponseTypeProcessor.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator.springdata; + +import java.io.IOException; +import java.util.Arrays; + +import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Direction; + +import io.swagger.v3.core.util.Json; + +public class TestPageResponseTypeProcessor { + interface PageSchema { + Page test(Page page); + } + + @Test + public void swagger() { + UnitTestSwaggerUtils.testSwagger("pageSchema.yaml", PageSchema.class); + } + + @Test + public void deserialize() throws IOException { + Json.mapper().registerModule(new SpringDataModule()); + + Sort sort = Sort.by(Direction.ASC, "name"); + Pageable pageable = PageRequest.of(1, 10, sort); + Page page = new PageImpl<>(Arrays.asList("c1", "c2"), pageable, 2); + String json = Json.mapper().writeValueAsString(page); + Assertions.assertEquals( + "{\"content\":[\"c1\",\"c2\"],\"pageable\":{\"pageNumber\":1,\"pageSize\":10,\"sort\":{\"properties\":[\"name\"]},\"offset\":10,\"paged\":true,\"unpaged\":false},\"empty\":false,\"first\":false,\"last\":true,\"number\":1,\"numberOfElements\":2,\"size\":10,\"sort\":{\"properties\":[\"name\"]},\"totalElements\":12,\"totalPages\":2}", + json); + + Page page2 = Json.mapper().readValue(json, Page.class); + + Assertions.assertEquals(json, + Json.mapper().writeValueAsString(page2)); + } +} diff --git a/swagger/swagger-generator/generator-spring-data/src/test/resources/pageSchema.yaml b/swagger/swagger-generator/generator-spring-data/src/test/resources/pageSchema.yaml new file mode 100644 index 00000000000..255ccceaca7 --- /dev/null +++ b/swagger/swagger-generator/generator-spring-data/src/test/resources/pageSchema.yaml @@ -0,0 +1,117 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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.swagger.generator.springdata.TestPageResponseTypeProcessor$PageSchema + version: 1.0.0 +servers: +- url: /PageSchema +paths: + /test: + post: + operationId: test + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PageString" + application/protobuf: + schema: + $ref: "#/components/schemas/PageString" + text/plain: + schema: + $ref: "#/components/schemas/PageString" + x-name: page + responses: + "200": + description: response of 200 + content: + application/json: + schema: + $ref: "#/components/schemas/PageString" + application/protobuf: + schema: + $ref: "#/components/schemas/PageString" + text/plain: + schema: + $ref: "#/components/schemas/PageString" +components: + schemas: + PageString: + type: object + properties: + content: + type: array + items: + type: string + empty: + type: boolean + first: + type: boolean + last: + type: boolean + number: + type: integer + format: int32 + numberOfElements: + type: integer + format: int32 + pageable: + $ref: "#/components/schemas/Pageable" + size: + type: integer + format: int32 + sort: + $ref: "#/components/schemas/Sort" + totalElements: + type: integer + format: int64 + totalPages: + type: integer + format: int32 + x-java-class: org.springframework.data.domain.Page + Pageable: + type: object + properties: + offset: + type: integer + format: int64 + pageNumber: + type: integer + format: int32 + pageSize: + type: integer + format: int32 + paged: + type: boolean + sort: + $ref: "#/components/schemas/Sort" + unpaged: + type: boolean + x-java-class: org.springframework.data.domain.Pageable + Sort: + type: object + properties: + empty: + type: boolean + sorted: + type: boolean + unsorted: + type: boolean + x-java-class: org.springframework.data.domain.Sort diff --git a/swagger/swagger-generator/generator-springmvc/pom.xml b/swagger/swagger-generator/generator-springmvc/pom.xml index ec2719f458a..c4225f11a96 100644 --- a/swagger/swagger-generator/generator-springmvc/pom.xml +++ b/swagger/swagger-generator/generator-springmvc/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - swagger-generator - 0.3.0-SNAPSHOT - - swagger-generator-springmvc + 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 + swagger-generator + 3.4.0-SNAPSHOT + + swagger-generator-springmvc + Java Chassis::Swagger::Generator::Spring MVC - - - org.springframework - spring-webmvc - - - io.servicecomb - swagger-generator-core - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + + + org.springframework + spring-webmvc + + + org.apache.servicecomb + swagger-generator-core + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.servicecomb + foundation-test-scaffolding + + diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGeneratorContext.java b/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGeneratorContext.java deleted file mode 100644 index b92f8f3dd0b..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGeneratorContext.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc; - -import java.lang.reflect.Method; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.CookieValue; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestAttribute; -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.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.servicecomb.swagger.generator.rest.RestSwaggerGeneratorContext; -import io.servicecomb.swagger.generator.springmvc.processor.annotation.CookieValueAnnotationProcessor; -import io.servicecomb.swagger.generator.springmvc.processor.annotation.PathVariableAnnotationProcessor; -import io.servicecomb.swagger.generator.springmvc.processor.annotation.RequestAttributeAnnotationProcessor; -import io.servicecomb.swagger.generator.springmvc.processor.annotation.RequestBodyAnnotationProcessor; -import io.servicecomb.swagger.generator.springmvc.processor.annotation.RequestHeaderAnnotationProcessor; -import io.servicecomb.swagger.generator.springmvc.processor.annotation.RequestMappingClassAnnotationProcessor; -import io.servicecomb.swagger.generator.springmvc.processor.annotation.RequestMappingMethodAnnotationProcessor; -import io.servicecomb.swagger.generator.springmvc.processor.annotation.RequestParamAnnotationProcessor; -import io.servicecomb.swagger.generator.springmvc.processor.parameter.SpringmvcDefaultParameterProcessor; -import io.servicecomb.swagger.generator.springmvc.processor.response.ResponseEntityProcessor; - -public class SpringmvcSwaggerGeneratorContext extends RestSwaggerGeneratorContext { - private static final int ORDER = 1000; - - @Override - public int getOrder() { - return ORDER; - } - - @Override - public boolean canProcess(Class cls) { - return ClassUtils.hasAnnotation(cls, RequestMapping.class); - } - - @Override - public boolean canProcess(Method method) { - return method.getAnnotation(RequestMapping.class) != null; - } - - @Override - protected void initClassAnnotationMgr() { - super.initClassAnnotationMgr(); - - classAnnotationMgr.register(RequestMapping.class, new RequestMappingClassAnnotationProcessor()); - } - - @Override - protected void initMethodAnnotationMgr() { - super.initMethodAnnotationMgr(); - - methodAnnotationMgr.register(RequestMapping.class, new RequestMappingMethodAnnotationProcessor()); - } - - @Override - protected void initParameterAnnotationMgr() { - super.initParameterAnnotationMgr(); - - parameterAnnotationMgr.register(CookieValue.class, new CookieValueAnnotationProcessor()); - parameterAnnotationMgr.register(PathVariable.class, new PathVariableAnnotationProcessor()); - parameterAnnotationMgr.register(RequestBody.class, new RequestBodyAnnotationProcessor()); - parameterAnnotationMgr.register(RequestHeader.class, new RequestHeaderAnnotationProcessor()); - parameterAnnotationMgr.register(RequestParam.class, new RequestParamAnnotationProcessor()); - parameterAnnotationMgr.register(RequestAttribute.class, new RequestAttributeAnnotationProcessor()); - } - - @Override - protected void initParameterTypeProcessorMgr() { - super.initParameterTypeProcessorMgr(); - } - - @Override - protected void initDefaultParameterProcessor() { - defaultParameterProcessor = new SpringmvcDefaultParameterProcessor(); - } - - @Override - protected void initResponseTypeProcessorMgr() { - super.initResponseTypeProcessorMgr(); - - responseTypeProcessorMgr.register(ResponseEntity.class, new ResponseEntityProcessor()); - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/CookieValueAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/CookieValueAnnotationProcessor.java deleted file mode 100644 index e5c4afd215e..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/CookieValueAnnotationProcessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc.processor.annotation; - -import org.springframework.web.bind.annotation.CookieValue; - -import io.servicecomb.swagger.generator.core.processor.parameter.AbstractParameterProcessor; -import io.swagger.models.parameters.CookieParameter; - -public class CookieValueAnnotationProcessor extends AbstractParameterProcessor { - @Override - protected String getAnnotationParameterName(Object annotation) { - return ((CookieValue) annotation).name(); - } - - @Override - protected CookieParameter createParameter() { - return new CookieParameter(); - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/PathVariableAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/PathVariableAnnotationProcessor.java deleted file mode 100644 index f9d135314dc..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/PathVariableAnnotationProcessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc.processor.annotation; - -import org.springframework.web.bind.annotation.PathVariable; - -import io.servicecomb.swagger.generator.core.processor.parameter.AbstractParameterProcessor; -import io.swagger.models.parameters.PathParameter; - -public class PathVariableAnnotationProcessor extends AbstractParameterProcessor { - @Override - protected PathParameter createParameter() { - return new PathParameter(); - } - - @Override - protected String getAnnotationParameterName(Object annotation) { - return ((PathVariable) annotation).value(); - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestAttributeAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestAttributeAnnotationProcessor.java deleted file mode 100644 index b3ecbb2cf39..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestAttributeAnnotationProcessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc.processor.annotation; - -import org.springframework.web.bind.annotation.RequestAttribute; - -import io.servicecomb.swagger.generator.core.processor.parameter.AbstractParameterProcessor; -import io.swagger.models.parameters.FormParameter; - -public class RequestAttributeAnnotationProcessor extends AbstractParameterProcessor { - @Override - protected FormParameter createParameter() { - return new FormParameter(); - } - - @Override - protected String getAnnotationParameterName(Object annotation) { - return ((RequestAttribute) annotation).name(); - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestBodyAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestBodyAnnotationProcessor.java deleted file mode 100644 index c5eb6c789c5..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestBodyAnnotationProcessor.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc.processor.annotation; - -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.core.ParameterAnnotationProcessor; -import io.servicecomb.swagger.generator.core.utils.ParamUtils; -import io.swagger.models.parameters.BodyParameter; - -public class RequestBodyAnnotationProcessor implements ParameterAnnotationProcessor { - @Override - public void process(Object annotation, OperationGenerator operationGenerator, int paramIdx) { - BodyParameter bodyParameter = ParamUtils.createBodyParameter(operationGenerator, paramIdx); - operationGenerator.addProviderParameter(bodyParameter); - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestHeaderAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestHeaderAnnotationProcessor.java deleted file mode 100644 index d59e4a3fc65..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestHeaderAnnotationProcessor.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc.processor.annotation; - -import org.springframework.web.bind.annotation.RequestHeader; - -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.core.processor.parameter.AbstractParameterProcessor; -import io.swagger.models.parameters.HeaderParameter; - -public class RequestHeaderAnnotationProcessor extends AbstractParameterProcessor { - @Override - protected HeaderParameter createParameter() { - return new HeaderParameter(); - } - - @Override - protected String getAnnotationParameterName(Object annotation) { - return ((RequestHeader) annotation).name(); - } - - @Override - protected void fillParameter(Object annotation, OperationGenerator operationGenerator, int paramIdx, - HeaderParameter parameter) { - super.fillParameter(annotation, operationGenerator, paramIdx, parameter); - - RequestHeader requestHeader = (RequestHeader) annotation; - parameter.setRequired(requestHeader.required()); - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingClassAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingClassAnnotationProcessor.java deleted file mode 100644 index dee84592063..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingClassAnnotationProcessor.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc.processor.annotation; - -import java.util.Arrays; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -import io.servicecomb.swagger.generator.core.ClassAnnotationProcessor; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.swagger.models.Swagger; - -public class RequestMappingClassAnnotationProcessor implements ClassAnnotationProcessor { - - @Override - public void process(Object annotation, SwaggerGenerator swaggerGenerator) { - RequestMapping requestMapping = (RequestMapping) annotation; - Swagger swagger = swaggerGenerator.getSwagger(); - - this.processMethod(requestMapping.method(), swaggerGenerator); - - // path/value是等同的 - this.processPath(requestMapping.path(), swaggerGenerator); - this.processPath(requestMapping.value(), swaggerGenerator); - this.processConsumes(requestMapping.consumes(), swagger); - this.processProduces(requestMapping.produces(), swagger); - } - - protected void processPath(String[] paths, SwaggerGenerator swaggerGenerator) { - if (null == paths || paths.length == 0) { - return; - } - - // swagger仅支持配一个basePath - if (paths.length > 1) { - throw new Error("not support multi path for " + swaggerGenerator.getCls().getName()); - } - - swaggerGenerator.setBasePath(paths[0]); - } - - protected void processMethod(RequestMethod[] requestMethods, SwaggerGenerator swaggerGenerator) { - if (null == requestMethods || requestMethods.length == 0) { - return; - } - - if (requestMethods.length > 1) { - throw new Error( - "not allowed multi http method for " + swaggerGenerator.getCls().getName()); - } - - swaggerGenerator.setHttpMethod(requestMethods[0].name()); - } - - private void processConsumes(String[] consumes, Swagger swagger) { - if (null == consumes || consumes.length == 0) { - return; - } - - swagger.setConsumes(Arrays.asList(consumes)); - } - - protected void processProduces(String[] produces, Swagger swagger) { - if (null == produces || produces.length == 0) { - return; - } - - swagger.setProduces(Arrays.asList(produces)); - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingMethodAnnotationProcessor.java deleted file mode 100644 index b930104ec96..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingMethodAnnotationProcessor.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc.processor.annotation; - -import java.util.Arrays; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -import io.servicecomb.swagger.generator.core.MethodAnnotationProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.swagger.models.Operation; - -public class RequestMappingMethodAnnotationProcessor implements MethodAnnotationProcessor { - - @Override - public void process(Object annotation, OperationGenerator operationGenerator) { - RequestMapping requestMapping = (RequestMapping) annotation; - Operation operation = operationGenerator.getOperation(); - - // path/value是等同的 - this.processPath(requestMapping.path(), operationGenerator); - this.processPath(requestMapping.value(), operationGenerator); - this.processMethod(requestMapping.method(), operationGenerator); - - this.processConsumes(requestMapping.consumes(), operation); - this.processProduces(requestMapping.produces(), operation); - - if (StringUtils.isEmpty(operationGenerator.getHttpMethod()) - && StringUtils.isEmpty(operationGenerator.getSwaggerGenerator().getHttpMethod())) { - throw new Error("HttpMethod must not both be empty in class and method"); - } - } - - protected void processPath(String[] paths, OperationGenerator operationGenerator) { - if (null == paths || paths.length == 0) { - return; - } - - // swagger仅支持配一个path,否则将会出现重复的operationId - if (paths.length > 1) { - throw new Error(String.format("not allowed multi path for %s:%s", - operationGenerator.getProviderMethod().getDeclaringClass().getName(), - operationGenerator.getProviderMethod().getName())); - } - - operationGenerator.setPath(paths[0]); - } - - protected void processMethod(RequestMethod[] requestMethods, OperationGenerator operationGenerator) { - if (null == requestMethods || requestMethods.length == 0) { - return; - } - - if (requestMethods.length > 1) { - throw new Error( - String.format("not allowed multi http method for %s:%s", - operationGenerator.getProviderMethod().getDeclaringClass().getName(), - operationGenerator.getProviderMethod().getName())); - } - - operationGenerator.setHttpMethod(requestMethods[0].name()); - } - - private void processConsumes(String[] consumes, Operation operation) { - if (null == consumes || consumes.length == 0) { - return; - } - - operation.setConsumes(Arrays.asList(consumes)); - } - - protected void processProduces(String[] produces, Operation operation) { - if (null == produces || produces.length == 0) { - return; - } - - operation.setProduces(Arrays.asList(produces)); - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestParamAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestParamAnnotationProcessor.java deleted file mode 100644 index b33bae866b8..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/annotation/RequestParamAnnotationProcessor.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc.processor.annotation; - -import org.springframework.web.bind.annotation.RequestParam; - -import io.servicecomb.swagger.generator.core.processor.parameter.AbstractParameterProcessor; -import io.swagger.models.parameters.QueryParameter; - -public class RequestParamAnnotationProcessor extends AbstractParameterProcessor { - @Override - protected QueryParameter createParameter() { - return new QueryParameter(); - } - - @Override - protected String getAnnotationParameterName(Object annotation) { - return ((RequestParam) annotation).name(); - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/parameter/SpringmvcDefaultParameterProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/parameter/SpringmvcDefaultParameterProcessor.java deleted file mode 100644 index 0af2000dec4..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/parameter/SpringmvcDefaultParameterProcessor.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc.processor.parameter; - -import io.servicecomb.swagger.generator.core.DefaultParameterProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.core.utils.ParamUtils; -import io.swagger.models.parameters.QueryParameter; - -public class SpringmvcDefaultParameterProcessor implements DefaultParameterProcessor { - - @Override - public void process(OperationGenerator operationGenerator, int paramIdx) { - String paramName = ParamUtils.getParameterName(operationGenerator.getProviderMethod(), paramIdx); - - QueryParameter queryParameter = new QueryParameter(); - queryParameter.setName(paramName); - ParamUtils.setParameterType(operationGenerator.getSwagger(), - operationGenerator.getProviderMethod(), - paramIdx, - queryParameter); - operationGenerator.addProviderParameter(queryParameter); - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/response/ResponseEntityProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/response/ResponseEntityProcessor.java deleted file mode 100644 index b2583f876c3..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/io/servicecomb/swagger/generator/springmvc/processor/response/ResponseEntityProcessor.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.springmvc.processor.response; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; - -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.generator.core.ResponseTypeProcessor; -import io.servicecomb.swagger.generator.core.utils.ParamUtils; -import io.swagger.converter.ModelConverters; -import io.swagger.models.properties.Property; - -public class ResponseEntityProcessor implements ResponseTypeProcessor { - - @Override - public Property process(OperationGenerator operationGenerator) { - ParameterizedType type = - (ParameterizedType) operationGenerator.getProviderMethod().getGenericReturnType(); - - Type responseType = type.getActualTypeArguments()[0]; - ParamUtils.addDefinitions(operationGenerator.getSwagger(), responseType); - return ModelConverters.getInstance().readAsProperty(responseType); - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/SpringmvcOperationGenerator.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/SpringmvcOperationGenerator.java new file mode 100644 index 00000000000..f9112f36676 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/SpringmvcOperationGenerator.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.swagger.generator.springmvc; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.core.AbstractSwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; +import org.apache.servicecomb.swagger.generator.rest.RestOperationGenerator; +import org.springframework.web.bind.annotation.RequestBody; + +public class SpringmvcOperationGenerator extends RestOperationGenerator { + public SpringmvcOperationGenerator(AbstractSwaggerGenerator swaggerGenerator, Method method) { + super(swaggerGenerator, method); + } + + @Override + protected void initMethodParameterGenerators(Map> methodAnnotationMap) { + super.initMethodParameterGenerators(methodAnnotationMap); + + parameterGenerators.stream() + .filter(pg -> pg.getHttpParameterType() == null) + .forEach(pg -> pg.setHttpParameterType(HttpParameterType.QUERY)); + } + + @Override + protected boolean isAggregatedParameter(ParameterGenerator parameterGenerator, Parameter methodParameter) { + return !isRequestBody(parameterGenerator) + && SwaggerUtils.isBean(methodParameter.getParameterizedType()); + } + + private boolean isRequestBody(ParameterGenerator parameterGenerator) { + for (Annotation annotation : parameterGenerator.getAnnotations()) { + if (annotation.annotationType() == RequestBody.class) { + return true; + } + } + return false; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGenerator.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGenerator.java new file mode 100644 index 00000000000..d6bfeac60c7 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGenerator.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.swagger.generator.springmvc; + +import java.lang.reflect.Method; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.rest.RestSwaggerGenerator; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +public class SpringmvcSwaggerGenerator extends RestSwaggerGenerator { + public SpringmvcSwaggerGenerator(Class cls) { + super(cls); + } + + @Override + protected boolean isSkipMethod(Method method) { + if (super.isSkipMethod(method)) { + return true; + } + + return method.getAnnotation(RequestMapping.class) == null && + method.getAnnotation(GetMapping.class) == null && + method.getAnnotation(PutMapping.class) == null && + method.getAnnotation(PostMapping.class) == null && + method.getAnnotation(PatchMapping.class) == null && + method.getAnnotation(DeleteMapping.class) == null; + } + + @SuppressWarnings("unchecked") + @Override + public T createOperationGenerator(Method method) { + return (T) new SpringmvcOperationGenerator(this, method); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGeneratorFactory.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGeneratorFactory.java new file mode 100644 index 00000000000..e85f10edb9f --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/SpringmvcSwaggerGeneratorFactory.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.swagger.generator.springmvc; + +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +public class SpringmvcSwaggerGeneratorFactory implements SwaggerGeneratorFactory { + private static final int ORDER = 1000; + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public boolean canProcess(Class cls) { + return SwaggerUtils.hasAnnotation(cls, RequestMapping.class) || SwaggerUtils + .hasAnnotation(cls, RestController.class); + } + + @Override + public SwaggerGenerator create(Class cls) { + return new SpringmvcSwaggerGenerator(cls); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/AbstractHttpMethodMappingAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/AbstractHttpMethodMappingAnnotationProcessor.java new file mode 100644 index 00000000000..8fe797bbba5 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/AbstractHttpMethodMappingAnnotationProcessor.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.swagger.generator.springmvc.processor.annotation; + +import java.util.Arrays; + +import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.springframework.web.bind.annotation.RequestMethod; + + +abstract class AbstractHttpMethodMappingAnnotationProcessor implements + MethodAnnotationProcessor { + protected void doProcess(OperationGenerator operationGenerator, String[] paths, String[] pathValues, + RequestMethod requestMethod, String[] consumes, String[] produces) { + // paths same to pathValues + this.processPath(operationGenerator, paths); + this.processPath(operationGenerator, pathValues); + + if (requestMethod != null) { + operationGenerator.setHttpMethod(requestMethod.name()); + } + if (consumes.length > 0) { + operationGenerator.getOperationGeneratorContext().updateConsumes(Arrays.asList(consumes)); + } + if (produces.length > 0) { + operationGenerator.getOperationGeneratorContext().updateProduces(Arrays.asList(produces)); + } + } + + protected void processPath(OperationGenerator operationGenerator, String[] paths) { + if (null == paths || paths.length == 0) { + return; + } + + // swagger仅支持配一个path,否则将会出现重复的operationId + if (paths.length > 1) { + throw new IllegalStateException("not allowed multi path."); + } + + operationGenerator.setPath(paths[0]); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/CookieValueAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/CookieValueAnnotationProcessor.java new file mode 100644 index 00000000000..d9b9df3f62a --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/CookieValueAnnotationProcessor.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.ValueConstants; + +public class CookieValueAnnotationProcessor extends + SpringmvcParameterAnnotationsProcessor { + @Override + public Type getProcessType() { + return CookieValue.class; + } + + @Override + public String getParameterName(CookieValue annotation) { + String value = annotation.value(); + if (value.isEmpty()) { + value = annotation.name(); + } + if (StringUtils.isNotEmpty(value)) { + return value; + } + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, CookieValue annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.COOKIE); + if (StringUtils.isNotEmpty(getParameterName(annotation))) { + parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); + } + parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); + if (!ValueConstants.DEFAULT_NONE.equals(annotation.defaultValue())) { + parameterGenerator.getParameterGeneratorContext() + .setDefaultValue(annotation.defaultValue()); + // if default value is set, must be required false. + parameterGenerator.getParameterGeneratorContext().setRequired(false); + } + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/DeleteMappingMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/DeleteMappingMethodAnnotationProcessor.java new file mode 100644 index 00000000000..994bf773e00 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/DeleteMappingMethodAnnotationProcessor.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.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +public class DeleteMappingMethodAnnotationProcessor extends + AbstractHttpMethodMappingAnnotationProcessor { + @Override + public Type getProcessType() { + return DeleteMapping.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + DeleteMapping deleteMapping) { + doProcess(operationGenerator, + deleteMapping.path(), deleteMapping.value(), + RequestMethod.DELETE, + deleteMapping.consumes(), deleteMapping.produces()); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/GetMappingMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/GetMappingMethodAnnotationProcessor.java new file mode 100644 index 00000000000..f140fc41d62 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/GetMappingMethodAnnotationProcessor.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.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +public class GetMappingMethodAnnotationProcessor extends AbstractHttpMethodMappingAnnotationProcessor { + @Override + public Type getProcessType() { + return GetMapping.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, GetMapping getMapping) { + doProcess(operationGenerator, + getMapping.path(), getMapping.value(), RequestMethod.GET, getMapping.consumes(), getMapping.produces()); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PatchMappingMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PatchMappingMethodAnnotationProcessor.java new file mode 100644 index 00000000000..41193fd8c48 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PatchMappingMethodAnnotationProcessor.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.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +public class PatchMappingMethodAnnotationProcessor extends AbstractHttpMethodMappingAnnotationProcessor { + @Override + public Type getProcessType() { + return PatchMapping.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + PatchMapping patchMapping) { + doProcess(operationGenerator, + patchMapping.path(), patchMapping.value(), + RequestMethod.PATCH, + patchMapping.consumes(), patchMapping.produces()); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PathVariableAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PathVariableAnnotationProcessor.java new file mode 100644 index 00000000000..ac89b8db84d --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PathVariableAnnotationProcessor.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.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; +import org.springframework.web.bind.annotation.PathVariable; + +public class PathVariableAnnotationProcessor extends + SpringmvcParameterAnnotationsProcessor { + @Override + public Type getProcessType() { + return PathVariable.class; + } + + @Override + public String getParameterName(PathVariable annotation) { + String value = annotation.value(); + if (value.isEmpty()) { + value = annotation.name(); + } + if (StringUtils.isNotEmpty(value)) { + return value; + } + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, PathVariable annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.PATH); + if (StringUtils.isNotEmpty(getParameterName(annotation))) { + parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); + } + parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PostMappingMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PostMappingMethodAnnotationProcessor.java new file mode 100644 index 00000000000..4ed80a7b8c5 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PostMappingMethodAnnotationProcessor.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.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +public class PostMappingMethodAnnotationProcessor extends AbstractHttpMethodMappingAnnotationProcessor { + @Override + public Type getProcessType() { + return PostMapping.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + PostMapping postMapping) { + doProcess(operationGenerator, + postMapping.path(), postMapping.value(), RequestMethod.POST, postMapping.consumes(), postMapping.produces()); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PutMappingMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PutMappingMethodAnnotationProcessor.java new file mode 100644 index 00000000000..e5672bfe303 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/PutMappingMethodAnnotationProcessor.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.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +public class PutMappingMethodAnnotationProcessor extends AbstractHttpMethodMappingAnnotationProcessor { + @Override + public Type getProcessType() { + return PutMapping.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, PutMapping putMapping) { + doProcess(operationGenerator, + putMapping.path(), putMapping.value(), RequestMethod.PUT, putMapping.consumes(), putMapping.produces()); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestAttributeAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestAttributeAnnotationProcessor.java new file mode 100644 index 00000000000..0b412afd984 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestAttributeAnnotationProcessor.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; +import org.springframework.web.bind.annotation.RequestAttribute; + +/** + * Use RequestAttribute to annotate a Form parameter. + * + * NOTICE: In spring-web, RequestAttribute is used to annotate request attribute, and use RequestParam + * to annotate query param and form param. This is implementation based. We can't use RequestParam to express + * both query and form in OpenAPI 3.0. And there is no request attribute. + */ +public class RequestAttributeAnnotationProcessor extends + SpringmvcParameterAnnotationsProcessor { + @Override + public Type getProcessType() { + return RequestAttribute.class; + } + + @Override + public String getParameterName(RequestAttribute annotation) { + String value = annotation.value(); + if (value.isEmpty()) { + value = annotation.name(); + } + if (StringUtils.isNotEmpty(value)) { + return value; + } + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, RequestAttribute annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.FORM); + if (StringUtils.isNotEmpty(getParameterName(annotation))) { + parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); + } + parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestBodyAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestBodyAnnotationProcessor.java new file mode 100644 index 00000000000..e7e755b3a5a --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestBodyAnnotationProcessor.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.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; +import org.springframework.web.bind.annotation.RequestBody; + +public class RequestBodyAnnotationProcessor extends + SpringmvcParameterAnnotationsProcessor { + @Override + public Type getProcessType() { + return RequestBody.class; + } + + @Override + public String getParameterName(RequestBody annotation) { + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, RequestBody annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.BODY); + parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestHeaderAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestHeaderAnnotationProcessor.java new file mode 100644 index 00000000000..dac8e2e0586 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestHeaderAnnotationProcessor.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.ValueConstants; + +public class RequestHeaderAnnotationProcessor extends + SpringmvcParameterAnnotationsProcessor { + @Override + public Type getProcessType() { + return RequestHeader.class; + } + + @Override + public String getParameterName(RequestHeader annotation) { + String value = annotation.value(); + if (value.isEmpty()) { + value = annotation.name(); + } + if (StringUtils.isNotEmpty(value)) { + return value; + } + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, RequestHeader annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.HEADER); + if (StringUtils.isNotEmpty(getParameterName(annotation))) { + parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); + } + parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); + if (!ValueConstants.DEFAULT_NONE.equals(annotation.defaultValue())) { + parameterGenerator.getParameterGeneratorContext() + .setDefaultValue(annotation.defaultValue()); + // if default value is set, must be required false. + parameterGenerator.getParameterGeneratorContext().setRequired(false); + } + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingClassAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingClassAnnotationProcessor.java new file mode 100644 index 00000000000..df18070f97a --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingClassAnnotationProcessor.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; +import java.util.Arrays; + +import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +public class RequestMappingClassAnnotationProcessor implements ClassAnnotationProcessor { + @Override + public Type getProcessType() { + return RequestMapping.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, RequestMapping requestMapping) { + this.processMethod(requestMapping.method(), swaggerGenerator); + + // path/value是等同的 + this.processPath(requestMapping.path(), swaggerGenerator); + this.processPath(requestMapping.value(), swaggerGenerator); + + if (requestMapping.consumes().length > 0) { + swaggerGenerator.getSwaggerGeneratorContext().updateConsumes(Arrays.asList(requestMapping.consumes())); + } + if (requestMapping.produces().length > 0) { + swaggerGenerator.getSwaggerGeneratorContext().updateProduces(Arrays.asList(requestMapping.produces())); + } + } + + protected void processPath(String[] paths, SwaggerGenerator swaggerGenerator) { + if (null == paths || paths.length == 0) { + return; + } + + // swagger仅支持配一个basePath + if (paths.length > 1) { + throw new IllegalStateException( + String.format("not support multi path, class=%s.", swaggerGenerator.getClazz().getName())); + } + + swaggerGenerator.setBasePath(paths[0]); + } + + protected void processMethod(RequestMethod[] requestMethods, SwaggerGenerator swaggerGenerator) { + if (null == requestMethods || requestMethods.length == 0) { + return; + } + + if (requestMethods.length > 1) { + throw new IllegalStateException( + String.format("not support multi http method, class=%s.", swaggerGenerator.getClazz().getName())); + } + + swaggerGenerator.setHttpMethod(requestMethods[0].name()); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingMethodAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingMethodAnnotationProcessor.java new file mode 100644 index 00000000000..b17505cac09 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestMappingMethodAnnotationProcessor.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.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +public class RequestMappingMethodAnnotationProcessor extends + AbstractHttpMethodMappingAnnotationProcessor { + @Override + public Type getProcessType() { + return RequestMapping.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + RequestMapping requestMapping) { + if (requestMapping.method().length > 1) { + throw new IllegalStateException("not allowed multi http method."); + } + + doProcess(operationGenerator, + requestMapping.path(), requestMapping.value(), + requestMapping.method().length == 0 ? + (StringUtils.isEmpty(operationGenerator.getHttpMethod()) ? RequestMethod.GET : null) : + requestMapping.method()[0], + requestMapping.consumes(), requestMapping.produces()); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestParamParameterProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestParamParameterProcessor.java new file mode 100644 index 00000000000..66785a337cf --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestParamParameterProcessor.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.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ValueConstants; + +/** + * Use RequestParam to annotate a Query parameter. + * + * NOTICE: In spring-web, RequestParam is used to annotate query param and form param. + * This is implementation based. We can't use RequestParam to express + * both query and form in OpenAPI 3.0. + */ +public class RequestParamParameterProcessor extends + SpringmvcParameterAnnotationsProcessor { + @Override + public Type getProcessType() { + return RequestParam.class; + } + + @Override + public String getParameterName(RequestParam annotation) { + String value = annotation.value(); + if (value.isEmpty()) { + value = annotation.name(); + } + if (StringUtils.isNotEmpty(value)) { + return value; + } + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, RequestParam annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.QUERY); + if (StringUtils.isNotEmpty(getParameterName(annotation))) { + parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); + } + parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); + if (!ValueConstants.DEFAULT_NONE.equals(annotation.defaultValue())) { + parameterGenerator.getParameterGeneratorContext() + .setDefaultValue(annotation.defaultValue()); + // if default value is set, must be required false. + parameterGenerator.getParameterGeneratorContext().setRequired(false); + } + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessor.java new file mode 100644 index 00000000000..4333201032e --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessor.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.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.ParameterGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; +import org.springframework.web.bind.annotation.RequestPart; + +public class RequestPartAnnotationProcessor extends + SpringmvcParameterAnnotationsProcessor { + @Override + public Type getProcessType() { + return RequestPart.class; + } + + @Override + public String getParameterName(RequestPart annotation) { + String value = annotation.value(); + if (value.isEmpty()) { + value = annotation.name(); + } + if (StringUtils.isNotEmpty(value)) { + return value; + } + return null; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + ParameterGenerator parameterGenerator, RequestPart annotation) { + parameterGenerator.setHttpParameterType(HttpParameterType.FORM); + if (StringUtils.isNotEmpty(getParameterName(annotation))) { + parameterGenerator.getParameterGeneratorContext().setParameterName(getParameterName(annotation)); + } + parameterGenerator.getParameterGeneratorContext().setRequired(annotation.required()); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RestControllerClassAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RestControllerClassAnnotationProcessor.java new file mode 100644 index 00000000000..66ac987a442 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RestControllerClassAnnotationProcessor.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.swagger.generator.springmvc.processor.annotation; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.springframework.web.bind.annotation.RestController; + +public class RestControllerClassAnnotationProcessor implements ClassAnnotationProcessor { + @Override + public Type getProcessType() { + return RestController.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, RestController restController) { + if (SwaggerUtils.getBasePath(swaggerGenerator.getOpenAPI()) == null) { + swaggerGenerator.setBasePath("/"); + } + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/SpringmvcParameterAnnotationsProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/SpringmvcParameterAnnotationsProcessor.java new file mode 100644 index 00000000000..4774d5c6256 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/SpringmvcParameterAnnotationsProcessor.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.swagger.generator.springmvc.processor.annotation; + +import org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor; + +/** + * A generic class to help classify implementations. + */ +public abstract class SpringmvcParameterAnnotationsProcessor implements + ParameterAnnotationProcessor { +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileArrayProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileArrayProcessor.java new file mode 100644 index 00000000000..be51858656d --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileArrayProcessor.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.swagger.generator.springmvc.processor.parameter; + +import org.apache.servicecomb.swagger.generator.core.processor.parameter.PartArrayParameterTypeProcessor; +import org.springframework.web.multipart.MultipartFile; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +public class MultipartFileArrayProcessor extends PartArrayParameterTypeProcessor { + @Override + public JavaType getProcessType() { + return TypeFactory.defaultInstance().constructType(MultipartFile[].class); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileListProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileListProcessor.java new file mode 100644 index 00000000000..85d4648c126 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileListProcessor.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.swagger.generator.springmvc.processor.parameter; + +import java.util.List; + +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; +import org.apache.servicecomb.swagger.generator.core.processor.parameter.PartArrayParameterTypeProcessor; +import org.springframework.web.multipart.MultipartFile; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +public class MultipartFileListProcessor extends PartArrayParameterTypeProcessor { + @Override + public JavaType getProcessType() { + return TypeFactory.defaultInstance().constructType( + ParameterizedTypeUtil.make(List.class, MultipartFile.class)); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileTypeProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileTypeProcessor.java new file mode 100644 index 00000000000..3781c2b128f --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileTypeProcessor.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.swagger.generator.springmvc.processor.parameter; + +import org.apache.servicecomb.swagger.generator.core.processor.parameter.PartParameterTypeProcessor; +import org.springframework.web.multipart.MultipartFile; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.TypeFactory; + +public class MultipartFileTypeProcessor extends PartParameterTypeProcessor { + @Override + public JavaType getProcessType() { + return TypeFactory.defaultInstance().constructType(MultipartFile.class); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/response/ResponseEntityProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/response/ResponseEntityProcessor.java new file mode 100644 index 00000000000..e6bed78ef83 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/response/ResponseEntityProcessor.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.swagger.generator.springmvc.processor.response; + +import org.apache.servicecomb.swagger.generator.core.processor.response.DefaultResponseTypeProcessor; +import org.springframework.http.ResponseEntity; + +public class ResponseEntityProcessor extends DefaultResponseTypeProcessor { + public ResponseEntityProcessor() { + extractActualType = true; + } + + @Override + public Class getProcessType() { + return ResponseEntity.class; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreator.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreator.java new file mode 100644 index 00000000000..6666f7813c5 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreator.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.swagger.generator.springmvc.property.creator; + +import org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator; +import org.springframework.web.multipart.MultipartFile; + +import io.swagger.v3.oas.models.media.FileSchema; +import io.swagger.v3.oas.models.media.Schema; + +public class MultipartFilePropertyCreator implements PropertyCreator { + private final Class[] classes = {MultipartFile.class}; + + @Override + public Schema createProperty() { + return new FileSchema(); + } + + @Override + public Class[] classes() { + return classes; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.SwaggerGeneratorContext b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.SwaggerGeneratorContext deleted file mode 100644 index 7c7d6860a0b..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.SwaggerGeneratorContext +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -io.servicecomb.swagger.generator.springmvc.SpringmvcSwaggerGeneratorContext \ No newline at end of file diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator new file mode 100644 index 00000000000..11b81b52e9b --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator @@ -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.swagger.generator.springmvc.property.creator.MultipartFilePropertyCreator diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor new file mode 100644 index 00000000000..e7d511d2796 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor @@ -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.swagger.generator.springmvc.processor.annotation.RestControllerClassAnnotationProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestMappingClassAnnotationProcessor diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor new file mode 100644 index 00000000000..21a0a455679 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor @@ -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. +# + +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestMappingMethodAnnotationProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.GetMappingMethodAnnotationProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.DeleteMappingMethodAnnotationProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.PostMappingMethodAnnotationProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.PutMappingMethodAnnotationProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.PatchMappingMethodAnnotationProcessor diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor new file mode 100644 index 00000000000..a1deb93045f --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor @@ -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. +# + +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.CookieValueAnnotationProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.PathVariableAnnotationProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestBodyAnnotationProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestParamParameterProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestHeaderAnnotationProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestPartAnnotationProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestAttributeAnnotationProcessor diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterTypeProcessor b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterTypeProcessor new file mode 100644 index 00000000000..c99b5978635 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterTypeProcessor @@ -0,0 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.apache.servicecomb.swagger.generator.springmvc.processor.parameter.MultipartFileTypeProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.parameter.MultipartFileArrayProcessor +org.apache.servicecomb.swagger.generator.springmvc.processor.parameter.MultipartFileListProcessor diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor new file mode 100644 index 00000000000..91110b15c86 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor @@ -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.swagger.generator.springmvc.processor.response.ResponseEntityProcessor diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory new file mode 100644 index 00000000000..88f6b99eb19 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory @@ -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.swagger.generator.springmvc.SpringmvcSwaggerGeneratorFactory diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/ClassMethodNoHttpMethod.java b/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/ClassMethodNoHttpMethod.java deleted file mode 100644 index 9c8daea8f67..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/ClassMethodNoHttpMethod.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc; - -import org.springframework.web.bind.annotation.RequestMapping; - -@RequestMapping(path = "c") -public class ClassMethodNoHttpMethod { - @RequestMapping(path = "m") - public void noHttpMethod() { - - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/ClassMethodNoPath.java b/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/ClassMethodNoPath.java deleted file mode 100644 index 58fbe107094..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/ClassMethodNoPath.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -@RequestMapping -public class ClassMethodNoPath { - @RequestMapping(method = RequestMethod.GET) - public void noPath() { - - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/ClassMultiHttpMethod.java b/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/ClassMultiHttpMethod.java deleted file mode 100644 index 1b857a9ad2b..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/ClassMultiHttpMethod.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -@RequestMapping(path = "Echo", method = {RequestMethod.GET, RequestMethod.POST}) -public class ClassMultiHttpMethod { - -} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/ClassMultiPath.java b/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/ClassMultiPath.java deleted file mode 100644 index fe0054980c5..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/ClassMultiPath.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc; - -import org.springframework.web.bind.annotation.RequestMapping; - -@RequestMapping(path = {"a", "b"}) -public class ClassMultiPath { - -} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/Echo.java b/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/Echo.java deleted file mode 100644 index 73d0b5af217..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/Echo.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc; - -import java.util.List; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestAttribute; -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; - -@RequestMapping( - path = "Echo", - method = {RequestMethod.PUT}, - consumes = {"a", "b"}, - produces = {"a", "b"}) -public class Echo { - @RequestMapping - public ResponseEntity> testResponseEntity() { - return null; - } - - @RequestMapping - public void emptyPath() { - - } - - @RequestMapping( - path = "echo/{targetName}", - method = {RequestMethod.POST}, - consumes = {"text/plain", "application/*"}, - produces = {"text/plain", "application/*"}) - public String echo(@RequestBody User srcUser, @RequestHeader String header, @PathVariable String targetName, - @RequestParam(name = "word") String word, @RequestAttribute String form) { - return String.format("%s %s %s %s %s", srcUser.name, header, targetName, word, form); - } - - @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}) - public void multiHttpMethod() { - } - - @RequestMapping(path = "query") - public void defaultParam(int query) { - } - - @RequestMapping(path = {"a", "b"}) - public void multiPath(int query) { - } - - @RequestMapping - public void inheritHttpMethod(int query) { - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/MultiDefaultPath.java b/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/MultiDefaultPath.java deleted file mode 100644 index b290e9e8d95..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/MultiDefaultPath.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.generator.springmvc; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -@RequestMapping(path = "abc") -public class MultiDefaultPath { - @RequestMapping(method = RequestMethod.GET) - public void p1() { - - } - - @RequestMapping(method = RequestMethod.GET) - public void p2() { - - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/TestSpringmvc.java b/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/TestSpringmvc.java deleted file mode 100644 index a9120cbd65b..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/TestSpringmvc.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.swagger.generator.core.CompositeSwaggerGeneratorContext; -import io.servicecomb.swagger.generator.core.SwaggerGeneratorContext; -import io.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; - -public class TestSpringmvc { - SwaggerGeneratorContext context = new SpringmvcSwaggerGeneratorContext(); - - @Test - public void testMultiDefaultPath() { - UnitTestSwaggerUtils.testException( - "Only allowed one default path. io.servicecomb.swagger.generator.springmvc.MultiDefaultPath:p2", - context, - MultiDefaultPath.class); - } - - @Test - public void testResponseEntity() throws Exception { - UnitTestSwaggerUtils.testSwagger("schemas/responseEntity.yaml", context, Echo.class, "testResponseEntity"); - } - - @Test - public void testEmptyPath() throws Exception { - UnitTestSwaggerUtils.testSwagger("schemas/emptyPath.yaml", context, Echo.class, "emptyPath"); - } - - @Test - public void testEcho() throws Exception { - UnitTestSwaggerUtils.testSwagger("schemas/echo.yaml", context, Echo.class, "echo"); - } - - @Test - public void testDefaultParam() throws Exception { - UnitTestSwaggerUtils.testSwagger("schemas/defaultParam.yaml", context, Echo.class, "defaultParam"); - } - - @Test - public void testInheritHttpMethod() throws Exception { - UnitTestSwaggerUtils.testSwagger("schemas/inheritHttpMethod.yaml", context, Echo.class, "inheritHttpMethod"); - } - - @Test - public void testClassMethodNoPath() throws Exception { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.springmvc.ClassMethodNoPath:noPath", - "Path must not both be empty in class and method", - context, - ClassMethodNoPath.class); - } - - @Test - public void testClassMethodNoHttpMetod() throws Exception { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.springmvc.ClassMethodNoHttpMethod:noHttpMethod", - "HttpMethod must not both be empty in class and method", - context, - ClassMethodNoHttpMethod.class); - } - - @Test - public void testMethodMultiHttpMethod() throws Exception { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.springmvc.Echo:multiHttpMethod", - "not allowed multi http method for io.servicecomb.swagger.generator.springmvc.Echo:multiHttpMethod", - context, - Echo.class, - "multiHttpMethod"); - } - - @Test - public void testClassMultiHttpMethod() throws Exception { - UnitTestSwaggerUtils.testException( - "not allowed multi http method for io.servicecomb.swagger.generator.springmvc.ClassMultiHttpMethod", - context, - ClassMultiHttpMethod.class); - } - - @Test - public void testMethodMultiPath() throws Exception { - UnitTestSwaggerUtils.testException( - "generate operation swagger failed, io.servicecomb.swagger.generator.springmvc.Echo:multiPath", - "not allowed multi path for io.servicecomb.swagger.generator.springmvc.Echo:multiPath", - context, - Echo.class, - "multiPath"); - } - - @Test - public void testClassMultiPath() throws Exception { - UnitTestSwaggerUtils.testException( - "not support multi path for io.servicecomb.swagger.generator.springmvc.ClassMultiPath", - context, - ClassMultiPath.class); - } - - @Test - public void testComposite() { - CompositeSwaggerGeneratorContext composite = new CompositeSwaggerGeneratorContext(); - SwaggerGeneratorContext context = composite.selectContext(Echo.class); - - Assert.assertEquals(SpringmvcSwaggerGeneratorContext.class, context.getClass()); - } -} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/User.java b/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/User.java deleted file mode 100644 index 22cf824b29c..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/test/java/io/servicecomb/swagger/generator/springmvc/User.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.generator.springmvc; - -public class User { - public String name; -} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMethodNoHttpMethod.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMethodNoHttpMethod.java new file mode 100644 index 00000000000..554224b9e5c --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMethodNoHttpMethod.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.swagger.generator.springmvc; + +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping(path = "c") +public class ClassMethodNoHttpMethod { + @RequestMapping(path = "m") + public void noHttpMethod() { + + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMethodNoPath.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMethodNoPath.java new file mode 100644 index 00000000000..5199854d22c --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMethodNoPath.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.swagger.generator.springmvc; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@RequestMapping +public class ClassMethodNoPath { + @RequestMapping(method = RequestMethod.GET) + public void noPath() { + + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMultiHttpMethod.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMultiHttpMethod.java new file mode 100644 index 00000000000..ee6c73e36ab --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMultiHttpMethod.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.swagger.generator.springmvc; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@RequestMapping(path = "Echo", method = {RequestMethod.GET, RequestMethod.POST}) +public class ClassMultiHttpMethod { + +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMultiPath.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMultiPath.java new file mode 100644 index 00000000000..5a76b50a4df --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/ClassMultiPath.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.springmvc; + +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping(path = {"a", "b"}) +public class ClassMultiPath { + +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/Echo.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/Echo.java new file mode 100644 index 00000000000..48ac1256c53 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/Echo.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.springmvc; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; +import org.apache.servicecomb.swagger.extend.annotations.RawJsonRequestBody; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CookieValue; +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.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.ws.rs.core.MediaType; + +@RequestMapping( + path = "Echo", + method = {RequestMethod.PUT}, + consumes = {MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}, + produces = {MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) +public class Echo { + + @RequestMapping("emptyPath") + public void emptyPath() { + } + + @RequestMapping("inheritHttpMethod") + public void inheritHttpMethod(int query) { + } + + @RequestMapping("cookie") + public void cookie(@CookieValue(value = "cookie", required = false) int cookie) { + } + + @RequestMapping("rawJsonStringMethod") + public void rawJsonStringMethod(@RawJsonRequestBody String jsonInput) { + } + + @RequestMapping("enumBody") + public void enumBody(@RequestBody Color color) { + } + + @RequestMapping("asyncResponseEntity") + public CompletableFuture>> asyncResponseEntity() { + return null; + } + + @RequestMapping("testResponseEntityOptional") + public ResponseEntity> testResponseEntityOptional() { + return null; + } + + @RequestMapping("testCompletableFutureResponseEntityOptional") + public CompletableFuture>> testCompletableFutureResponseEntityOptional() { + return null; + } + + @RequestMapping(value = "part", consumes = MediaType.MULTIPART_FORM_DATA) + public void part(@RequestPart MultipartFile part) { + + } + + @RequestMapping(value = "partArray", consumes = MediaType.MULTIPART_FORM_DATA) + public void partArray(@RequestPart MultipartFile[] part) { + + } + + @RequestMapping(value = "partList", consumes = MediaType.MULTIPART_FORM_DATA) + public void partList(@RequestPart List part) { + + } + + @RequestMapping(value = "partAnnotation", consumes = MediaType.MULTIPART_FORM_DATA) + public void partAnnotation(@RequestPart MultipartFile part) { + + } + + @RequestMapping(value = "partArrayAnnotation", consumes = MediaType.MULTIPART_FORM_DATA) + public void partArrayAnnotation(@RequestPart MultipartFile[] part) { + + } + + @RequestMapping(value = "partListAnnotation", consumes = MediaType.MULTIPART_FORM_DATA) + public void partListAnnotation(@RequestPart List part) { + + } + + @RequestMapping("nestedListString") + public List> nestedListString(@RequestBody List> param) { + return param; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodDefaultParameter.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodDefaultParameter.java new file mode 100644 index 00000000000..d023d7c991d --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodDefaultParameter.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.swagger.generator.springmvc; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@RequestMapping(path = "MethodDefaultParameter") +public class MethodDefaultParameter { + + @RequestMapping(method = RequestMethod.PUT, path = "usingRequestMapping") + public void usingRequestMapping(int query) { + } + + @GetMapping(path = "usingGetMapping") + public void usingGetMapping(int query) { + } + + @PutMapping(path = "usingPutMapping") + public void usingPutMapping(int query) { + } + + @PostMapping(path = "usingPostMapping") + public void usingPostMapping(int query) { + } + + @PatchMapping(path = "usingPatchMapping") + public void usingPatchMapping(int query) { + } + + @DeleteMapping(path = "usingDeleteMapping") + public void usingDeleteMapping(int query) { + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodEmptyPath.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodEmptyPath.java new file mode 100644 index 00000000000..59b62412647 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodEmptyPath.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.swagger.generator.springmvc; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.swagger.v3.oas.annotations.Operation; + +@RequestMapping(path = "MethodEmptyPath") +public class MethodEmptyPath { + + @GetMapping + public void usingGetMapping() { + + } + + @PostMapping + public void usingPostMapping() { + + } + + @PutMapping + public void usingPutMapping() { + + } + + @DeleteMapping + public void usingDeleteMapping() { + + } + + @PatchMapping + public void usingPatchMapping() { + + } + + // this will be ignored in the generation of service contract + // as ApiOperation is not a restful annotation + @Operation(summary = "") + public void ignoredNonRestful() { + + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodMixupAnnotations.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodMixupAnnotations.java new file mode 100644 index 00000000000..5d93a2862f3 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodMixupAnnotations.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.springmvc; + +import java.time.LocalDateTime; + +import org.apache.servicecomb.foundation.test.scaffolding.model.User; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +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 org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +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.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.Part; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; + +@SuppressWarnings("unused") +@RequestMapping(path = "MethodMixupAnnotations") +public class MethodMixupAnnotations { + @RequestMapping( + path = "usingRequestMapping/{targetName}", + method = {RequestMethod.POST}, + consumes = {"text/plain", "application/json"}, + produces = {"text/plain", "application/json"}) + public String usingRequestMapping(@RequestBody User srcUser, @RequestHeader String header, + @PathVariable String targetName, @RequestParam(name = "word") String word) { + return String.format("%s %s %s %s", srcUser.name, header, targetName, word); + } + + @GetMapping( + path = "usingGetMapping/{targetName}", + consumes = {"text/plain", "application/json"}, + produces = {"text/plain", "application/json"}) + public String usingGetMapping(@RequestBody User srcUser, @RequestHeader String header, + @PathVariable String targetName, @RequestParam(name = "word") String word) { + return String.format("%s %s %s %s", srcUser.name, header, targetName, word); + } + + @PutMapping( + path = "usingPutMapping/{targetName}", + consumes = {"text/plain", "application/json"}, + produces = {"text/plain", "application/json"}) + public String usingPutMapping(@RequestBody User srcUser, @RequestHeader String header, + @PathVariable String targetName, @RequestParam(name = "word") String word) { + return String.format("%s %s %s %s", srcUser.name, header, targetName, word); + } + + @PostMapping( + path = "usingPostMapping/{targetName}", + consumes = {"text/plain", "application/json"}, + produces = {"text/plain", "application/json"}) + public String usingPostMapping(@RequestBody User srcUser, @RequestHeader String header, + @PathVariable String targetName, @RequestParam(name = "word") String word) { + return String.format("%s %s %s %s", srcUser.name, header, targetName, word); + } + + @PatchMapping( + path = "usingPatchMapping/{targetName}", + consumes = {"text/plain", "application/json"}, + produces = {"text/plain", "application/json"}) + public String usingPatchMapping(@RequestBody User srcUser, @RequestHeader String header, + @PathVariable String targetName, @RequestParam(name = "word") String word) { + return String.format("%s %s %s %s", srcUser.name, header, targetName, word); + } + + @DeleteMapping( + path = "usingDeleteMapping/{targetName}", + consumes = {"text/plain", "application/json"}, + produces = {"text/plain", "application/json"}) + public String usingDeleteMapping(@RequestBody User srcUser, @RequestHeader String header, + @PathVariable String targetName, @RequestParam(name = "word") String word) { + return String.format("%s %s %s %s", srcUser.name, header, targetName, word); + } + + @PostMapping(path = "/uploadFileAndAttribute") + public String uploadFileAndAttribute(@RequestPart(name = "file") MultipartFile file, + @RequestPart(name = "attribute") String attribute) { + return null; + } + + @PostMapping(path = "/uploadFilesAndAttribute") + public String uploadFilesAndAttribute(@RequestPart(name = "files") MultipartFile[] files, + @RequestPart(name = "attribute") String attribute) { + return null; + } + + @GetMapping(path = "/reduce") + @Parameters({@Parameter(name = "a", schema = @Schema(implementation = String.class), in = ParameterIn.QUERY)}) + public int reduce(HttpServletRequest request, @CookieValue(name = "b") int b) { + return 0; + } + + @RequestMapping(path = "/defaultQueryParam", method = RequestMethod.POST) + public String defaultQueryParam(String prefix, @RequestBody User user) { + return null; + } + + @GetMapping(path = "/diffNames") + @Operation(summary = "differentName", operationId = "differentName") + public int diffNames(@RequestParam("x") int a, @RequestParam("y") int b) { + return a * 2 + b; + } + + @GetMapping(path = "/bytes") + public byte[] bytes(@RequestBody byte[] value) { + return null; + } + + @PostMapping(path = "/upload", produces = MediaType.TEXT_PLAIN_VALUE) + public String fileUpload(@RequestPart(name = "file1") MultipartFile file1, + @RequestPart(name = "someFile") Part file2) { + return null; + } + + @PostMapping(path = "/testImplicitForm") + @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 testImplicitForm(HttpServletRequest request) { + return null; + } + + @GetMapping("/testDefaultValue") + public String testDefaultValue(@RequestParam(name = "e", required = false) int e, + @RequestHeader(name = "a", defaultValue = "20") int a, + @CookieValue(name = "b", defaultValue = "bobo") String b, + @RequestParam(name = "c", defaultValue = "40") Integer c, + @Min(value = 20) @Max(value = 30) @RequestParam(name = "d", required = false) int d) { + return "Hello " + a + b + c + d + e; + } + + @GetMapping(path = "/testLocalDateTime") + public LocalDateTime testLocalDateTime(@RequestParam("date") LocalDateTime date) { + return date; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodMultiPath.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodMultiPath.java new file mode 100644 index 00000000000..b1f8622748f --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodMultiPath.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.swagger.generator.springmvc; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@RequestMapping +public class MethodMultiPath { + @RequestMapping(method = RequestMethod.GET, path = {"a", "b"}) + public void usingRequestMapping() { + } + + @GetMapping(path = {"a", "b"}) + public void usingGetMapping() { + + } + + @PutMapping(path = {"a", "b"}) + public void usingPutMapping() { + + } + + @PostMapping(path = {"a", "b"}) + public void usingPostMapping() { + + } + + @PatchMapping(path = {"a", "b"}) + public void usingPatchMapping() { + + } + + @DeleteMapping(path = {"a", "b"}) + public void usingDeleteMapping() { + + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodResponseEntity.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodResponseEntity.java new file mode 100644 index 00000000000..93b02db5a3f --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MethodResponseEntity.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.springmvc; + +import java.util.List; + +import org.apache.servicecomb.foundation.test.scaffolding.model.User; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@RequestMapping(path = "MethodResponseEntity", method = RequestMethod.POST) +public class MethodResponseEntity { + + @RequestMapping(method = RequestMethod.PUT, path = "usingRequestMapping") + public ResponseEntity> usingRequestMapping() { + return null; + } + + @GetMapping(path = "usingGetMapping") + public ResponseEntity> usingGetMapping() { + return null; + } + + @PutMapping(path = "usingPutMapping") + public ResponseEntity> usingPutMapping() { + return null; + } + + @PostMapping(path = "usingPostMapping") + public ResponseEntity> usingPostMapping() { + return null; + } + + @PatchMapping(path = "usingPatchMapping") + public ResponseEntity> usingPatchMapping() { + return null; + } + + @DeleteMapping(path = "usingDeleteMapping") + public ResponseEntity> usingDeleteMapping() { + return null; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MultiDefaultPath.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MultiDefaultPath.java new file mode 100644 index 00000000000..dc84054c7e7 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/MultiDefaultPath.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.swagger.generator.springmvc; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@RequestMapping(path = "abc", method = RequestMethod.POST) +public class MultiDefaultPath { + @GetMapping + public void p1() { + + } + + @RequestMapping(method = RequestMethod.GET) + public void p2() { + + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestSpringmvc.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestSpringmvc.java new file mode 100644 index 00000000000..a0bb9b996c7 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestSpringmvc.java @@ -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. + */ + +package org.apache.servicecomb.swagger.generator.springmvc; + +import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; +import org.apache.servicecomb.swagger.generator.springmvc.model.DefaultParameterSchema; +import org.apache.servicecomb.swagger.generator.springmvc.model.RestControllerWithPathSchema; +import org.apache.servicecomb.swagger.generator.springmvc.model.SwaggerTestTarget; +import org.apache.servicecomb.swagger.generator.springmvc.model.SwaggerTestTarget_ValueOverWritePath; +import org.junit.jupiter.api.Test; + +public class TestSpringmvc { + @Test + public void testMultiDefaultPath() { + UnitTestSwaggerUtils.testException( + "Duplicate operation path detected. method=org.apache.servicecomb.swagger.generator.springmvc.MultiDefaultPath:p2.", + MultiDefaultPath.class); + } + + @Test + public void testResponseEntity() { + UnitTestSwaggerUtils.testSwagger("schemas/responseEntity.yaml", MethodResponseEntity.class); + } + + @Test + public void testEcho() { + UnitTestSwaggerUtils.testSwagger("schemas/echo.yaml", Echo.class); + } + + @Test + public void testMixupAnnotations() { + UnitTestSwaggerUtils.testSwagger("schemas/mixupAnnotations.yaml", MethodMixupAnnotations.class); + } + + @Test + public void testDefaultParameter() { + UnitTestSwaggerUtils.testSwagger("schemas/defaultParameter.yaml", MethodDefaultParameter.class); + } + + + @Test + public void testClassMethodNoPath() { + UnitTestSwaggerUtils.testException( + "Generate swagger operation failed, method=ClassMethodNoPath:noPath, " + + "cause=Path must not both be empty in class and method", + ClassMethodNoPath.class, + "noPath"); + } + + @Test + public void testClassMethodNoHttpMethod() { + UnitTestSwaggerUtils + .testSwagger("schemas/requestMappingHttpMethod.yaml", ClassMethodNoHttpMethod.class, "noHttpMethod"); + } + + + @Test + public void testClassMultiHttpMethod() { + UnitTestSwaggerUtils.testException( + "not support multi http method, class=org.apache.servicecomb.swagger.generator.springmvc.ClassMultiHttpMethod.", + ClassMultiHttpMethod.class); + } + + @Test + public void testMethodMultiPathUsingRequestMapping() { + UnitTestSwaggerUtils.testException( + "Generate swagger operation failed, method=MethodMultiPath:usingRequestMapping, cause=not allowed multi path.", + MethodMultiPath.class, + "usingRequestMapping"); + } + + @Test + public void testMethodMultiPathUsingGetMapping() { + UnitTestSwaggerUtils.testException( + "Generate swagger operation failed, method=MethodMultiPath:usingGetMapping, cause=not allowed multi path.", + MethodMultiPath.class, + "usingGetMapping"); + } + + @Test + public void testMethodMultiPathUsingPutMapping() { + UnitTestSwaggerUtils.testException( + "Generate swagger operation failed, method=MethodMultiPath:usingPutMapping, cause=not allowed multi path.", + MethodMultiPath.class, + "usingPutMapping"); + } + + @Test + public void testMethodMultiPathUsingPostMapping() { + UnitTestSwaggerUtils.testException( + "Generate swagger operation failed, method=MethodMultiPath:usingPostMapping, cause=not allowed multi path.", + MethodMultiPath.class, + "usingPostMapping"); + } + + @Test + public void testMethodMultiPathUsingPatchMapping() { + UnitTestSwaggerUtils.testException( + "Generate swagger operation failed, method=MethodMultiPath:usingPatchMapping, cause=not allowed multi path.", + MethodMultiPath.class, + "usingPatchMapping"); + } + + @Test + public void testMethodMultiPathUsingDeleteMapping() { + UnitTestSwaggerUtils.testException( + "Generate swagger operation failed, method=MethodMultiPath:usingDeleteMapping, cause=not allowed multi path.", + MethodMultiPath.class, + "usingDeleteMapping"); + } + + @Test + public void testClassMultiPath() { + UnitTestSwaggerUtils.testException( + "not support multi path, class=org.apache.servicecomb.swagger.generator.springmvc.ClassMultiPath.", + ClassMultiPath.class); + } + + + @Test + public void testDefaultParameterSchema() { + UnitTestSwaggerUtils.testSwagger("schemas/DefaultParameterSchema.yaml", DefaultParameterSchema.class); + } + + @Test + public void testRestControllerWithPathSchema() { + UnitTestSwaggerUtils.testSwagger("schemas/RestControllerWithPathSchema.yaml", RestControllerWithPathSchema.class); + } + + @Test + public void swaggerTestTarget() { + UnitTestSwaggerUtils.testSwagger("schemas/swaggerTestTarget.yaml", SwaggerTestTarget.class); + } + + @Test + public void swaggerTestTarget_ValueOverWritePath() { + UnitTestSwaggerUtils + .testSwagger("schemas/swaggerTestTarget_ValueOverWritePath.yaml", SwaggerTestTarget_ValueOverWritePath.class); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestTwoSameNameModels.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestTwoSameNameModels.java new file mode 100644 index 00000000000..5bcb3b3fda6 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestTwoSameNameModels.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.swagger.generator.springmvc; + +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.springmvc.model.Generic; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +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.models.OpenAPI; +import jakarta.ws.rs.core.MediaType; + +public class TestTwoSameNameModels { + private static final String SCHEMA_CONTENT = """ + openapi: 3.0.1 + info: + title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.TestTwoSameNameModels$TwoSameModelService + version: 1.0.0 + servers: + - url: /TwoSameModelService + paths: + /sameGeneric: + post: + operationId: genericService + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/GenericSameModel" + required: true + x-name: param + responses: + "200": + description: response of 200 + content: + application/json: + schema: + $ref: "#/components/schemas/GenericSameModel" + /same: + post: + operationId: service + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/SameModel" + required: true + x-name: param + responses: + "200": + description: response of 200 + content: + application/json: + schema: + $ref: "#/components/schemas/SameModel" + components: + schemas: + GenericSameModel: + type: object + properties: + data: + $ref: "#/components/schemas/SameModel" + x-java-class: org.apache.servicecomb.swagger.generator.springmvc.model.Generic + SameModel: + type: object + properties: + name: + type: string + x-java-class: org.apache.servicecomb.swagger.generator.springmvc.model.same1.SameModel + """; + + interface TwoSameModelService { + @RequestMapping( + path = "/same", + method = {RequestMethod.POST}, + consumes = {MediaType.APPLICATION_JSON}, + produces = {MediaType.APPLICATION_JSON}) + org.apache.servicecomb.swagger.generator.springmvc.model.same1.SameModel service + (@RequestBody org.apache.servicecomb.swagger.generator.springmvc.model.same1.SameModel param); + + @RequestMapping( + path = "/sameGeneric", + method = {RequestMethod.POST}, + consumes = {MediaType.APPLICATION_JSON}, + produces = {MediaType.APPLICATION_JSON}) + Generic genericService + (@RequestBody Generic param); + } + + interface SameModelService { + @RequestMapping( + path = "/same", + method = {RequestMethod.POST}, + consumes = {MediaType.APPLICATION_JSON}, + produces = {MediaType.APPLICATION_JSON}) + org.apache.servicecomb.swagger.generator.springmvc.model.same1.SameModel service + (@RequestBody org.apache.servicecomb.swagger.generator.springmvc.model.same2.SameModel param); + } + + interface SameModelThrowService { + @RequestMapping( + path = "/same", + method = {RequestMethod.POST}, + consumes = {MediaType.APPLICATION_JSON}, + produces = {MediaType.APPLICATION_JSON}) + org.apache.servicecomb.swagger.generator.springmvc.model.same1.SameModelThrow service + (@RequestBody org.apache.servicecomb.swagger.generator.springmvc.model.same2.SameModelThrow param); + } + + @Test + public void testTwoSameModelWork() { + SwaggerGenerator generator = SwaggerGenerator.create(TwoSameModelService.class); + OpenAPI openAPI = generator.generate(); + Assertions.assertEquals(SCHEMA_CONTENT, SwaggerUtils.swaggerToString(openAPI)); + } + + @Test + public void testSameModelThrowThrowException() { + SwaggerGenerator generator = SwaggerGenerator.create(SameModelThrowService.class); + Assertions.assertThrows(IllegalStateException.class, () -> generator.generate()); + } + + @Test + public void testSameModelThrowException() { + SwaggerGenerator generator = SwaggerGenerator.create(SameModelService.class); + Assertions.assertThrows(IllegalStateException.class, () -> generator.generate()); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/DefaultParameterSchema.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/DefaultParameterSchema.java new file mode 100644 index 00000000000..9a2bae4d569 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/DefaultParameterSchema.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.swagger.generator.springmvc.model; + +import java.util.List; +import java.util.Map; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.ws.rs.core.MediaType; + +@RestController +public class DefaultParameterSchema { + @GetMapping("/testSimpleParam") + public String testSimpleParam(String strParam) { + return strParam; + } + + @GetMapping("/testObjectParam") + public String testObjectParam(TestParam objParam) { + return objParam.toString(); + } + + @GetMapping("/testUnsupportedParamType") + public String testUnsupportedParamType(int i, List integerList, Map stringMap) { + return null; + } + + @RequestMapping(path = "testSingleMediaType", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON) + public String testSingleMediaType(String input) { + return input; + } + + @RequestMapping(path = "testMultipleMediaType", method = RequestMethod.PUT, consumes = {MediaType.TEXT_PLAIN, + MediaType.APPLICATION_JSON}, + produces = {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON}) + public String testMultipleMediaType(String input) { + return input; + } + + @RequestMapping(path = "testBlankMediaType", method = RequestMethod.POST) + public String testBlankMediaType(String input) { + return input; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/Generic.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/Generic.java new file mode 100644 index 00000000000..6c419dd39f9 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/Generic.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.swagger.generator.springmvc.model; + +public class Generic { + private T data; + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/RestControllerWithPathSchema.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/RestControllerWithPathSchema.java new file mode 100644 index 00000000000..c6553ba8c15 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/RestControllerWithPathSchema.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.swagger.generator.springmvc.model; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/prefix") +public class RestControllerWithPathSchema { + @GetMapping("/testSimpleParam") + public String testSimpleParam(String strParam) { + return strParam; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/SwaggerTestTarget.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/SwaggerTestTarget.java new file mode 100644 index 00000000000..1eb7d967835 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/SwaggerTestTarget.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.swagger.generator.springmvc.model; + +import jakarta.ws.rs.core.MediaType; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@RequestMapping(path = "/test", method = RequestMethod.POST, + consumes = {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON}, + produces = {MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN}) +public class SwaggerTestTarget { + @RequestMapping + public void method() { + + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/SwaggerTestTarget_ValueOverWritePath.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/SwaggerTestTarget_ValueOverWritePath.java new file mode 100644 index 00000000000..8d014633157 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/SwaggerTestTarget_ValueOverWritePath.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.swagger.generator.springmvc.model; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@RequestMapping(value = "/testValue", path = "/testPath", method = RequestMethod.GET) +public class SwaggerTestTarget_ValueOverWritePath { + @RequestMapping + public void method() { + + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/TestParam.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/TestParam.java new file mode 100644 index 00000000000..ab96c6a8daf --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/TestParam.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.swagger.generator.springmvc.model; + +public class TestParam { + private String name; + + private int age; + + public String getName() { + return name; + } + + public TestParam setName(String name) { + this.name = name; + return this; + } + + public int getAge() { + return age; + } + + public TestParam setAge(int age) { + this.age = age; + return this; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/TestProducer.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/TestProducer.java new file mode 100644 index 00000000000..3dcfee2026e --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/TestProducer.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.swagger.generator.springmvc.model; + +import jakarta.ws.rs.core.MediaType; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@RequestMapping(path = "/") +public class TestProducer { + @RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_XML, produces = MediaType.APPLICATION_XML) + public String testSingleMediaType(String input) { + return input; + } + + @RequestMapping(method = RequestMethod.PUT, consumes = {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON}, + produces = {MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON}) + public String testMultipleMediaType(String input) { + return input; + } + + @RequestMapping(method = RequestMethod.POST, consumes = "", produces = "") + public String testBlankMediaType(String input) { + return input; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same1/SameModel.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same1/SameModel.java new file mode 100644 index 00000000000..eab9313fb71 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same1/SameModel.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.swagger.generator.springmvc.model.same1; + +public class SameModel { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same1/SameModelThrow.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same1/SameModelThrow.java new file mode 100644 index 00000000000..06a1f61769a --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same1/SameModelThrow.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.swagger.generator.springmvc.model.same1; + +public class SameModelThrow { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same2/SameModel.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same2/SameModel.java new file mode 100644 index 00000000000..afa8c27fc52 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same2/SameModel.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.swagger.generator.springmvc.model.same2; + +public class SameModel { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same2/SameModelThrow.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same2/SameModelThrow.java new file mode 100644 index 00000000000..f7c7d52749b --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/same2/SameModelThrow.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.swagger.generator.springmvc.model.same2; + +public class SameModelThrow { + private String name; + + private int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessorTest.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessorTest.java new file mode 100644 index 00000000000..a17f85d1584 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RequestPartAnnotationProcessorTest.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.swagger.generator.springmvc.processor.annotation; + +import java.util.List; + +import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; +import org.junit.jupiter.api.Test; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +public class RequestPartAnnotationProcessorTest { + @Test + public void testDemoRest() { + UnitTestSwaggerUtils.testSwagger("schemas/DemoRest.yaml", DemoRest.class); + } + + @RequestMapping("/") + public static class DemoRest { + @RequestMapping(method = RequestMethod.POST, path = "/fun") + public void fun(@RequestPart("stringParam") String stringParam, + @RequestPart(name = "intParam") int intParam, + @RequestPart("stringParamArray") String[] stringParamArray, + @RequestPart("stringParamCollection") List stringParamCollection, + @RequestPart("file") MultipartFile file, + @RequestPart("fileArray") MultipartFile[] fileArray, + @RequestPart("fileCollection") List fileCollection) { + } + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreatorTest.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreatorTest.java new file mode 100644 index 00000000000..5597874ee66 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreatorTest.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.swagger.generator.springmvc.property.creator; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import org.springframework.web.multipart.MultipartFile; + +import io.swagger.v3.oas.models.media.FileSchema; +import io.swagger.v3.oas.models.media.Schema; + +public class MultipartFilePropertyCreatorTest { + private final MultipartFilePropertyCreator multipartFilePropertyCreator = new MultipartFilePropertyCreator(); + + @Test + public void createProperty() { + Schema property = multipartFilePropertyCreator.createProperty(); + MatcherAssert.assertThat(property, Matchers.instanceOf(FileSchema.class)); + } + + @Test + public void classes() { + Class[] classes = multipartFilePropertyCreator.classes(); + MatcherAssert.assertThat(classes, Matchers.arrayContaining(MultipartFile.class)); + } +} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/log4j.properties b/swagger/swagger-generator/generator-springmvc/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DefaultParameterSchema.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DefaultParameterSchema.yaml new file mode 100644 index 00000000000..7ba6ea4d9dc --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DefaultParameterSchema.yaml @@ -0,0 +1,171 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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.swagger.generator.springmvc.model.DefaultParameterSchema + version: 1.0.0 +servers: +- url: / +paths: + /testBlankMediaType: + post: + operationId: testBlankMediaType + parameters: + - name: input + in: query + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /testMultipleMediaType: + put: + operationId: testMultipleMediaType + parameters: + - name: input + in: query + schema: + type: string + responses: + "200": + description: response of 200 + content: + text/plain: + schema: + type: string + application/json: + schema: + type: string + /testObjectParam: + get: + operationId: testObjectParam + parameters: + - name: name + in: query + schema: + type: string + - name: age + in: query + schema: + type: integer + format: int32 + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /testSimpleParam: + get: + operationId: testSimpleParam + parameters: + - name: strParam + in: query + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /testSingleMediaType: + post: + operationId: testSingleMediaType + parameters: + - name: input + in: query + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + /testUnsupportedParamType: + get: + operationId: testUnsupportedParamType + parameters: + - name: i + in: query + schema: + type: integer + format: int32 + - name: integerList + in: query + schema: + type: array + items: + $ref: "#/components/schemas/TestParam" + - name: stringMap + in: query + schema: + type: object + additionalProperties: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string +components: + schemas: + TestParam: + type: object + properties: + name: + type: string + age: + type: integer + format: int32 + x-java-class: org.apache.servicecomb.swagger.generator.springmvc.model.TestParam diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DemoRest.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DemoRest.yaml new file mode 100644 index 00000000000..9656cc47a5c --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DemoRest.yaml @@ -0,0 +1,64 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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.swagger.generator.springmvc.processor.annotation.RequestPartAnnotationProcessorTest$DemoRest + version: 1.0.0 +servers: +- url: / +paths: + /fun: + post: + operationId: fun + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + stringParam: + type: string + intParam: + type: integer + format: int32 + stringParamArray: + type: array + items: + type: string + stringParamCollection: + type: array + items: + type: string + file: + type: string + format: binary + fileArray: + type: array + items: + type: string + format: binary + fileCollection: + type: array + items: + type: string + format: binary + responses: + "200": + description: response of 200 +components: {} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/MethodEmptyPath.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/MethodEmptyPath.yaml new file mode 100644 index 00000000000..958dc7d43d7 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/MethodEmptyPath.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.MethodEmptyPath" + x-java-interface: "gen.cse.ms.ut.MethodEmptyPathIntf" +basePath: "/MethodEmptyPath" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /: + get: + operationId: "usingGetMapping" + parameters: [] + responses: + "200": + description: "response of 200" + post: + operationId: "usingPostMapping" + parameters: [] + responses: + "200": + description: "response of 200" + put: + operationId: "usingPutMapping" + parameters: [] + responses: + "200": + description: "response of 200" + delete: + operationId: "usingDeleteMapping" + parameters: [] + responses: + "200": + description: "response of 200" + patch: + operationId: "usingPatchMapping" + parameters: [] + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/RestControllerWithPathSchema.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/RestControllerWithPathSchema.yaml new file mode 100644 index 00000000000..20786b5d695 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/RestControllerWithPathSchema.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.RestControllerWithPathSchema + version: 1.0.0 +servers: +- url: /prefix +paths: + /testSimpleParam: + get: + operationId: testSimpleParam + parameters: + - name: strParam + in: query + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string +components: {} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/asyncResponseEntity.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/asyncResponseEntity.yaml new file mode 100644 index 00000000000..cb3125f8d0f --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/asyncResponseEntity.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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "a" +- "b" +produces: +- "a" +- "b" +paths: + /: + put: + operationId: "asyncResponseEntity" + parameters: [] + responses: + "200": + description: "response of 200" + schema: + type: "array" + items: + type: "string" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/cookie.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/cookie.yaml new file mode 100644 index 00000000000..5ec6b6d7f95 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/cookie.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "a" +- "b" +produces: +- "a" +- "b" +paths: + /: + put: + operationId: "cookie" + parameters: + - name: "cookie" + in: "cookie" + required: false + type: "integer" + format: "int32" + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/defaultParam.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/defaultParam.yaml deleted file mode 100644 index 19557b0a4d0..00000000000 --- a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/defaultParam.yaml +++ /dev/null @@ -1,26 +0,0 @@ ---- -swagger: "2.0" -info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.springmvc.Echo" - x-java-interface: "gen.cse.ms.ut.EchoIntf" -basePath: "/Echo" -consumes: -- "a" -- "b" -produces: -- "a" -- "b" -paths: - /query: - put: - operationId: "defaultParam" - parameters: - - name: "query" - in: "query" - required: false - type: "integer" - format: "int32" - responses: - 200: - description: "response of 200" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/defaultParameter.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/defaultParameter.yaml new file mode 100644 index 00000000000..f1003fea277 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/defaultParameter.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.MethodDefaultParameter + version: 1.0.0 +servers: +- url: MethodDefaultParameter +paths: + /usingDeleteMapping: + delete: + operationId: usingDeleteMapping + parameters: + - name: query + in: query + schema: + type: integer + format: int32 + responses: + "200": + description: response of 200 + /usingGetMapping: + get: + operationId: usingGetMapping + parameters: + - name: query + in: query + schema: + type: integer + format: int32 + responses: + "200": + description: response of 200 + /usingPatchMapping: + patch: + operationId: usingPatchMapping + parameters: + - name: query + in: query + schema: + type: integer + format: int32 + responses: + "200": + description: response of 200 + /usingPostMapping: + post: + operationId: usingPostMapping + parameters: + - name: query + in: query + schema: + type: integer + format: int32 + responses: + "200": + description: response of 200 + /usingPutMapping: + put: + operationId: usingPutMapping + parameters: + - name: query + in: query + schema: + type: integer + format: int32 + responses: + "200": + description: response of 200 + /usingRequestMapping: + put: + operationId: usingRequestMapping + parameters: + - name: query + in: query + schema: + type: integer + format: int32 + responses: + "200": + description: response of 200 +components: {} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/echo.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/echo.yaml index 4bdaaee396e..a2f75700c1f 100644 --- a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/echo.yaml +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/echo.yaml @@ -1,57 +1,278 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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 io.servicecomb.swagger.generator.springmvc.Echo" - x-java-interface: "gen.cse.ms.ut.EchoIntf" -basePath: "/Echo" -consumes: -- "a" -- "b" -produces: -- "a" -- "b" + title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo + version: 1.0.0 +servers: +- url: Echo paths: - /echo/{targetName}: - post: - operationId: "echo" - consumes: - - "text/plain" - - "application/*" - produces: - - "text/plain" - - "application/*" + /asyncResponseEntity: + put: + operationId: asyncResponseEntity + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + type: string + text/plain: + schema: + type: array + items: + type: string + /cookie: + put: + operationId: cookie parameters: - - in: "body" - name: "srcUser" + - name: cookie + in: cookie required: false schema: - $ref: "#/definitions/User" - - name: "header" - in: "header" + type: integer + format: int32 + responses: + "200": + description: response of 200 + /emptyPath: + put: + operationId: emptyPath + responses: + "200": + description: response of 200 + /enumBody: + put: + operationId: enumBody + requestBody: + content: + application/json: + schema: + type: string + enum: + - RED + - YELLOW + - BLUE + text/plain: + schema: + type: string + enum: + - RED + - YELLOW + - BLUE required: true - type: "string" - - name: "targetName" - in: "path" + x-name: color + responses: + "200": + description: response of 200 + /inheritHttpMethod: + put: + operationId: inheritHttpMethod + parameters: + - name: query + in: query + schema: + type: integer + format: int32 + responses: + "200": + description: response of 200 + /nestedListString: + put: + operationId: nestedListString + requestBody: + content: + application/json: + schema: + type: array + items: + type: array + items: + type: string + text/plain: + schema: + type: array + items: + type: array + items: + type: string required: true - type: "string" - - name: "word" - in: "query" - required: false - type: "string" - - name: "form" - in: "formData" - required: false - type: "string" - responses: - 200: - description: "response of 200" - schema: - type: "string" -definitions: - User: - type: "object" - properties: - name: - type: "string" - x-java-class: "io.servicecomb.swagger.generator.springmvc.User" + x-name: param + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + type: array + items: + type: string + text/plain: + schema: + type: array + items: + type: array + items: + type: string + /part: + put: + operationId: part + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + part: + type: string + format: binary + responses: + "200": + description: response of 200 + /partAnnotation: + put: + operationId: partAnnotation + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + part: + type: string + format: binary + responses: + "200": + description: response of 200 + /partArray: + put: + operationId: partArray + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + part: + type: array + items: + type: string + format: binary + responses: + "200": + description: response of 200 + /partArrayAnnotation: + put: + operationId: partArrayAnnotation + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + part: + type: array + items: + type: string + format: binary + responses: + "200": + description: response of 200 + /partList: + put: + operationId: partList + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + part: + type: array + items: + type: string + format: binary + responses: + "200": + description: response of 200 + /partListAnnotation: + put: + operationId: partListAnnotation + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + part: + type: array + items: + type: string + format: binary + responses: + "200": + description: response of 200 + /rawJsonStringMethod: + put: + operationId: rawJsonStringMethod + requestBody: + content: + application/json: + schema: + type: string + text/plain: + schema: + type: string + required: true + x-raw-json: true + x-name: jsonInput + responses: + "200": + description: response of 200 + /testCompletableFutureResponseEntityOptional: + put: + operationId: testCompletableFutureResponseEntityOptional + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + text/plain: + schema: + type: string + /testResponseEntityOptional: + put: + operationId: testResponseEntityOptional + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + text/plain: + schema: + type: string +components: {} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/emptyPath.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/emptyPath.yaml index cfa5287e4ea..cd9b531492b 100644 --- a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/emptyPath.yaml +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/emptyPath.yaml @@ -1,21 +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. +## --------------------------------------------------------------------------- + --- -swagger: "2.0" +openapi: 3.0.1 info: - version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.springmvc.Echo" - x-java-interface: "gen.cse.ms.ut.EchoIntf" -basePath: "/Echo" -consumes: -- "a" -- "b" -produces: -- "a" -- "b" + title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo + version: 1.0.0 +servers: +- url: Echo paths: /: put: - operationId: "emptyPath" - parameters: [] + operationId: emptyPath responses: - 200: - description: "response of 200" + "200": + description: response of 200 diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/enumBody.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/enumBody.yaml new file mode 100644 index 00000000000..449ac41cf2c --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/enumBody.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "a" +- "b" +produces: +- "a" +- "b" +paths: + /: + put: + operationId: "enumBody" + parameters: + - in: "body" + name: "color" + required: true + schema: + type: "string" + enum: + - "RED" + - "YELLOW" + - "BLUE" + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/inheritHttpMethod.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/inheritHttpMethod.yaml index 3583a5b5f4b..69f8051f284 100644 --- a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/inheritHttpMethod.yaml +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/inheritHttpMethod.yaml @@ -1,8 +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. +## --------------------------------------------------------------------------- + --- swagger: "2.0" info: version: "1.0.0" - title: "swagger definition for io.servicecomb.swagger.generator.springmvc.Echo" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" x-java-interface: "gen.cse.ms.ut.EchoIntf" basePath: "/Echo" consumes: @@ -22,5 +39,5 @@ paths: type: "integer" format: "int32" responses: - 200: + "200": description: "response of 200" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/mixupAnnotations.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/mixupAnnotations.yaml new file mode 100644 index 00000000000..25f68d289f4 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/mixupAnnotations.yaml @@ -0,0 +1,590 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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.swagger.generator.springmvc.MethodMixupAnnotations + version: 1.0.0 +servers: +- url: MethodMixupAnnotations +paths: + /bytes: + get: + operationId: bytes + requestBody: + content: + application/json: + schema: + type: string + format: byte + application/protobuf: + schema: + type: string + format: byte + text/plain: + schema: + type: string + format: byte + required: true + x-name: value + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + format: byte + application/protobuf: + schema: + type: string + format: byte + text/plain: + schema: + type: string + format: byte + /defaultQueryParam: + post: + operationId: defaultQueryParam + parameters: + - name: prefix + in: query + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/User" + application/protobuf: + schema: + $ref: "#/components/schemas/User" + text/plain: + schema: + $ref: "#/components/schemas/User" + required: true + x-name: user + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /diffNames: + get: + summary: differentName + operationId: differentName + parameters: + - name: x + in: query + required: true + schema: + type: integer + format: int32 + - name: "y" + in: query + required: true + schema: + type: integer + format: int32 + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: integer + format: int32 + application/protobuf: + schema: + type: integer + format: int32 + text/plain: + schema: + type: integer + format: int32 + /upload: + post: + operationId: fileUpload + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + file1: + type: string + format: binary + someFile: + type: string + format: binary + responses: + "200": + description: response of 200 + content: + text/plain: + schema: + type: string + /reduce: + get: + operationId: reduce + parameters: + - name: b + in: cookie + required: true + schema: + type: integer + format: int32 + - name: a + in: query + required: false + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: integer + format: int32 + application/protobuf: + schema: + type: integer + format: int32 + text/plain: + schema: + type: integer + format: int32 + /testDefaultValue: + get: + operationId: testDefaultValue + parameters: + - name: e + in: query + required: false + schema: + type: integer + format: int32 + - name: a + in: header + required: false + schema: + type: integer + format: int32 + default: 20 + - name: b + in: cookie + required: false + schema: + type: string + default: bobo + - name: c + in: query + required: false + schema: + type: integer + format: int32 + default: 40 + - name: d + in: query + required: false + schema: + maximum: 30 + minimum: 20 + type: integer + format: int32 + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /testImplicitForm: + post: + operationId: testImplicitForm + requestBody: + content: + application/x-www-form-urlencoded: + schema: + properties: + form1: + type: string + description: a required form param + nullable: false + example: "" + form2: + type: string + description: an optional form param + nullable: true + example: "" + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /testLocalDateTime: + get: + operationId: testLocalDateTime + parameters: + - name: date + in: query + required: true + schema: + type: string + format: date-time + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + format: date-time + application/protobuf: + schema: + type: string + format: date-time + text/plain: + schema: + type: string + format: date-time + /uploadFileAndAttribute: + post: + operationId: uploadFileAndAttribute + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + attribute: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /uploadFilesAndAttribute: + post: + operationId: uploadFilesAndAttribute + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + files: + type: array + items: + type: string + format: binary + attribute: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string + /usingDeleteMapping/{targetName}: + delete: + operationId: usingDeleteMapping + parameters: + - name: header + in: header + required: true + schema: + type: string + - name: targetName + in: path + required: true + schema: + type: string + - name: word + in: query + required: true + schema: + type: string + requestBody: + content: + text/plain: + schema: + $ref: "#/components/schemas/User" + application/json: + schema: + $ref: "#/components/schemas/User" + required: true + x-name: srcUser + responses: + "200": + description: response of 200 + content: + text/plain: + schema: + type: string + application/json: + schema: + type: string + /usingGetMapping/{targetName}: + get: + operationId: usingGetMapping + parameters: + - name: header + in: header + required: true + schema: + type: string + - name: targetName + in: path + required: true + schema: + type: string + - name: word + in: query + required: true + schema: + type: string + requestBody: + content: + text/plain: + schema: + $ref: "#/components/schemas/User" + application/json: + schema: + $ref: "#/components/schemas/User" + required: true + x-name: srcUser + responses: + "200": + description: response of 200 + content: + text/plain: + schema: + type: string + application/json: + schema: + type: string + /usingPatchMapping/{targetName}: + patch: + operationId: usingPatchMapping + parameters: + - name: header + in: header + required: true + schema: + type: string + - name: targetName + in: path + required: true + schema: + type: string + - name: word + in: query + required: true + schema: + type: string + requestBody: + content: + text/plain: + schema: + $ref: "#/components/schemas/User" + application/json: + schema: + $ref: "#/components/schemas/User" + required: true + x-name: srcUser + responses: + "200": + description: response of 200 + content: + text/plain: + schema: + type: string + application/json: + schema: + type: string + /usingPostMapping/{targetName}: + post: + operationId: usingPostMapping + parameters: + - name: header + in: header + required: true + schema: + type: string + - name: targetName + in: path + required: true + schema: + type: string + - name: word + in: query + required: true + schema: + type: string + requestBody: + content: + text/plain: + schema: + $ref: "#/components/schemas/User" + application/json: + schema: + $ref: "#/components/schemas/User" + required: true + x-name: srcUser + responses: + "200": + description: response of 200 + content: + text/plain: + schema: + type: string + application/json: + schema: + type: string + /usingPutMapping/{targetName}: + put: + operationId: usingPutMapping + parameters: + - name: header + in: header + required: true + schema: + type: string + - name: targetName + in: path + required: true + schema: + type: string + - name: word + in: query + required: true + schema: + type: string + requestBody: + content: + text/plain: + schema: + $ref: "#/components/schemas/User" + application/json: + schema: + $ref: "#/components/schemas/User" + required: true + x-name: srcUser + responses: + "200": + description: response of 200 + content: + text/plain: + schema: + type: string + application/json: + schema: + type: string + /usingRequestMapping/{targetName}: + post: + operationId: usingRequestMapping + parameters: + - name: header + in: header + required: true + schema: + type: string + - name: targetName + in: path + required: true + schema: + type: string + - name: word + in: query + required: true + schema: + type: string + requestBody: + content: + text/plain: + schema: + $ref: "#/components/schemas/User" + application/json: + schema: + $ref: "#/components/schemas/User" + required: true + x-name: srcUser + responses: + "200": + description: response of 200 + content: + text/plain: + schema: + type: string + application/json: + schema: + type: string +components: + schemas: + User: + type: object + properties: + name: + type: string + friends: + type: array + items: + $ref: "#/components/schemas/User" + x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/nestedListString.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/nestedListString.yaml new file mode 100644 index 00000000000..4375c9e0b39 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/nestedListString.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "a" +- "b" +produces: +- "a" +- "b" +paths: + /nestedListString: + post: + operationId: "nestedListString" + parameters: + - in: "body" + name: "param" + required: true + schema: + type: "array" + items: + type: "array" + items: + type: "string" + responses: + "200": + description: "response of 200" + schema: + type: "array" + items: + type: "array" + items: + type: "string" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/part.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/part.yaml new file mode 100644 index 00000000000..fcfa80f5b43 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/part.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "a" +- "b" +produces: +- "a" +- "b" +paths: + /: + put: + operationId: "part" + consumes: + - "multipart/form-data" + parameters: + - name: "part" + in: "formData" + required: false + type: "file" + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partAnnotation.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partAnnotation.yaml new file mode 100644 index 00000000000..1d12b4283ab --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partAnnotation.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "a" +- "b" +produces: +- "a" +- "b" +paths: + /: + put: + operationId: "partAnnotation" + consumes: + - "multipart/form-data" + parameters: + - name: "part" + in: "formData" + required: true + type: "file" + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partArray.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partArray.yaml new file mode 100644 index 00000000000..579eb52cd0c --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partArray.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "a" +- "b" +produces: +- "a" +- "b" +paths: + /: + put: + operationId: "partArray" + consumes: + - "multipart/form-data" + parameters: + - name: "part" + in: "formData" + required: false + type: "array" + items: + type: "file" + collectionFormat: "multi" + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partArrayAnnotation.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partArrayAnnotation.yaml new file mode 100644 index 00000000000..510e0a68e21 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partArrayAnnotation.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "a" +- "b" +produces: +- "a" +- "b" +paths: + /: + put: + operationId: "partArrayAnnotation" + consumes: + - "multipart/form-data" + parameters: + - name: "part" + in: "formData" + required: true + type: "array" + items: + type: "file" + collectionFormat: "multi" + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partList.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partList.yaml new file mode 100644 index 00000000000..f1621bf6077 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partList.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "a" +- "b" +produces: +- "a" +- "b" +paths: + /: + put: + operationId: "partList" + consumes: + - "multipart/form-data" + parameters: + - name: "part" + in: "formData" + required: false + type: "array" + items: + type: "file" + collectionFormat: "multi" + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partListAnnotation.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partListAnnotation.yaml new file mode 100644 index 00000000000..1adfd9ac37a --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/partListAnnotation.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "a" +- "b" +produces: +- "a" +- "b" +paths: + /: + put: + operationId: "partListAnnotation" + consumes: + - "multipart/form-data" + parameters: + - name: "part" + in: "formData" + required: true + type: "array" + items: + type: "file" + collectionFormat: "multi" + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/rawJsonStringMethod.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/rawJsonStringMethod.yaml new file mode 100644 index 00000000000..94c17f0b76c --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/rawJsonStringMethod.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "a" +- "b" +produces: +- "a" +- "b" +paths: + /: + put: + operationId: "rawJsonStringMethod" + parameters: + - in: "body" + name: "jsonInput" + required: true + schema: + type: "string" + x-raw-json: true + responses: + "200": + description: "response of 200" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/requestMappingHttpMethod.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/requestMappingHttpMethod.yaml new file mode 100644 index 00000000000..dade54c3f01 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/requestMappingHttpMethod.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.ClassMethodNoHttpMethod + version: 1.0.0 +servers: +- url: c +paths: + /m: + get: + operationId: noHttpMethod + responses: + "200": + description: response of 200 +components: {} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/responseEntity.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/responseEntity.yaml index 15789626d32..c3763d4600d 100644 --- a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/responseEntity.yaml +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/responseEntity.yaml @@ -1,32 +1,169 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT 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 io.servicecomb.swagger.generator.springmvc.Echo" - x-java-interface: "gen.cse.ms.ut.EchoIntf" -basePath: "/Echo" -consumes: -- "a" -- "b" -produces: -- "a" -- "b" + title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.MethodResponseEntity + version: 1.0.0 +servers: +- url: MethodResponseEntity paths: - /: + /usingDeleteMapping: + delete: + operationId: usingDeleteMapping + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/User" + application/protobuf: + schema: + type: array + items: + $ref: "#/components/schemas/User" + text/plain: + schema: + type: array + items: + $ref: "#/components/schemas/User" + /usingGetMapping: + get: + operationId: usingGetMapping + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/User" + application/protobuf: + schema: + type: array + items: + $ref: "#/components/schemas/User" + text/plain: + schema: + type: array + items: + $ref: "#/components/schemas/User" + /usingPatchMapping: + patch: + operationId: usingPatchMapping + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/User" + application/protobuf: + schema: + type: array + items: + $ref: "#/components/schemas/User" + text/plain: + schema: + type: array + items: + $ref: "#/components/schemas/User" + /usingPostMapping: + post: + operationId: usingPostMapping + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/User" + application/protobuf: + schema: + type: array + items: + $ref: "#/components/schemas/User" + text/plain: + schema: + type: array + items: + $ref: "#/components/schemas/User" + /usingPutMapping: + put: + operationId: usingPutMapping + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/User" + application/protobuf: + schema: + type: array + items: + $ref: "#/components/schemas/User" + text/plain: + schema: + type: array + items: + $ref: "#/components/schemas/User" + /usingRequestMapping: put: - operationId: "testResponseEntity" - parameters: [] + operationId: usingRequestMapping responses: - 200: - description: "response of 200" - schema: - type: "array" - items: - $ref: "#/definitions/User" -definitions: - User: - type: "object" - properties: - name: - type: "string" - x-java-class: "io.servicecomb.swagger.generator.springmvc.User" + "200": + description: response of 200 + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/User" + application/protobuf: + schema: + type: array + items: + $ref: "#/components/schemas/User" + text/plain: + schema: + type: array + items: + $ref: "#/components/schemas/User" +components: + schemas: + User: + type: object + properties: + name: + type: string + friends: + type: array + items: + $ref: "#/components/schemas/User" + x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/swaggerTestTarget.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/swaggerTestTarget.yaml new file mode 100644 index 00000000000..ea7c551bff2 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/swaggerTestTarget.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.SwaggerTestTarget + version: 1.0.0 +servers: +- url: /test +paths: + /: + post: + operationId: method + responses: + "200": + description: response of 200 +components: {} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/swaggerTestTarget_ValueOverWritePath.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/swaggerTestTarget_ValueOverWritePath.yaml new file mode 100644 index 00000000000..781f06649f2 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/swaggerTestTarget_ValueOverWritePath.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.SwaggerTestTarget_ValueOverWritePath + version: 1.0.0 +servers: +- url: /testValue +paths: + /: + get: + operationId: method + responses: + "200": + description: response of 200 +components: {} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testBlankMediaType.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testBlankMediaType.yaml new file mode 100644 index 00000000000..242dec4d4ce --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testBlankMediaType.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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.TestProducer" + x-java-interface: "gen.cse.ms.ut.TestProducerIntf" +basePath: "/" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /: + post: + operationId: "testBlankMediaType" + parameters: + - name: "input" + in: "query" + required: false + type: "string" + responses: + "200": + description: "response of 200" + schema: + type: "string" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testCompletableFutureResponseEntityOptional.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testCompletableFutureResponseEntityOptional.yaml new file mode 100644 index 00000000000..d7970e1a099 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testCompletableFutureResponseEntityOptional.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "a" +- "b" +produces: +- "a" +- "b" +paths: + /: + put: + operationId: "testCompletableFutureResponseEntityOptional" + parameters: [] + responses: + "200": + description: "response of 200" + schema: + type: "string" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testMultipleMediaType.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testMultipleMediaType.yaml new file mode 100644 index 00000000000..4ecccae23fb --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testMultipleMediaType.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.TestProducer" + x-java-interface: "gen.cse.ms.ut.TestProducerIntf" +basePath: "/" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /: + put: + operationId: "testMultipleMediaType" + consumes: + - "text/plain" + - "application/json" + produces: + - "text/plain" + - "application/json" + parameters: + - name: "input" + in: "query" + required: false + type: "string" + responses: + "200": + description: "response of 200" + schema: + type: "string" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testObjectParam.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testObjectParam.yaml new file mode 100644 index 00000000000..a28f15bfc84 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testObjectParam.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.DefaultParameterSchema" + x-java-interface: "gen.cse.ms.ut.DefaultParameterSchemaIntf" +basePath: "/" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /testObjectParam: + get: + operationId: "testObjectParam" + parameters: + - name: "name" + in: "query" + required: false + type: "string" + - name: "age" + in: "query" + required: false + type: "integer" + format: "int32" + responses: + "200": + description: "response of 200" + schema: + type: "string" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testResponseEntityOptional.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testResponseEntityOptional.yaml new file mode 100644 index 00000000000..b31a1731ff3 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testResponseEntityOptional.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.Echo" + x-java-interface: "gen.cse.ms.ut.EchoIntf" +basePath: "/Echo" +consumes: +- "a" +- "b" +produces: +- "a" +- "b" +paths: + /: + put: + operationId: "testResponseEntityOptional" + parameters: [] + responses: + "200": + description: "response of 200" + schema: + type: "string" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testSimpleParam.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testSimpleParam.yaml new file mode 100644 index 00000000000..a6406ff606d --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testSimpleParam.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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.DefaultParameterSchema" + x-java-interface: "gen.cse.ms.ut.DefaultParameterSchemaIntf" +basePath: "/" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /testSimpleParam: + get: + operationId: "testSimpleParam" + parameters: + - name: "strParam" + in: "query" + required: false + type: "string" + responses: + "200": + description: "response of 200" + schema: + type: "string" diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testSingleMediaType.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testSingleMediaType.yaml new file mode 100644 index 00000000000..47f8bd011c4 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/testSingleMediaType.yaml @@ -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. +## --------------------------------------------------------------------------- + +--- +swagger: "2.0" +info: + version: "1.0.0" + title: "swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.TestProducer" + x-java-interface: "gen.cse.ms.ut.TestProducerIntf" +basePath: "/" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /: + post: + operationId: "testSingleMediaType" + consumes: + - "application/xml" + produces: + - "application/xml" + parameters: + - name: "input" + in: "query" + required: false + type: "string" + responses: + "200": + description: "response of 200" + schema: + type: "string" diff --git a/swagger/swagger-generator/pom.xml b/swagger/swagger-generator/pom.xml index 18b4a04a9ec..e9a03a9c579 100644 --- a/swagger/swagger-generator/pom.xml +++ b/swagger/swagger-generator/pom.xml @@ -1,11 +1,12 @@ - + 4.0.0 - io.servicecomb + org.apache.servicecomb swagger - 0.3.0-SNAPSHOT + 3.4.0-SNAPSHOT swagger-generator + Java Chassis::Swagger::Generator pom - generator-core - generator-jaxrs - generator-springmvc + generator-core + generator-jaxrs + generator-springmvc + generator-spring-data diff --git a/swagger/swagger-invocation/invocation-core/pom.xml b/swagger/swagger-invocation/invocation-core/pom.xml index 40a3bd8e223..20897dafd3a 100644 --- a/swagger/swagger-invocation/invocation-core/pom.xml +++ b/swagger/swagger-invocation/invocation-core/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - swagger-invocation - 0.3.0-SNAPSHOT - - swagger-invocation-core + 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 + swagger-invocation + 3.4.0-SNAPSHOT + + swagger-invocation-core + Java Chassis::Swagger::Invocation::Core + + + + org.apache.servicecomb + swagger-generator-core + - - - io.servicecomb - swagger-generator-core - - - io.servicecomb - swagger-generator-jaxrs - test - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + + org.apache.servicecomb + foundation-test-scaffolding + + + org.apache.servicecomb + swagger-generator-jaxrs + test + + + org.apache.servicecomb + swagger-generator-springmvc + test + + + org.jmockit + jmockit + test + + diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerBootstrap.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerBootstrap.java deleted file mode 100644 index 882e170778a..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerBootstrap.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.engine; - -public interface SwaggerBootstrap { - SwaggerEnvironment boot(); -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerConsumer.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerConsumer.java deleted file mode 100644 index 7d55bded53d..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerConsumer.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.engine; - -import java.util.HashMap; -import java.util.Map; - -public class SwaggerConsumer { - private Class consumerIntf; - - private Class swaggerIntf; - - private Map opMap = new HashMap<>(); - - public Class getConsumerIntf() { - return consumerIntf; - } - - public void setConsumerIntf(Class consumerIntf) { - this.consumerIntf = consumerIntf; - } - - public Class getSwaggerIntf() { - return swaggerIntf; - } - - public void setSwaggerIntf(Class swaggerIntf) { - this.swaggerIntf = swaggerIntf; - } - - public void addOperation(SwaggerConsumerOperation op) { - opMap.put(op.getName(), op); - } - - public SwaggerConsumerOperation findOperation(String name) { - return opMap.get(name); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerConsumerOperation.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerConsumerOperation.java deleted file mode 100644 index 1c4a5e3c97b..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerConsumerOperation.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.engine; - -import java.lang.reflect.Method; - -import io.servicecomb.swagger.invocation.arguments.consumer.ConsumerArgumentsMapper; -import io.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; - -public class SwaggerConsumerOperation { - private String name; - - private Method consumerMethod; - - private Method swaggerMethod; - - private ConsumerArgumentsMapper argumentsMapper; - - private ConsumerResponseMapper responseMapper; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Method getConsumerMethod() { - return consumerMethod; - } - - public void setConsumerMethod(Method consumerMethod) { - this.consumerMethod = consumerMethod; - } - - public Method getSwaggerMethod() { - return swaggerMethod; - } - - public void setSwaggerMethod(Method swaggerMethod) { - this.swaggerMethod = swaggerMethod; - } - - public ConsumerArgumentsMapper getArgumentsMapper() { - return argumentsMapper; - } - - public void setArgumentsMapper(ConsumerArgumentsMapper argumentsMapper) { - this.argumentsMapper = argumentsMapper; - } - - public ConsumerResponseMapper getResponseMapper() { - return responseMapper; - } - - public void setResponseMapper(ConsumerResponseMapper responseMapper) { - this.responseMapper = responseMapper; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerEnvironment.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerEnvironment.java deleted file mode 100644 index 648fe9857da..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerEnvironment.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.engine; - -import java.lang.reflect.Method; - -import javax.inject.Inject; - -import org.springframework.stereotype.Component; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import io.servicecomb.foundation.common.utils.ReflectUtils; -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; -import io.servicecomb.swagger.generator.core.utils.ClassUtils; -import io.servicecomb.swagger.invocation.arguments.consumer.ConsumerArgumentsMapper; -import io.servicecomb.swagger.invocation.arguments.consumer.ConsumerArgumentsMapperFactory; -import io.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapper; -import io.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapperFactory; -import io.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; -import io.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory; -import io.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; -import io.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; -import io.swagger.models.Swagger; - -@Component -public class SwaggerEnvironment { - @Inject - private ProducerArgumentsMapperFactory producerArgumentsFactory; - - @Inject - private ProducerResponseMapperFactory producerResponseMapperFactory; - - @Inject - private ConsumerArgumentsMapperFactory consumerArgumentsFactory; - - @Inject - private ConsumerResponseMapperFactory consumerResponseMapperFactory; - - public ProducerArgumentsMapperFactory getProducerArgumentsFactory() { - return producerArgumentsFactory; - } - - public void setProducerArgumentsFactory(ProducerArgumentsMapperFactory producerArgumentsFactory) { - this.producerArgumentsFactory = producerArgumentsFactory; - } - - public ProducerResponseMapperFactory getProducerResponseMapperFactory() { - return producerResponseMapperFactory; - } - - public void setProducerResponseMapperFactory(ProducerResponseMapperFactory producerResponseMapperFactory) { - this.producerResponseMapperFactory = producerResponseMapperFactory; - } - - public ConsumerArgumentsMapperFactory getConsumerArgumentsFactory() { - return consumerArgumentsFactory; - } - - public void setConsumerArgumentsFactory(ConsumerArgumentsMapperFactory consumerArgumentsFactory) { - this.consumerArgumentsFactory = consumerArgumentsFactory; - } - - public ConsumerResponseMapperFactory getConsumerResponseMapperFactory() { - return consumerResponseMapperFactory; - } - - public void setConsumerResponseMapperFactory(ConsumerResponseMapperFactory consumerResponseMapperFactory) { - this.consumerResponseMapperFactory = consumerResponseMapperFactory; - } - - public SwaggerConsumer createConsumer(Class consumerIntf) { - // consumer与契约接口相同 - return createConsumer(consumerIntf, consumerIntf); - } - - public SwaggerConsumer createConsumer(Class consumerIntf, Class swaggerIntf) { - SwaggerConsumer consumer = new SwaggerConsumer(); - consumer.setConsumerIntf(consumerIntf); - consumer.setSwaggerIntf(swaggerIntf); - for (Method consumerMethod : consumerIntf.getMethods()) { - String methodName = consumerMethod.getName(); - // consumer参数不一定等于swagger参数 - Method swaggerMethod = ReflectUtils.findMethod(swaggerIntf, methodName); - if (swaggerMethod == null) { - // consumer大于契约,非法 - String msg = String.format("consumer method %s:%s not exist in swagger.", - consumerIntf.getName(), - consumerMethod.getName()); - throw new Error(msg); - } - - ConsumerArgumentsMapper argsMapper = - consumerArgumentsFactory.createArgumentsMapper(swaggerMethod, consumerMethod); - ConsumerResponseMapper responseMapper = consumerResponseMapperFactory.createResponseMapper( - swaggerMethod.getGenericReturnType(), - consumerMethod.getGenericReturnType()); - - SwaggerConsumerOperation op = new SwaggerConsumerOperation(); - op.setName(methodName); - op.setConsumerMethod(consumerMethod); - op.setSwaggerMethod(swaggerMethod); - op.setArgumentsMapper(argsMapper); - op.setResponseMapper(responseMapper); - - consumer.addOperation(op); - } - - return consumer; - } - - public SwaggerProducer createProducer(Object producerInstance, Swagger swagger) { - Class producerCls = BeanUtils.getImplClassFromBean(producerInstance); - Class swaggerIntf = ClassUtils.getOrCreateInterface(swagger, null, null); - - SwaggerProducer producer = new SwaggerProducer(); - producer.setProducerCls(producerCls); - producer.setSwaggerIntf(swaggerIntf); - for (Method swaggerMethod : swaggerIntf.getMethods()) { - String methodName = swaggerMethod.getName(); - // producer参数不一定等于swagger参数 - Method producerMethod = ReflectUtils.findMethod(producerCls, methodName); - if (producerMethod == null) { - // producer未实现契约,非法 - String msg = String.format("swagger method %s:%s not exist in producer.", - swaggerIntf.getClass().getName(), - methodName); - throw new Error(msg); - } - - ProducerArgumentsMapper argsMapper = producerArgumentsFactory.createArgumentsMapper(swaggerMethod, - producerMethod); - ProducerResponseMapper responseMapper = producerResponseMapperFactory.createResponseMapper( - producerMethod.getGenericReturnType(), - swaggerMethod.getGenericReturnType()); - - SwaggerProducerOperation op = new SwaggerProducerOperation(); - op.setName(methodName); - op.setProducerClass(producerCls); - op.setProducerInstance(producerInstance); - op.setProducerMethod(producerMethod); - op.setSwaggerMethod(swaggerMethod); - op.setArgumentsMapper(argsMapper); - op.setResponseMapper(responseMapper); - - producer.addOperation(op); - } - - return producer; - } - - public SwaggerProducer createProducer(Object producerInstance) { - Class producerCls = BeanUtils.getImplClassFromBean(producerInstance); - SwaggerGenerator producerGenerator = UnitTestSwaggerUtils.generateSwagger(producerCls); - Swagger swagger = producerGenerator.getSwagger(); - - return createProducer(producerInstance, swagger); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerProducer.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerProducer.java deleted file mode 100644 index e37a0caf4c6..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerProducer.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.engine; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -public class SwaggerProducer { - private Class producerCls; - - private Class swaggerIntf; - - private Map opMap = new HashMap<>(); - - public Class getProducerCls() { - return producerCls; - } - - public void setProducerCls(Class producerCls) { - this.producerCls = producerCls; - } - - public Class getSwaggerIntf() { - return swaggerIntf; - } - - public void setSwaggerIntf(Class swaggerIntf) { - this.swaggerIntf = swaggerIntf; - } - - public void addOperation(SwaggerProducerOperation op) { - opMap.put(op.getName(), op); - } - - public SwaggerProducerOperation findOperation(String name) { - return opMap.get(name); - } - - public Collection getAllOperations() { - return opMap.values(); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerProducerOperation.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerProducerOperation.java deleted file mode 100644 index 533415931e7..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/SwaggerProducerOperation.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.engine; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.SwaggerInvocation; -import io.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapper; -import io.servicecomb.swagger.invocation.context.ContextUtils; -import io.servicecomb.swagger.invocation.exception.InvocationException; -import io.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; - -public class SwaggerProducerOperation { - private static final Logger LOGGER = LoggerFactory.getLogger(SwaggerProducerOperation.class); - - private String name; - - // 因为存在aop场景,所以,producerClass不一定等于producerInstance.getClass() - private Class producerClass; - - private Object producerInstance; - - private Method producerMethod; - - private Method swaggerMethod; - - private ProducerArgumentsMapper argumentsMapper; - - private ProducerResponseMapper responseMapper; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Class getProducerClass() { - return producerClass; - } - - public void setProducerClass(Class producerClass) { - this.producerClass = producerClass; - } - - public Object getProducerInstance() { - return producerInstance; - } - - public void setProducerInstance(Object producerInstance) { - this.producerInstance = producerInstance; - } - - public Method getProducerMethod() { - return producerMethod; - } - - public void setProducerMethod(Method producerMethod) { - this.producerMethod = producerMethod; - } - - public Method getSwaggerMethod() { - return swaggerMethod; - } - - public void setSwaggerMethod(Method swaggerMethod) { - this.swaggerMethod = swaggerMethod; - } - - public ProducerArgumentsMapper getArgumentsMapper() { - return argumentsMapper; - } - - public void setArgumentsMapper(ProducerArgumentsMapper argumentsMapper) { - this.argumentsMapper = argumentsMapper; - } - - public ProducerResponseMapper getResponseMapper() { - return responseMapper; - } - - public void setResponseMapper(ProducerResponseMapper responseMapper) { - this.responseMapper = responseMapper; - } - - public void invoke(SwaggerInvocation invocation, AsyncResponse asyncResp) { - ContextUtils.setInvocationContext(invocation); - - Response response = doInvoke(invocation); - - ContextUtils.removeInvocationContext(); - - asyncResp.handle(response); - } - - public Response doInvoke(SwaggerInvocation invocation) { - Response response = null; - try { - Object[] args = argumentsMapper.toProducerArgs(invocation); - Object result = producerMethod.invoke(producerInstance, args); - response = responseMapper.mapResponse(invocation.getStatus(), result); - } catch (Throwable e) { - response = processException(e); - } - return response; - } - - protected Response processException(Throwable e) { - if (InvocationTargetException.class.isInstance(e)) { - e = ((InvocationTargetException) e).getTargetException(); - } - - if (InvocationException.class.isInstance(e)) { - return Response.failResp((InvocationException) e); - } - - // 未知异常,记录下来方便定位问题 - Response response = Response.producerFailResp(e); - String msg = - String.format("Producer invoke failed, %s:%s", producerClass.getName(), producerMethod.getName()); - LOGGER.error(msg, e); - return response; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/bootstrap/BootstrapNormal.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/bootstrap/BootstrapNormal.java deleted file mode 100644 index f18dc1a714d..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/bootstrap/BootstrapNormal.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.engine.bootstrap; - -import java.util.Arrays; - -import io.servicecomb.swagger.engine.SwaggerBootstrap; -import io.servicecomb.swagger.engine.SwaggerEnvironment; -import io.servicecomb.swagger.invocation.arguments.consumer.ConsumerArgumentsMapperFactory; -import io.servicecomb.swagger.invocation.arguments.consumer.ConsumerInvocationContextMapperFactory; -import io.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapperFactory; -import io.servicecomb.swagger.invocation.arguments.producer.ProducerInvocationContextMapperFactory; -import io.servicecomb.swagger.invocation.converter.ConverterMgr; -import io.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory; -import io.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; - -public class BootstrapNormal implements SwaggerBootstrap { - public SwaggerEnvironment boot() { - SwaggerEnvironment env = new SwaggerEnvironment(); - - ConverterMgr converterMgr = new ConverterMgr(); - - ProducerArgumentsMapperFactory producerArgumentsFactory = new ProducerArgumentsMapperFactory(); - producerArgumentsFactory.setFactoryList(Arrays.asList(new ProducerInvocationContextMapperFactory())); - producerArgumentsFactory.setConverterMgr(converterMgr); - env.setProducerArgumentsFactory(producerArgumentsFactory); - - ProducerResponseMapperFactory producerResponseMapperFactory = new ProducerResponseMapperFactory(); - producerResponseMapperFactory.setConverterMgr(converterMgr); - env.setProducerResponseMapperFactory(producerResponseMapperFactory); - - ConsumerArgumentsMapperFactory consumerArgumentsFactory = new ConsumerArgumentsMapperFactory(); - consumerArgumentsFactory.setFactoryList(Arrays.asList(new ConsumerInvocationContextMapperFactory())); - consumerArgumentsFactory.setConverterMgr(converterMgr); - env.setConsumerArgumentsFactory(consumerArgumentsFactory); - - ConsumerResponseMapperFactory consumerResponseMapperFactory = new ConsumerResponseMapperFactory(); - consumerResponseMapperFactory.setConverterMgr(converterMgr); - env.setConsumerResponseMapperFactory(consumerResponseMapperFactory); - - return env; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/unittest/LocalProducerInvoker.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/unittest/LocalProducerInvoker.java deleted file mode 100644 index 1e174ba4067..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/engine/unittest/LocalProducerInvoker.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.engine.unittest; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -import io.servicecomb.swagger.engine.SwaggerConsumer; -import io.servicecomb.swagger.engine.SwaggerConsumerOperation; -import io.servicecomb.swagger.engine.SwaggerProducer; -import io.servicecomb.swagger.engine.SwaggerProducerOperation; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.SwaggerInvocation; - -public class LocalProducerInvoker implements InvocationHandler { - private Object proxy; - - private SwaggerConsumer consumer; - - private SwaggerProducer producer; - - private SwaggerInvocation invocation; - - private Response producerResponse; - - public LocalProducerInvoker(SwaggerConsumer consumer, SwaggerProducer producer) { - this.consumer = consumer; - this.producer = producer; - - proxy = Proxy.newProxyInstance(consumer.getConsumerIntf().getClassLoader(), - new Class[] {consumer.getConsumerIntf()}, - this); - } - - @SuppressWarnings("unchecked") - public T getProxy() { - return (T) proxy; - } - - public SwaggerInvocation getInvocation() { - return invocation; - } - - public T getSwaggerArgument(int idx) { - return invocation.getSwaggerArgument(idx); - } - - public T getContext(String key) { - return invocation.getContext(key); - } - - public Response getProducerResponse() { - return producerResponse; - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - invocation = new SwaggerInvocation(); - - SwaggerConsumerOperation consumerOp = consumer.findOperation(method.getName()); - SwaggerProducerOperation producerOp = producer.findOperation(method.getName()); - - consumerOp.getArgumentsMapper().toInvocation(args, invocation); - producerResponse = producerOp.doInvoke(invocation); - return consumerOp.getResponseMapper().mapResponse(producerResponse); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/extend/parameter/InvocationContextParameter.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/extend/parameter/InvocationContextParameter.java deleted file mode 100644 index b7cba507989..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/extend/parameter/InvocationContextParameter.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.extend.parameter; - -import io.swagger.models.parameters.AbstractParameter; - -public class InvocationContextParameter extends AbstractParameter implements ContextParameter { - -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/AsyncResponse.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/AsyncResponse.java deleted file mode 100644 index e5730a06e3f..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/AsyncResponse.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation; - -import javax.ws.rs.core.Response.StatusType; - -public interface AsyncResponse { - void handle(Response response); - - default void success(StatusType status, Object result) { - handle(Response.status(status).entity(result)); - } - - default void success(Object result) { - handle(Response.ok(result)); - } - - default void consumerFail(Throwable e) { - handle(Response.createConsumerFail(e)); - } - - default void producerFail(Throwable e) { - handle(Response.createProducerFail(e)); - } - - default void fail(InvocationType type, Throwable e) { - handle(Response.createFail(type, e)); - } - - default void complete(Response resp) { - handle(resp); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/InvocationType.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/InvocationType.java deleted file mode 100644 index 1270f86b609..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/InvocationType.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation; - -public enum InvocationType { - CONSUMER, - PRODUCER -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/Response.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/Response.java deleted file mode 100644 index 0146bd86465..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/Response.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation; - -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.Response.StatusType; - -import io.servicecomb.swagger.invocation.context.HttpStatus; -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.servicecomb.swagger.invocation.exception.ExceptionFactory; -import io.servicecomb.swagger.invocation.exception.InvocationException; -import io.servicecomb.swagger.invocation.response.Headers; - -/** - * 用jaxrs的Response能表达所有概念 - * 但是那个是abstract类,在不引入jersey之类的库的情况下,我们需要补充的功能太多 - * - * 所以,使用一个简单的归一化类 - */ -public class Response { - private StatusType status; - - private Headers headers = new Headers(); - - // 失败场景中,result是Throwable - private Object result; - - public boolean isSuccessed() { - return HttpStatus.isSuccess(status); - } - - public boolean isFailed() { - return !isSuccessed(); - } - - public int getStatusCode() { - return status.getStatusCode(); - } - - public String getReasonPhrase() { - return status.getReasonPhrase(); - } - - public StatusType getStatus() { - return status; - } - - public void setStatus(StatusType status) { - this.status = status; - } - - public Headers getHeaders() { - return headers; - } - - public void setHeaders(Headers headers) { - this.headers = headers; - } - - @SuppressWarnings("unchecked") - public T getResult() { - return (T) result; - } - - public void setResult(Object result) { - this.result = result; - } - - // 如果是成功,body即是result - // 如果是失败,body可能是InvocationException,也可能是InvocationException中的errorData - public static Response create(int statusCode, String reasonPhrase, Object result) { - StatusType status = new HttpStatus(statusCode, reasonPhrase); - return create(status, result); - } - - public static Response create(StatusType status, Object result) { - Response response = Response.status(status); - if (response.isFailed()) { - result = ExceptionFactory.create(status, result); - } - return response.entity(result); - } - - // 有的场景下,需要返回非200的,其他2xx状态码,所以需要支持指定 - public static Response createSuccess(StatusType status, Object result) { - return Response.status(status).entity(result); - } - - public static Response createSuccess(Object result) { - return Response.status(Status.OK).entity(result); - } - - public static Response createFail(InvocationException exception) { - return Response.status(exception.getStatus()).entity(exception); - } - - public static Response createFail(InvocationType invocationType, String errorMsg) { - CommonExceptionData errorData = new CommonExceptionData(errorMsg); - if (InvocationType.CONSUMER.equals(invocationType)) { - return createFail(ExceptionFactory.createConsumerException(errorData)); - } - - return createFail(ExceptionFactory.createProducerException(errorData)); - } - - public static Response createFail(InvocationType invocationType, Throwable throwable) { - if (InvocationType.CONSUMER.equals(invocationType)) { - return createConsumerFail(throwable); - } - - return createProducerFail(throwable); - } - - public static Response createConsumerFail(Throwable throwable) { - InvocationException exception = ExceptionFactory.convertConsumerException(throwable); - return createFail(exception); - } - - public static Response createProducerFail(Throwable throwable) { - InvocationException exception = ExceptionFactory.convertProducerException(throwable); - return createFail(exception); - } - - // 兼容接口 - - public static Response consumerFailResp(Throwable e) { - return createConsumerFail(e); - } - - public static Response producerFailResp(Throwable e) { - return createProducerFail(e); - } - - public static Response providerFailResp(Throwable e) { - return createProducerFail(e); - } - - public static Response success(Object result, StatusType status) { - return createSuccess(status, result); - } - - public static Response succResp(Object result) { - return createSuccess(result); - } - - public static Response failResp(InvocationException e) { - return createFail(e); - } - - public static Response failResp(InvocationType invocationType, Throwable e) { - return createFail(invocationType, e); - } - - // 下面是jaxrs Response的一些常见用法,照搬过来 - public Response entity(Object result) { - setResult(result); - return this; - } - - public Response build() { - return this; - } - - public static Response status(StatusType status) { - Response response = new Response(); - response.setStatus(status); - return response; - } - - public static Response ok(Object result) { - Response response = new Response(); - response.setStatus(Status.OK); - response.setResult(result); - return response; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/SwaggerInvocation.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/SwaggerInvocation.java deleted file mode 100644 index f3732dbb20c..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/SwaggerInvocation.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation; - -import io.servicecomb.swagger.invocation.context.ContextUtils; -import io.servicecomb.swagger.invocation.context.InvocationContext; - -public class SwaggerInvocation extends InvocationContext { - // 本实例是在consumer端,还是在provider端 - protected InvocationType invocationType; - - protected Object[] swaggerArguments; - - public SwaggerInvocation() { - InvocationContext context = ContextUtils.getInvocationContext(); - if (context != null) { - addContext(context.getContext()); - } - } - - public InvocationType getInvocationType() { - return invocationType; - } - - public Object[] getSwaggerArguments() { - return swaggerArguments; - } - - @SuppressWarnings("unchecked") - public T getSwaggerArgument(int idx) { - return (T) swaggerArguments[idx]; - } - - public void setSwaggerArguments(Object[] swaggerArguments) { - this.swaggerArguments = swaggerArguments; - } - - public void setSwaggerArgument(int idx, Object swaggerArgument) { - this.swaggerArguments[idx] = swaggerArgument; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ArgumentMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ArgumentMapper.java deleted file mode 100644 index 52b2be2f485..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ArgumentMapper.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments; - -import io.servicecomb.swagger.invocation.SwaggerInvocation; - -public interface ArgumentMapper { - void mapArgument(SwaggerInvocation invocation, Object[] arguments); -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ArgumentsMapperConfig.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ArgumentsMapperConfig.java deleted file mode 100644 index e8844951d26..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ArgumentsMapperConfig.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -public class ArgumentsMapperConfig { - // input - private Method swaggerMethod; - - private Method providerMethod; - - // output - private List argumentMapperList = new ArrayList<>(); - - public Method getSwaggerMethod() { - return swaggerMethod; - } - - public void setSwaggerMethod(Method swaggerMethod) { - this.swaggerMethod = swaggerMethod; - } - - public Method getProviderMethod() { - return providerMethod; - } - - public void setProviderMethod(Method providerMethod) { - this.providerMethod = providerMethod; - } - - public List getArgumentMapperList() { - return argumentMapperList; - } - - public void setArgumentMapperList(List argumentMapperList) { - this.argumentMapperList = argumentMapperList; - } - - public void addArgumentMapper(ArgumentMapper argumentMapper) { - argumentMapperList.add(argumentMapper); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ArgumentsMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ArgumentsMapperFactory.java deleted file mode 100644 index f74e887fe74..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ArgumentsMapperFactory.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; - -import org.springframework.util.TypeUtils; - -import io.servicecomb.swagger.invocation.InvocationType; -import io.servicecomb.swagger.invocation.converter.Converter; -import io.servicecomb.swagger.invocation.converter.ConverterMgr; -import io.servicecomb.swagger.invocation.converter.impl.ConverterCommon; - -public abstract class ArgumentsMapperFactory { - @Inject - protected ConverterMgr converterMgr; - - protected InvocationType type; - - // key为ContextParameter - private Map, ContextArgumentMapperFactory> factoryMap = new HashMap<>(); - - public void setConverterMgr(ConverterMgr converterMgr) { - this.converterMgr = converterMgr; - } - - protected void createFactoryMap(List factoryList) { - factoryList.forEach(factory -> { - factoryMap.put(factory.getContextClass(), factory); - }); - } - - protected ContextArgumentMapperFactory findFactory(Type type) { - if (type.getClass().equals(Class.class)) { - return factoryMap.get((Class) type); - } - return null; - } - - public T createArgumentsMapper(Method swaggerMethod, Method providerMethod) { - ArgumentsMapperConfig config = new ArgumentsMapperConfig(); - config.setSwaggerMethod(swaggerMethod); - config.setProviderMethod(providerMethod); - - collectArgumentsMapper(config); - - return createArgumentsMapper(config); - } - - protected void collectArgumentsMapper(ArgumentsMapperConfig config) { - List providerNormalParams = collectContextArgumentsMapper(config); - if (providerNormalParams.isEmpty()) { - return; - } - - if (isSwaggerWrapBody(config, providerNormalParams)) { - collectWrapBodyMapper(config, providerNormalParams); - return; - } - - collectSwaggerArgumentsMapper(config, providerNormalParams); - } - - protected boolean isSwaggerWrapBody(ArgumentsMapperConfig config, List providerNormalParams) { - Method swaggerMethod = config.getSwaggerMethod(); - if (swaggerMethod.getParameterCount() != 1) { - return false; - } - - Type swaggerType = config.getSwaggerMethod().getGenericParameterTypes()[0]; - if (!swaggerType.getClass().equals(Class.class)) { - return false; - } - - Type firstProviderParam = providerNormalParams.get(0).getType(); - if (TypeUtils.isAssignable(firstProviderParam, swaggerType)) { - return false; - } - - swaggerType = ((Class) swaggerType).getFields()[0].getGenericType(); - Converter converter = converterMgr.findConverter(type, firstProviderParam, swaggerType); - if (ConverterCommon.class.isInstance(converter)) { - return false; - } - // 透明rpc的包装场景 - return true; - } - - // 处理所有context类型的参数 - // 剩余的参数返回 - protected List collectContextArgumentsMapper(ArgumentsMapperConfig config) { - List providerNormalParams = new ArrayList<>(); - - Type[] providerParameterTypes = config.getProviderMethod().getGenericParameterTypes(); - for (int providerIdx = 0; providerIdx < providerParameterTypes.length; providerIdx++) { - Type parameterType = providerParameterTypes[providerIdx]; - ContextArgumentMapperFactory factory = findFactory(parameterType); - if (factory != null) { - ArgumentMapper mapper = factory.create(providerIdx); - config.addArgumentMapper(mapper); - continue; - } - - ProviderParameter pp = new ProviderParameter(providerIdx, parameterType); - providerNormalParams.add(pp); - } - - return providerNormalParams; - } - - protected void collectSwaggerArgumentsMapper(ArgumentsMapperConfig config, - List providerNormalParams) { - Method swaggerMethod = config.getSwaggerMethod(); - Type[] swaggerParams = swaggerMethod.getGenericParameterTypes(); - - // 普通场景,要求除了provider上的context,其他参数必须按顺序一一对应,provider上的有效参数可以与契约不一致 - int minParamCount = Math.min(providerNormalParams.size(), swaggerParams.length); - for (int swaggerIdx = 0; swaggerIdx < minParamCount; swaggerIdx++) { - ProviderParameter providerParameter = providerNormalParams.get(swaggerIdx); - Type swaggerParameter = swaggerParams[swaggerIdx]; - - Converter converter = converterMgr.findConverter(type, providerParameter.getType(), swaggerParameter); - ArgumentMapper mapper = - createArgumentMapperWithConverter(swaggerIdx, providerParameter.getIndex(), converter); - config.addArgumentMapper(mapper); - } - } - - protected void collectWrapBodyMapper(ArgumentsMapperConfig config, List providerNormalParams) { - // 将provider的参数存入唯一swagger参数的field - // 或是将唯一swagger参数的field存入provider参数 - Method swaggerMethod = config.getSwaggerMethod(); - Class swaggerParam = swaggerMethod.getParameterTypes()[0]; - Field[] swaggerParamFields = swaggerParam.getFields(); - - // 普通场景,要求除了provider上的context,其他参数必须按顺序一一对应,provider上的有效参数可以与契约不一致 - int minParamCount = Math.min(providerNormalParams.size(), swaggerParamFields.length); - - Map fieldMap = new HashMap<>(); - for (int swaggerIdx = 0; swaggerIdx < minParamCount; swaggerIdx++) { - ProviderParameter providerParameter = providerNormalParams.get(swaggerIdx); - Field swaggerField = swaggerParamFields[swaggerIdx]; - swaggerField.setAccessible(true); - - Converter converter = converterMgr.findConverter(type, - providerParameter.getType(), - swaggerField.getGenericType()); - FieldInfo info = new FieldInfo(swaggerField, converter); - fieldMap.put(providerParameter.getIndex(), info); - } - - ArgumentMapper bodyFieldArg = createBodyFieldArgMapper(config, fieldMap); - config.addArgumentMapper(bodyFieldArg); - } - - protected abstract T createArgumentsMapper(ArgumentsMapperConfig config); - - protected abstract ArgumentMapper createArgumentMapperWithConverter(int swaggerIdx, int providerIdx, - Converter converter); - - protected abstract ArgumentMapper createBodyFieldArgMapper(ArgumentsMapperConfig config, - Map fieldMap); -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ContextArgumentMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ContextArgumentMapperFactory.java deleted file mode 100644 index b9d2d54dad2..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ContextArgumentMapperFactory.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments; - -public interface ContextArgumentMapperFactory { - Class getContextClass(); - - ArgumentMapper create(int argumentIdx); -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/FieldInfo.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/FieldInfo.java deleted file mode 100644 index 98a23b44261..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/FieldInfo.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.arguments; - -import java.lang.reflect.Field; - -import io.servicecomb.swagger.invocation.converter.Converter; - -public class FieldInfo { - private Field field; - - private Converter converter; - - public FieldInfo(Field field, Converter converter) { - this.field = field; - this.converter = converter; - } - - public Field getField() { - return field; - } - - public Converter getConverter() { - return converter; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ProviderParameter.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ProviderParameter.java deleted file mode 100644 index 6bad5dff0a8..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/ProviderParameter.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.arguments; - -import java.lang.reflect.Type; - -public class ProviderParameter { - private int index; - - private Type type; - - public ProviderParameter(int index, Type type) { - this.index = index; - this.type = type; - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - public Type getType() { - return type; - } - - public void setType(Type type) { - this.type = type; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentSame.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentSame.java deleted file mode 100644 index fce7e940594..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentSame.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.consumer; - -import io.servicecomb.swagger.invocation.SwaggerInvocation; -import io.servicecomb.swagger.invocation.arguments.ArgumentMapper; -import io.servicecomb.swagger.invocation.converter.Converter; - -public final class ConsumerArgumentSame implements ArgumentMapper { - private int consumerIdx; - - private int swaggerIdx; - - private Converter converter; - - public ConsumerArgumentSame(int consumerIdx, int swaggerIdx, Converter converter) { - this.consumerIdx = consumerIdx; - this.swaggerIdx = swaggerIdx; - this.converter = converter; - } - - @Override - public void mapArgument(SwaggerInvocation invocation, Object[] consumerArguments) { - Object swaggerParam = converter.convert(consumerArguments[consumerIdx]); - invocation.setSwaggerArgument(swaggerIdx, swaggerParam); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentToBodyField.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentToBodyField.java deleted file mode 100644 index d6b4314868e..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentToBodyField.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.consumer; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import io.servicecomb.swagger.invocation.SwaggerInvocation; -import io.servicecomb.swagger.invocation.arguments.ArgumentMapper; -import io.servicecomb.swagger.invocation.arguments.FieldInfo; - -/** - * 透明RPC的典型场景 - * 因为没有标注指明RESTful映射方式 - * 所以,所有参数被包装为一个class,每个参数是一个field - */ -public class ConsumerArgumentToBodyField implements ArgumentMapper { - private Class swaggerParamType; - - // key为consumerArgs的下标 - private Map fieldMap; - - public ConsumerArgumentToBodyField(Class swaggerParamType, Map fieldMap) { - this.swaggerParamType = correctType(swaggerParamType); - this.fieldMap = fieldMap; - } - - protected Class correctType(Class swaggerParamType) { - if (!Modifier.isAbstract(swaggerParamType.getModifiers())) { - return swaggerParamType; - } - - if (List.class.isAssignableFrom(swaggerParamType)) { - return ArrayList.class; - } - - if (Set.class.isAssignableFrom(swaggerParamType)) { - return HashSet.class; - } - - throw new Error("not support " + swaggerParamType.getName()); - } - - public Class getSwaggerParamType() { - return swaggerParamType; - } - - @Override - public void mapArgument(SwaggerInvocation invocation, Object[] consumerArguments) { - try { - Object body = swaggerParamType.newInstance(); - for (Entry entry : fieldMap.entrySet()) { - FieldInfo info = entry.getValue(); - - Object consumerParam = consumerArguments[entry.getKey()]; - Object swaggerParam = info.getConverter().convert(consumerParam); - info.getField().set(body, swaggerParam); - } - invocation.setSwaggerArgument(0, body); - } catch (Throwable e) { - throw new Error(e); - } - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapper.java deleted file mode 100644 index 7238492fb22..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapper.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.consumer; - -import java.util.List; - -import io.servicecomb.swagger.invocation.SwaggerInvocation; -import io.servicecomb.swagger.invocation.arguments.ArgumentMapper; - -/** - * 将consumer参数保存到invocation中去(args/context) - * 比如契约原型是 int add(int x, int y) - * 而consumer原型是int add(InvocationContext context, int x, int y) - * - * 除了context参数,剩下的参数,必须与契约中的一一匹配,包括顺序、类型 - */ -public class ConsumerArgumentsMapper { - private List consumerArgMapperList; - - private int swaggerParameterCount; - - // for test - public ArgumentMapper getArgumentMapper(int idx) { - return consumerArgMapperList.get(idx); - } - - public ConsumerArgumentsMapper(List consumerArgMapperList, int swaggerParameterCount) { - this.consumerArgMapperList = consumerArgMapperList; - this.swaggerParameterCount = swaggerParameterCount; - } - - public void toInvocation(Object[] consumerArguments, SwaggerInvocation invocation) { - Object[] swaggerArguments = new Object[swaggerParameterCount]; - invocation.setSwaggerArguments(swaggerArguments); - - for (ArgumentMapper argMapper : consumerArgMapperList) { - argMapper.mapArgument(invocation, consumerArguments); - } - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapperFactory.java deleted file mode 100644 index 775e4ac7c9b..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapperFactory.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.consumer; - -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; - -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.InvocationType; -import io.servicecomb.swagger.invocation.arguments.ArgumentMapper; -import io.servicecomb.swagger.invocation.arguments.ArgumentsMapperConfig; -import io.servicecomb.swagger.invocation.arguments.ArgumentsMapperFactory; -import io.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; -import io.servicecomb.swagger.invocation.arguments.FieldInfo; -import io.servicecomb.swagger.invocation.converter.Converter; - -@Component -public class ConsumerArgumentsMapperFactory extends ArgumentsMapperFactory { - public ConsumerArgumentsMapperFactory() { - type = InvocationType.CONSUMER; - } - - @Inject - @Qualifier("consumer") - public void setFactoryList(List factoryList) { - createFactoryMap(factoryList); - } - - @SuppressWarnings("unchecked") - @Override - protected T createArgumentsMapper(ArgumentsMapperConfig config) { - return (T) new ConsumerArgumentsMapper(config.getArgumentMapperList(), - config.getSwaggerMethod().getParameterCount()); - } - - @Override - protected ArgumentMapper createArgumentMapperWithConverter(int swaggerIdx, int consumerIdx, Converter converter) { - return new ConsumerArgumentSame(consumerIdx, swaggerIdx, converter); - } - - @Override - protected ArgumentMapper createBodyFieldArgMapper(ArgumentsMapperConfig config, - Map fieldMap) { - Class swaggerParamType = config.getSwaggerMethod().getParameterTypes()[0]; - return new ConsumerArgumentToBodyField(swaggerParamType, fieldMap); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapper.java deleted file mode 100644 index 9dcef35a2bb..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapper.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.consumer; - -import io.servicecomb.swagger.invocation.SwaggerInvocation; -import io.servicecomb.swagger.invocation.arguments.ArgumentMapper; -import io.servicecomb.swagger.invocation.context.InvocationContext; - -public class ConsumerInvocationContextMapper implements ArgumentMapper { - private int consumerIdx; - - public ConsumerInvocationContextMapper(int consumerIdx) { - this.consumerIdx = consumerIdx; - } - - @Override - public void mapArgument(SwaggerInvocation invocation, Object[] consumerArguments) { - InvocationContext context = (InvocationContext) consumerArguments[consumerIdx]; - invocation.addContext(context.getContext()); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapperFactory.java deleted file mode 100644 index dd7745c1dfd..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapperFactory.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.consumer; - -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.arguments.ArgumentMapper; -import io.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; -import io.servicecomb.swagger.invocation.context.InvocationContext; - -@Component -@Qualifier("consumer") -public class ConsumerInvocationContextMapperFactory implements ContextArgumentMapperFactory { - - @Override - public Class getContextClass() { - return InvocationContext.class; - } - - @Override - public ArgumentMapper create(int consumerArgIdx) { - return new ConsumerInvocationContextMapper(consumerArgIdx); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/AbstractProducerContextArgMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/AbstractProducerContextArgMapper.java deleted file mode 100644 index 05b9d46d589..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/AbstractProducerContextArgMapper.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.producer; - -import io.servicecomb.swagger.invocation.SwaggerInvocation; -import io.servicecomb.swagger.invocation.arguments.ArgumentMapper; - -public abstract class AbstractProducerContextArgMapper implements ArgumentMapper { - protected int producerArgIdx; - - public AbstractProducerContextArgMapper(int producerArgIdx) { - this.producerArgIdx = producerArgIdx; - } - - @Override - public void mapArgument(SwaggerInvocation invocation, Object[] producerArguments) { - Object producerArg = createContextArg(invocation); - producerArguments[producerArgIdx] = producerArg; - } - - public abstract Object createContextArg(SwaggerInvocation invocation); -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentSame.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentSame.java deleted file mode 100644 index f0b2237c402..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentSame.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.producer; - -import io.servicecomb.swagger.invocation.SwaggerInvocation; -import io.servicecomb.swagger.invocation.arguments.ArgumentMapper; -import io.servicecomb.swagger.invocation.converter.Converter; - -public class ProducerArgumentSame implements ArgumentMapper { - private int swaggerIdx; - - private int producerIdx; - - private Converter converter; - - public ProducerArgumentSame(int swaggerIdx, int producerIdx, Converter converter) { - this.swaggerIdx = swaggerIdx; - this.producerIdx = producerIdx; - this.converter = converter; - } - - @Override - public void mapArgument(SwaggerInvocation invocation, Object[] producerArguments) { - Object swaggerParam = invocation.getSwaggerArgument(swaggerIdx); - Object producerParam = converter.convert(swaggerParam); - producerArguments[producerIdx] = producerParam; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapper.java deleted file mode 100644 index 0b46e904840..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapper.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.producer; - -import java.util.List; - -import io.servicecomb.swagger.invocation.SwaggerInvocation; -import io.servicecomb.swagger.invocation.arguments.ArgumentMapper; - -/** - * 将契约参数转为producer原型 - * 比如契约原型是 int add(int x, int y) - * 而producer原型是int add(HttpRequest request, int x, int y) - */ -public class ProducerArgumentsMapper { - private List producerArgMapperList; - - private int producerParameterCount; - - public ProducerArgumentsMapper(List producerArgMapperList, int producerParameterCount) { - this.producerArgMapperList = producerArgMapperList; - this.producerParameterCount = producerParameterCount; - } - - public Object[] toProducerArgs(SwaggerInvocation invocation) { - Object[] producerArgs = new Object[producerParameterCount]; - - for (ArgumentMapper argMapper : producerArgMapperList) { - argMapper.mapArgument(invocation, producerArgs); - } - - return producerArgs; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperFactory.java deleted file mode 100644 index a2de3586d92..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperFactory.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.producer; - -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; - -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.InvocationType; -import io.servicecomb.swagger.invocation.arguments.ArgumentMapper; -import io.servicecomb.swagger.invocation.arguments.ArgumentsMapperConfig; -import io.servicecomb.swagger.invocation.arguments.ArgumentsMapperFactory; -import io.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; -import io.servicecomb.swagger.invocation.arguments.FieldInfo; -import io.servicecomb.swagger.invocation.converter.Converter; - -@Component -public class ProducerArgumentsMapperFactory extends ArgumentsMapperFactory { - public ProducerArgumentsMapperFactory() { - type = InvocationType.PRODUCER; - } - - @Inject - @Qualifier("producer") - public void setFactoryList(List factoryList) { - createFactoryMap(factoryList); - } - - @SuppressWarnings("unchecked") - @Override - protected T createArgumentsMapper(ArgumentsMapperConfig config) { - return (T) new ProducerArgumentsMapper(config.getArgumentMapperList(), - config.getProviderMethod().getParameterCount()); - } - - @Override - protected ArgumentMapper createArgumentMapperWithConverter(int swaggerIdx, int producerIdx, Converter converter) { - return new ProducerArgumentSame(swaggerIdx, producerIdx, converter); - } - - @Override - protected ArgumentMapper createBodyFieldArgMapper(ArgumentsMapperConfig config, - Map fieldMap) { - return new SwaggerArgumentToProducerBodyField(fieldMap); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapper.java deleted file mode 100644 index 85d59351da2..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapper.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.producer; - -import io.servicecomb.swagger.invocation.SwaggerInvocation; - -public class ProducerInvocationContextMapper extends AbstractProducerContextArgMapper { - - public ProducerInvocationContextMapper(int producerIdx) { - super(producerIdx); - } - - @Override - public Object createContextArg(SwaggerInvocation invocation) { - return invocation; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapperFactory.java deleted file mode 100644 index fb681ae71e4..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapperFactory.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.producer; - -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.arguments.ArgumentMapper; -import io.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; -import io.servicecomb.swagger.invocation.context.InvocationContext; - -@Component -@Qualifier("producer") -public class ProducerInvocationContextMapperFactory implements ContextArgumentMapperFactory { - - @Override - public Class getContextClass() { - return InvocationContext.class; - } - - @Override - public ArgumentMapper create(int producerArgIdx) { - return new ProducerInvocationContextMapper(producerArgIdx); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/SwaggerArgumentToProducerBodyField.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/SwaggerArgumentToProducerBodyField.java deleted file mode 100644 index 5d5e0eb1974..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/arguments/producer/SwaggerArgumentToProducerBodyField.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.producer; - -import java.util.Map; -import java.util.Map.Entry; - -import io.servicecomb.swagger.invocation.SwaggerInvocation; -import io.servicecomb.swagger.invocation.arguments.ArgumentMapper; -import io.servicecomb.swagger.invocation.arguments.FieldInfo; - -/** - * 透明RPC的典型场景 - * 因为没有标注指明RESTful映射方式 - * 所以,所有参数被包装为一个class,每个参数是一个field - * producer在处理时,需要将这些field取出来当作参数使用 - */ -public class SwaggerArgumentToProducerBodyField implements ArgumentMapper { - // key为producerArgs的下标 - private Map fieldMap; - - public SwaggerArgumentToProducerBodyField(Map fieldMap) { - this.fieldMap = fieldMap; - } - - @Override - public void mapArgument(SwaggerInvocation invocation, Object[] producerArguments) { - Object body = invocation.getSwaggerArgument(0); - - try { - for (Entry entry : fieldMap.entrySet()) { - FieldInfo info = entry.getValue(); - - Object fieldValue = info.getField().get(body); - Object producerParam = info.getConverter().convert(fieldValue); - producerArguments[entry.getKey()] = producerParam; - } - } catch (Throwable e) { - throw new Error(e); - } - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/context/ContextUtils.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/context/ContextUtils.java deleted file mode 100644 index ce23db57722..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/context/ContextUtils.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.context; - -/** - * 传递调用过程的上下文数据 - */ -public final class ContextUtils { - private ContextUtils() { - } - - private static ThreadLocal contextMgr = new ThreadLocal<>(); - - public static InvocationContext getInvocationContext() { - return contextMgr.get(); - } - - public static InvocationContext getAndRemoveInvocationContext() { - InvocationContext context = contextMgr.get(); - if (context != null) { - contextMgr.remove(); - } - return context; - } - - public static void setInvocationContext(InvocationContext invocationContext) { - contextMgr.set(invocationContext); - } - - public static void removeInvocationContext() { - contextMgr.remove(); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/context/HttpStatus.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/context/HttpStatus.java deleted file mode 100644 index 3e3f988afc6..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/context/HttpStatus.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.context; - -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.Response.Status.Family; -import javax.ws.rs.core.Response.StatusType; - -public class HttpStatus implements StatusType { - public static boolean isSuccess(int code) { - return Status.Family.SUCCESSFUL.equals(Status.Family.familyOf(code)); - } - - public static boolean isSuccess(StatusType status) { - return Status.Family.SUCCESSFUL.equals(status.getFamily()); - } - - private final int statusCode; - - private final String reason; - - public HttpStatus(final int statusCode, final String reasonPhrase) { - this.statusCode = statusCode; - this.reason = reasonPhrase; - } - - @Override - public int getStatusCode() { - return statusCode; - } - - @Override - public Family getFamily() { - return Family.familyOf(statusCode); - } - - @Override - public String getReasonPhrase() { - return reason; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/context/HttpStatusManager.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/context/HttpStatusManager.java deleted file mode 100644 index 248f475ae80..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/context/HttpStatusManager.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.context; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.Response.StatusType; - -import io.servicecomb.foundation.common.utils.SPIServiceUtils; - -public class HttpStatusManager { - private Map statusMap = new ConcurrentHashMap<>(); - - public HttpStatusManager() { - for (Status status : Status.values()) { - statusMap.put(status.getStatusCode(), status); - } - - SPIServiceUtils.getAllService(StatusType.class).forEach(status -> { - addStatusType(status); - }); - } - - public void addStatusType(StatusType status) { - if (statusMap.containsKey(status.getStatusCode())) { - throw new Error("repeated status code: " + status.getStatusCode()); - } - - statusMap.put(status.getStatusCode(), status); - } - - public StatusType getOrCreateByStatusCode(int code) { - StatusType statusType = statusMap.get(code); - if (statusType != null) { - return statusType; - } - - statusType = new HttpStatus(code, ""); - addStatusType(statusType); - - return statusType; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/context/InvocationContext.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/context/InvocationContext.java deleted file mode 100644 index c6a3f0561ae..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/context/InvocationContext.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.context; - -import java.util.HashMap; -import java.util.Map; - -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.Response.StatusType; - -/** - * 设置特定的Cse Context数据 - */ -public class InvocationContext { - private static HttpStatusManager statusMgr = new HttpStatusManager(); - - protected StatusType httpStatus; - - // value只能是简单类型 - protected Map context = new HashMap<>(); - - public InvocationContext() { - httpStatus = Status.OK; - } - - public Map getContext() { - return context; - } - - public void setContext(Map context) { - this.context = context; - } - - public void addContext(String key, String value) { - context.put(key, value); - } - - @SuppressWarnings("unchecked") - public T getContext(String key) { - return (T) context.get(key); - } - - public void addContext(InvocationContext otherContext) { - addContext(otherContext.getContext()); - } - - public void addContext(Map otherContext) { - if (otherContext == null) { - return; - } - - context.putAll(otherContext); - } - - public StatusType getStatus() { - return httpStatus; - } - - public void setStatus(StatusType status) { - this.httpStatus = status; - } - - public void setStatus(int statusCode, String reason) { - httpStatus = new io.servicecomb.swagger.invocation.context.HttpStatus(statusCode, reason); - } - - public void setStatus(int statusCode) { - this.httpStatus = statusMgr.getOrCreateByStatusCode(statusCode); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/Converter.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/Converter.java deleted file mode 100644 index ad11cdb60c9..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/Converter.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.converter; - -public interface Converter { - Object convert(Object value); -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/ConverterMgr.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/ConverterMgr.java deleted file mode 100644 index e6dd91996b4..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/ConverterMgr.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.converter; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.commons.lang3.reflect.TypeUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.InvocationType; -import io.servicecomb.swagger.invocation.converter.impl.ConverterCommon; -import io.servicecomb.swagger.invocation.converter.impl.ConverterSame; -import io.servicecomb.swagger.invocation.converter.impl.SameElementArrayToList; -import io.servicecomb.swagger.invocation.converter.impl.SameElementArrayToSet; -import io.servicecomb.swagger.invocation.converter.impl.SameElementCollectionToArray; - -@Component -public class ConverterMgr { - private static final Logger LOGGER = LoggerFactory.getLogger(ConverterMgr.class); - - // 第一层key是src type,第二层key是target type - private Map> srcTargetMap = new HashMap<>(); - - // key为target type,这个场景,不关注源实例类型,直接用jackson convert - // 效率最低,走到这个处理流程的,都需要打印日志,说明这是很坏的实践 - // 但是如果不做这个功能,又会有一堆人觉得约束太多 - private Map commonMap = new ConcurrentHashMap<>(); - - private Converter same = ConverterSame.getInstance(); - - private Converter arrayToList = SameElementArrayToList.getInstance(); - - private Converter arrayToSet = SameElementArrayToSet.getInstance(); - - // array的构造需要有类型,不能使用统一的处理 - private Map collectionToArrayMap = new ConcurrentHashMap<>(); - - @Autowired(required = false) - public void setCustomizedConverters(List converters) { - for (CustomizedConverter converter : converters) { - Map map = srcTargetMap.get(converter.getSrcType()); - if (map == null) { - map = new HashMap<>(); - srcTargetMap.put(converter.getSrcType(), map); - } - map.put(converter.getTargetType(), converter); - } - } - - public Converter findConverter(InvocationType invocationType, Type provider, Type swagger) { - if (InvocationType.CONSUMER.equals(invocationType)) { - return findConverter(provider, swagger); - } - - return findConverter(swagger, provider); - } - - public Converter findConverter(Type src, Type target) { - Converter converter = findSrcTarget(src, target); - if (converter != null) { - return converter; - } - - converter = findAssignable(src, target); - if (converter != null) { - return converter; - } - - converter = findCollectionToArray(src, target); - if (converter != null) { - return converter; - } - - converter = findArrayToCollection(src, target); - if (converter != null) { - return converter; - } - - LOGGER.warn("Bad practice, low performance, convert from {} to {}", src, target); - return findCommonConverter(target); - } - - protected Converter findSrcTarget(Type src, Type target) { - Map map = srcTargetMap.get(src); - if (map == null) { - return null; - } - - return map.get(target); - } - - protected Converter findCommonConverter(Type target) { - Converter converter = commonMap.get(target); - if (converter != null) { - return converter; - } - - // 并发导致重复创建没有问题 - converter = new ConverterCommon(target); - commonMap.put(target, converter); - return converter; - } - - protected Converter findArrayToCollection(Type src, Type target) { - if (src.getClass().equals(Class.class) && ParameterizedType.class.isAssignableFrom(target.getClass())) { - Class srcCls = (Class) src; - ParameterizedType targetType = (ParameterizedType) target; - Class targetCls = (Class) targetType.getRawType(); - - if (srcCls.isArray() && srcCls.getComponentType().equals(targetType.getActualTypeArguments()[0])) { - if (List.class.isAssignableFrom(targetCls)) { - return arrayToList; - } - if (Set.class.isAssignableFrom(targetCls)) { - return arrayToSet; - } - } - } - - return null; - } - - protected Converter findAssignable(Type src, Type target) { - if (TypeUtils.isAssignable(src, target)) { - return same; - } - - return null; - } - - protected Converter findCollectionToArray(Type src, Type target) { - if (ParameterizedType.class.isAssignableFrom(src.getClass()) && target.getClass().equals(Class.class)) { - ParameterizedType srcType = (ParameterizedType) src; - Class srcCls = (Class) srcType.getRawType(); - Class targetCls = (Class) target; - - if (Collection.class.isAssignableFrom(srcCls) && targetCls.isArray() - && srcType.getActualTypeArguments()[0].equals(targetCls.getComponentType())) { - Converter converter = collectionToArrayMap.get(target); - if (converter == null) { - converter = new SameElementCollectionToArray(targetCls.getComponentType()); - collectionToArrayMap.put(target, converter); - } - return converter; - } - } - - return null; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/CustomizedConverter.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/CustomizedConverter.java deleted file mode 100644 index 70c79d3743f..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/CustomizedConverter.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.converter; - -import java.lang.reflect.Type; - -public interface CustomizedConverter extends Converter { - Type getSrcType(); - - Type getTargetType(); -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/ConverterCommon.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/ConverterCommon.java deleted file mode 100644 index 6340ff612e1..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/ConverterCommon.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.converter.impl; - -import java.lang.reflect.Type; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.foundation.common.utils.JsonUtils; -import io.servicecomb.swagger.invocation.converter.Converter; - -public class ConverterCommon implements Converter { - private JavaType targetJavaType; - - public ConverterCommon(Type targetType) { - targetJavaType = TypeFactory.defaultInstance().constructType(targetType); - } - - @Override - public Object convert(Object value) { - return JsonUtils.OBJ_MAPPER.convertValue(value, targetJavaType); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/ConverterSame.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/ConverterSame.java deleted file mode 100644 index b8bbf8061d1..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/ConverterSame.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.converter.impl; - -import io.servicecomb.swagger.invocation.converter.Converter; - -public final class ConverterSame implements Converter { - private static final Converter INSTANCE = new ConverterSame(); - - public static Converter getInstance() { - return INSTANCE; - } - - private ConverterSame() { - } - - @Override - public Object convert(Object value) { - return value; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/SameElementArrayToList.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/SameElementArrayToList.java deleted file mode 100644 index be1ae1ebba6..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/SameElementArrayToList.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.converter.impl; - -import java.util.Arrays; - -import io.servicecomb.swagger.invocation.converter.Converter; - -public final class SameElementArrayToList implements Converter { - private static final Converter INSTANCE = new SameElementArrayToList(); - - public static Converter getInstance() { - return INSTANCE; - } - - private SameElementArrayToList() { - } - - @Override - public Object convert(Object value) { - if (value == null) { - return null; - } - - Object[] array = (Object[]) value; - return Arrays.asList(array); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/SameElementArrayToSet.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/SameElementArrayToSet.java deleted file mode 100644 index ff7fdfae62c..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/SameElementArrayToSet.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.converter.impl; - -import java.util.HashSet; -import java.util.Set; - -import io.servicecomb.swagger.invocation.converter.Converter; - -public final class SameElementArrayToSet implements Converter { - private static final Converter INSTANCE = new SameElementArrayToSet(); - - public static Converter getInstance() { - return INSTANCE; - } - - private SameElementArrayToSet() { - } - - @Override - public Object convert(Object value) { - if (value == null) { - return null; - } - - Object[] array = (Object[]) value; - Set set = new HashSet<>(); - for (Object e : array) { - set.add(e); - } - return set; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/SameElementCollectionToArray.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/SameElementCollectionToArray.java deleted file mode 100644 index ad2fe708373..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/converter/impl/SameElementCollectionToArray.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.converter.impl; - -import java.lang.reflect.Array; -import java.util.Collection; - -import io.servicecomb.swagger.invocation.converter.Converter; - -public class SameElementCollectionToArray implements Converter { - private Class elementCls; - - public SameElementCollectionToArray(Class elementCls) { - this.elementCls = elementCls; - } - - @Override - public Object convert(Object value) { - if (value == null) { - return null; - } - - @SuppressWarnings("unchecked") - Collection collection = (Collection) value; - Object array = Array.newInstance(elementCls, collection.size()); - return collection.toArray((Object[]) array); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/exception/CommonExceptionData.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/exception/CommonExceptionData.java deleted file mode 100644 index 62d13ec2386..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/exception/CommonExceptionData.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.exception; - -/** - * 将普通异常转换为InvocationException时,保存message信息 - */ -public class CommonExceptionData { - private String message; - - public CommonExceptionData() { - } - - public CommonExceptionData(String message) { - this.message = message; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - @Override - public String toString() { - return "CommonExceptionData [message=" + message + "]"; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/exception/ExceptionFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/exception/ExceptionFactory.java deleted file mode 100644 index b2f702cfc32..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/exception/ExceptionFactory.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.exception; - -import java.lang.reflect.InvocationTargetException; - -import javax.ws.rs.core.Response.StatusType; - -import io.servicecomb.swagger.invocation.context.HttpStatus; - -public final class ExceptionFactory { - // cse内置的错误 - // 考虑到springmvc不支持自定义http错误码,所以只能使用“标准”错误码 - // 510是ws.rs中未定义,在springmvc中定义为not extended - // 在我们的流程中不可能用到这个错误定义,所以将之转义为cse的provider内部错误 - // private static final int PROVIDER_INNER_STATUS_CODE = 510; - public static final int PRODUCER_INNER_STATUS_CODE = 590; - - public static final String PRODUCER_INNER_REASON_PHRASE = "Cse Internal Server Error"; - - public static final StatusType PRODUCER_INNER_STATUS = - new HttpStatus(PRODUCER_INNER_STATUS_CODE, PRODUCER_INNER_REASON_PHRASE); - - // 420是ws.rs中未定义,在springmvc中定义为Method Failure - // 在我们的流程中不可能用到这个错误定义,所以将之转义为cse的consumer内部错误 - // private static final int CONSUMER_INNER_STATUS_CODE = 420; - public static final int CONSUMER_INNER_STATUS_CODE = 490; - - public static final String CONSUMER_INNER_REASON_PHRASE = "Cse Internal Bad Request"; - - public static final StatusType CONSUMER_INNER_STATUS = - new HttpStatus(CONSUMER_INNER_STATUS_CODE, CONSUMER_INNER_REASON_PHRASE); - - private ExceptionFactory() { - } - - public static InvocationException create(StatusType status, - Object exceptionOrErrorData) { - if (InvocationException.class.isInstance(exceptionOrErrorData)) { - return (InvocationException) exceptionOrErrorData; - } - - return doCreate(status, exceptionOrErrorData); - } - - public static InvocationException createConsumerException(Object errorData) { - return create(CONSUMER_INNER_STATUS, errorData); - } - - public static InvocationException createProducerException(Object errorData) { - return create(PRODUCER_INNER_STATUS, errorData); - } - - protected static InvocationException doCreate(StatusType status, - Object errorData) { - return new InvocationException(status, errorData); - } - - protected static InvocationException doCreate(int statusCode, String reasonPhrase, CommonExceptionData data, - Throwable e) { - return new InvocationException(statusCode, reasonPhrase, data, e); - } - - public static InvocationException convertConsumerException(Throwable e) { - return convertException(CONSUMER_INNER_STATUS_CODE, - CONSUMER_INNER_REASON_PHRASE, - e, - CONSUMER_INNER_REASON_PHRASE); - } - - public static InvocationException convertConsumerException(Throwable e, String errorMsg) { - return convertException(CONSUMER_INNER_STATUS_CODE, - CONSUMER_INNER_REASON_PHRASE, - e, - errorMsg); - } - - public static InvocationException convertProducerException(Throwable e) { - return convertException(PRODUCER_INNER_STATUS_CODE, - PRODUCER_INNER_REASON_PHRASE, - e, - PRODUCER_INNER_REASON_PHRASE); - } - - public static InvocationException convertProducerException(Throwable e, String errorMsg) { - return convertException(PRODUCER_INNER_STATUS_CODE, - PRODUCER_INNER_REASON_PHRASE, - e, - errorMsg); - } - - // 如果e中取不出可以直接返回的InvocationException - // 则需要创建新的InvocationException实例,此时不允许使用e.getMessage,因为可能会涉及关键信息被传给远端 - // 新创建的InvocationException,会使用errorMsg来构建CommonExceptionData - protected static InvocationException convertException(int statusCode, String reasonPhrase, Throwable e, - String errorMsg) { - if (InvocationTargetException.class.isInstance(e)) { - e = ((InvocationTargetException) e).getTargetException(); - } - - if (InvocationException.class.isInstance(e)) { - return (InvocationException) e; - } - - CommonExceptionData data = new CommonExceptionData(errorMsg); - return doCreate(statusCode, reasonPhrase, data, e); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/exception/InvocationException.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/exception/InvocationException.java deleted file mode 100644 index 46de7db2464..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/exception/InvocationException.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.exception; - -import javax.ws.rs.core.Response.StatusType; - -import io.servicecomb.swagger.invocation.context.HttpStatus; - -/** - * 业务在provider端,想返回在swagger中定义好的错误 - * 通过抛出本类型的异常来实现 - * 如果不是本类型的异常,则强制认为是PRODUCER_INNER_STATUS_CODE错误 - */ -public class InvocationException extends RuntimeException { - private static final long serialVersionUID = 8027482777502649656L; - - /** - * http header中的statusCode - * 不直接使用Status类型,是为了支持业务自定义code - */ - private StatusType status; - - private Object errorData; - - public InvocationException(StatusType status, Object errorData) { - this.status = status; - this.errorData = errorData; - } - - public InvocationException(StatusType status, String msg) { - this.status = status; - this.errorData = new CommonExceptionData(msg); - } - - public InvocationException(int statusCode, String reasonPhrase, Object errorData, Throwable cause) { - super(cause); - status = new HttpStatus(statusCode, reasonPhrase); - this.errorData = errorData; - } - - public InvocationException(int statusCode, String reasonPhrase, Object errorData) { - status = new HttpStatus(statusCode, reasonPhrase); - this.errorData = errorData; - } - - public StatusType getStatus() { - return status; - } - - public int getStatusCode() { - return status.getStatusCode(); - } - - public String getReasonPhrase() { - return status.getReasonPhrase(); - } - - public Object getErrorData() { - return errorData; - } - - @Override - public String getMessage() { - return this.toString(); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("InvocationException: code="); - sb.append(getStatusCode()); - sb.append(";msg="); - sb.append(getErrorData()); - return sb.toString(); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/generator/InvocationContextProcessor.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/generator/InvocationContextProcessor.java deleted file mode 100644 index a3f5032fe1d..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/generator/InvocationContextProcessor.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.generator; - -import io.servicecomb.swagger.extend.parameter.InvocationContextParameter; -import io.servicecomb.swagger.generator.core.CommonParameterTypeProcessor; -import io.servicecomb.swagger.generator.core.OperationGenerator; -import io.servicecomb.swagger.invocation.context.InvocationContext; - -public class InvocationContextProcessor implements CommonParameterTypeProcessor { - @Override - public Class getParameterType() { - return InvocationContext.class; - } - - @Override - public void process(OperationGenerator operationGenerator, int paramIdx) { - InvocationContextParameter parameter = new InvocationContextParameter(); - operationGenerator.addProviderParameter(parameter); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/Headers.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/Headers.java deleted file mode 100644 index dc6128c2641..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/Headers.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class Headers { - private Map> headerMap; - - public Map> getHeaderMap() { - return headerMap; - } - - public void setHeaderMap(Map> headerMap) { - this.headerMap = headerMap; - } - - public Object getFirst(String name) { - if (headerMap == null) { - return null; - } - - List values = headerMap.get(name); - if (values == null || values.isEmpty()) { - return null; - } - - return values.get(0); - } - - public List getHeader(String name) { - if (headerMap == null) { - return null; - } - - return headerMap.get(name); - } - - public Headers addHeader(String name, Object value) { - if (headerMap == null) { - headerMap = new HashMap<>(); - } - - List values = headerMap.get(name); - if (values == null) { - values = new ArrayList<>(); - headerMap.put(name, values); - } - values.add(value); - - return this; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/ResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/ResponseMapperFactory.java deleted file mode 100644 index 46ede2fa5d5..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/ResponseMapperFactory.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.Map; - -import javax.inject.Inject; - -import io.servicecomb.swagger.invocation.converter.Converter; -import io.servicecomb.swagger.invocation.converter.ConverterMgr; - -public abstract class ResponseMapperFactory { - @Inject - protected ConverterMgr converterMgr; - - // 特殊的应答,比如ResponseEntity/cse Response之类 - protected Map, MAPPER> mappers = new HashMap<>(); - - public void setConverterMgr(ConverterMgr converterMgr) { - this.converterMgr = converterMgr; - } - - public MAPPER createResponseMapper(Type src, Type target) { - Type type = choose(src, target); - if (ParameterizedType.class.isAssignableFrom(type.getClass())) { - type = ((ParameterizedType) type).getRawType(); - } - MAPPER mapper = mappers.get(type); - if (mapper != null) { - return mapper; - } - - Converter converter = converterMgr.findConverter(src, target); - return doCreateResponseMapper(converter); - } - - protected abstract Type choose(Type src, Type target); - - protected abstract MAPPER doCreateResponseMapper(Converter converter); -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/ResponseMeta.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/ResponseMeta.java deleted file mode 100644 index eea318daf75..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/ResponseMeta.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import com.fasterxml.jackson.databind.JavaType; - -import io.servicecomb.swagger.converter.ConverterMgr; -import io.swagger.models.Response; -import io.swagger.models.Swagger; -import io.swagger.models.properties.Property; - -public class ResponseMeta { - /** - * swagger中定义的statusCode与java类型的映射,方便consumer端transport将码流转换为具体的类型 - */ - private JavaType javaType; - - private Map headers = new HashMap<>(); - - public void init(ClassLoader classLoader, String packageName, Swagger swagger, Response response) { - if (javaType == null) { - Property property = response.getSchema(); - javaType = ConverterMgr.findJavaType(classLoader, packageName, swagger, property); - } - - if (response.getHeaders() == null) { - return; - } - for (Entry entry : response.getHeaders().entrySet()) { - JavaType headerJavaType = ConverterMgr.findJavaType(classLoader, packageName, swagger, entry.getValue()); - headers.put(entry.getKey(), headerJavaType); - } - } - - public JavaType getJavaType() { - return javaType; - } - - public void setJavaType(JavaType javaType) { - this.javaType = javaType; - } - - public Map getHeaders() { - return headers; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/ResponsesMeta.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/ResponsesMeta.java deleted file mode 100644 index dd4170ea880..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/ResponsesMeta.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response; - -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import javax.ws.rs.core.Response.Status; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.SimpleType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import io.servicecomb.swagger.invocation.context.HttpStatus; -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.swagger.models.Operation; -import io.swagger.models.Response; -import io.swagger.models.Swagger; - -public class ResponsesMeta { - private static final JavaType COMMON_EXCEPTION_JAVATYPE = SimpleType.constructUnsafe(CommonExceptionData.class); - - private Map responseMap = new HashMap<>(); - - private ResponseMeta defaultResponse; - - // 最后一个参数returnType用于兼容场景 - // 历史版本中swagger中定义的return可能没定义class名,此时consumer与swagger接口是一致的 - // 如果不传return类型进来,完全以swagger为标准,会导致生成的class不等于return - public void init(ClassLoader classLoader, String packageName, Swagger swagger, Operation operation, - Type returnType) { - initSuccessResponse(returnType); - - for (Entry entry : operation.getResponses().entrySet()) { - if ("default".equals(entry.getKey())) { - defaultResponse = new ResponseMeta(); - defaultResponse.init(classLoader, packageName, swagger, entry.getValue()); - continue; - } - - Integer statusCode = Integer.parseInt(entry.getKey()); - ResponseMeta responseMeta = responseMap.get(statusCode); - if (responseMeta == null) { - responseMeta = new ResponseMeta(); - responseMap.put(statusCode, responseMeta); - } - responseMeta.init(classLoader, packageName, swagger, entry.getValue()); - } - - if (defaultResponse == null) { - // swagger中没有定义default,加上default专用于处理exception - ResponseMeta responseMeta = new ResponseMeta(); - responseMeta.setJavaType(COMMON_EXCEPTION_JAVATYPE); - - defaultResponse = responseMeta; - } - } - - protected void initSuccessResponse(Type returnType) { - ResponseMeta successResponse = new ResponseMeta(); - successResponse.setJavaType(TypeFactory.defaultInstance().constructType(returnType)); - responseMap.put(Status.OK.getStatusCode(), successResponse); - } - - public ResponseMeta findResponseMeta(int statusCode) { - ResponseMeta responseMeta = responseMap.get(statusCode); - if (responseMeta == null) { - if (HttpStatus.isSuccess(statusCode)) { - return responseMap.get(Status.OK.getStatusCode()); - } - - return defaultResponse; - } - - return responseMeta; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/consumer/ConsumerResponseCseSame.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/consumer/ConsumerResponseCseSame.java deleted file mode 100644 index 9dbff11db75..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/consumer/ConsumerResponseCseSame.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response.consumer; - -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.Response; - -@Component -public class ConsumerResponseCseSame implements ConsumerResponseMapper { - @Override - public Class getResponseClass() { - return Response.class; - } - - @Override - public Object mapResponse(Response response) { - return response; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapper.java deleted file mode 100644 index 6e322f243e5..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapper.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response.consumer; - -import io.servicecomb.swagger.invocation.Response; - -public interface ConsumerResponseMapper { - Class getResponseClass(); - - Object mapResponse(Response response); -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapperCommon.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapperCommon.java deleted file mode 100644 index 66ac2fa9145..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapperCommon.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response.consumer; - -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.converter.Converter; - -public class ConsumerResponseMapperCommon implements ConsumerResponseMapper { - private Converter converter; - - public ConsumerResponseMapperCommon(Converter converter) { - this.converter = converter; - } - - @Override - public Class getResponseClass() { - return null; - } - - @Override - public Object mapResponse(Response response) { - return converter.convert(response.getResult()); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapperFactory.java deleted file mode 100644 index d6d6c82fcc5..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapperFactory.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response.consumer; - -import java.lang.reflect.Type; -import java.util.List; - -import javax.inject.Inject; - -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.converter.Converter; -import io.servicecomb.swagger.invocation.response.ResponseMapperFactory; - -@Component -public class ConsumerResponseMapperFactory extends ResponseMapperFactory { - @Inject - public void setMapperList(List mapperList) { - for (ConsumerResponseMapper mapper : mapperList) { - if (mapper.getResponseClass() == null) { - throw new Error("response class must not be null"); - } - - mappers.put(mapper.getResponseClass(), mapper); - } - } - - @Override - protected Type choose(Type src, Type target) { - return target; - } - - @Override - protected ConsumerResponseMapper doCreateResponseMapper(Converter converter) { - return new ConsumerResponseMapperCommon(converter); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/producer/ProducerResponseCseSame.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/producer/ProducerResponseCseSame.java deleted file mode 100644 index c4e234b4954..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/producer/ProducerResponseCseSame.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response.producer; - -import javax.ws.rs.core.Response.StatusType; - -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.Response; - -@Component -public class ProducerResponseCseSame implements ProducerResponseMapper { - @Override - public Class getResponseClass() { - return Response.class; - } - - @Override - public Response mapResponse(StatusType status, Object response) { - return (Response) response; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/producer/ProducerResponseMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/producer/ProducerResponseMapper.java deleted file mode 100644 index 2170ce3ae29..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/producer/ProducerResponseMapper.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response.producer; - -import javax.ws.rs.core.Response.StatusType; - -import io.servicecomb.swagger.invocation.Response; - -public interface ProducerResponseMapper { - Class getResponseClass(); - - Response mapResponse(StatusType status, Object response); -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/producer/ProducerResponseMapperCommon.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/producer/ProducerResponseMapperCommon.java deleted file mode 100644 index cc537b2056b..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/producer/ProducerResponseMapperCommon.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response.producer; - -import javax.ws.rs.core.Response.StatusType; - -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.converter.Converter; - -public class ProducerResponseMapperCommon implements ProducerResponseMapper { - private Converter converter; - - public ProducerResponseMapperCommon(Converter converter) { - this.converter = converter; - } - - @Override - public Class getResponseClass() { - return null; - } - - @Override - public Response mapResponse(StatusType status, Object response) { - Object swaggerResult = converter.convert(response); - return Response.create(status, swaggerResult); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/producer/ProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/producer/ProducerResponseMapperFactory.java deleted file mode 100644 index cb23b0c86fa..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/io/servicecomb/swagger/invocation/response/producer/ProducerResponseMapperFactory.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response.producer; - -import java.lang.reflect.Type; -import java.util.List; - -import javax.inject.Inject; - -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.converter.Converter; -import io.servicecomb.swagger.invocation.response.ResponseMapperFactory; - -@Component -public class ProducerResponseMapperFactory extends ResponseMapperFactory { - - @Inject - public void setMapperList(List mapperList) { - for (ProducerResponseMapper mapper : mapperList) { - if (mapper.getResponseClass() == null) { - throw new Error("response class must not be null"); - } - - mappers.put(mapper.getResponseClass(), mapper); - } - } - - @Override - protected Type choose(Type src, Type target) { - return src; - } - - @Override - protected ProducerResponseMapper doCreateResponseMapper(Converter converter) { - return new ProducerResponseMapperCommon(converter); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerConsumer.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerConsumer.java new file mode 100644 index 00000000000..0925c264248 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerConsumer.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.engine; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import com.google.common.annotations.VisibleForTesting; + +public class SwaggerConsumer { + private Class consumerIntf; + + // key is consumer method name + private final Map operations = new HashMap<>(); + + public Class getConsumerIntf() { + return consumerIntf; + } + + public void setConsumerIntf(Class consumerIntf) { + this.consumerIntf = consumerIntf; + } + + public void addOperation(SwaggerConsumerOperation op) { + operations.put(op.getConsumerMethod(), op); + } + + @VisibleForTesting + public SwaggerConsumerOperation findOperation(String consumerMethodName) { + for (Entry operationEntry : operations.entrySet()) { + if (operationEntry.getKey().getName().equals(consumerMethodName)) { + return operationEntry.getValue(); + } + } + return null; + } + + public SwaggerConsumerOperation findOperation(Method consumerMethod) { + return operations.get(consumerMethod); + } + + public Map getOperations() { + return operations; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerConsumerOperation.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerConsumerOperation.java new file mode 100644 index 00000000000..627119fe9fe --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerConsumerOperation.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.swagger.engine; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; + +public class SwaggerConsumerOperation { + private Class consumerClass; + + private Method consumerMethod; + + private String[] consumerParameterNames; + + private SwaggerOperation swaggerOperation; + + private ArgumentsMapper argumentsMapper; + + private ConsumerResponseMapper responseMapper; + + public Method getConsumerMethod() { + return consumerMethod; + } + + public void setConsumerMethod(Method consumerMethod) { + this.consumerMethod = consumerMethod; + + this.consumerParameterNames = Arrays.stream(consumerMethod.getParameters()) + .map(Parameter::getName) + .toArray(String[]::new); + } + + public Class getConsumerClass() { + return consumerClass; + } + + public void setConsumerClass(Class consumerClass) { + this.consumerClass = consumerClass; + } + + public SwaggerOperation getSwaggerOperation() { + return swaggerOperation; + } + + public void setSwaggerOperation(SwaggerOperation swaggerOperation) { + this.swaggerOperation = swaggerOperation; + } + + public ArgumentsMapper getArgumentsMapper() { + return argumentsMapper; + } + + public void setArgumentsMapper(ArgumentsMapper argumentsMapper) { + this.argumentsMapper = argumentsMapper; + } + + public ConsumerResponseMapper getResponseMapper() { + return responseMapper; + } + + public void setResponseMapper(ConsumerResponseMapper responseMapper) { + this.responseMapper = responseMapper; + } + + public Map toInvocationArguments(Object[] args) { + Map arguments = new HashMap<>(); + for (int i = 0; i < consumerParameterNames.length; i++) { + arguments.put(consumerParameterNames[i], args[i]); + } + return arguments; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerEnvironment.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerEnvironment.java new file mode 100644 index 00000000000..655e7a2c6a1 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerEnvironment.java @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.engine; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; +import org.apache.servicecomb.swagger.generator.core.utils.MethodUtils; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; +import org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerArgumentsMapperCreator; +import org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory; +import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapperCreator; +import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; +import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; +import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory; +import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; +import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.swagger.v3.core.util.Json; +import io.swagger.v3.oas.models.OpenAPI; + +public class SwaggerEnvironment { + private static final Logger LOGGER = LoggerFactory.getLogger(SwaggerEnvironment.class); + + /* + * For Consumer, first load swagger from resources. Generate one if not exists. + */ + public SwaggerConsumer createConsumer(Class consumerIntf, OpenAPI swagger) { + swagger = checkAndGenerateSwagger(consumerIntf, swagger); + + Map, ContextArgumentMapperFactory> contextFactories = SPIServiceUtils + .getOrLoadSortedService(ConsumerContextArgumentMapperFactory.class) + .stream() + .collect(Collectors.toMap(ConsumerContextArgumentMapperFactory::getContextClass, Function.identity())); + ResponseMapperFactories consumerResponseMapperFactories = + new ResponseMapperFactories<>(ConsumerResponseMapperFactory.class); + + SwaggerOperations swaggerOperations = new SwaggerOperations(swagger); + + SwaggerConsumer consumer = new SwaggerConsumer(); + consumer.setConsumerIntf(consumerIntf); + for (Method consumerMethod : MethodUtils.findSwaggerMethods(consumerIntf)) { + String operationId = findOperationId(consumerMethod); + SwaggerOperation swaggerOperation = swaggerOperations.findOperation(operationId); + if (swaggerOperation == null) { + // consumer method set is bigger than contract, it's invalid + // but we need to support consumer upgrade before producer, so only log and ignore it. + LOGGER.warn("consumer method {}:{} not exist in contract.", + consumerIntf.getName(), + consumerMethod.getName()); + continue; + } + + ConsumerArgumentsMapperCreator creator = new ConsumerArgumentsMapperCreator( + Json.mapper().getSerializationConfig(), + contextFactories, + consumerIntf, + consumerMethod, + swaggerOperation); + ArgumentsMapper argsMapper = creator.createArgumentsMapper(); + ConsumerResponseMapper responseMapper = consumerResponseMapperFactories + .createResponseMapper(consumerMethod.getGenericReturnType()); + + SwaggerConsumerOperation op = new SwaggerConsumerOperation(); + op.setConsumerClass(consumerIntf); + op.setConsumerMethod(consumerMethod); + op.setSwaggerOperation(swaggerOperation); + op.setArgumentsMapper(argsMapper); + op.setResponseMapper(responseMapper); + + consumer.addOperation(op); + } + + return consumer; + } + + protected String findOperationId(Method consumerMethod) { + return MethodUtils.findSwaggerMethodName(consumerMethod); + } + + /* + * For producer, always generate swagger from code. + */ + public SwaggerProducer createProducer(Object producerInstance) { + return createProducer(producerInstance, null); + } + + /* + * For producer, always generate swagger from code. + */ + public SwaggerProducer createProducer(Object producerInstance, Class schemaInterface) { + Class producerCls = targetSwaggerClass(producerInstance, schemaInterface); + + OpenAPI swagger = SwaggerGenerator.generate(producerCls); + + Map, ContextArgumentMapperFactory> contextFactories = SPIServiceUtils + .getOrLoadSortedService(ProducerContextArgumentMapperFactory.class) + .stream() + .collect(Collectors.toMap(ProducerContextArgumentMapperFactory::getContextClass, Function.identity())); + ResponseMapperFactories producerResponseMapperFactories = + new ResponseMapperFactories<>(ProducerResponseMapperFactory.class); + + SwaggerOperations swaggerOperations = new SwaggerOperations(swagger); + + Map visibleProducerMethods = MethodUtils.findSwaggerMethodsMapOfOperationId(producerCls); + + SwaggerProducer producer = new SwaggerProducer(); + producer.setSwagger(swagger); + producer.setProducerCls(producerCls); + producer.setProducerInstance(producerInstance); + for (SwaggerOperation swaggerOperation : swaggerOperations.getOperations().values()) { + String operationId = swaggerOperation.getOperationId(); + // producer参数不一定等于swagger参数 + Method producerMethod = visibleProducerMethods.getOrDefault(operationId, null); + if (producerMethod == null) { + // producer未实现契约,非法 + String msg = String.format("operationId %s not exist in producer %s.", + operationId, + producerInstance.getClass().getName()); + throw new IllegalStateException(msg); + } + + ProducerArgumentsMapperCreator creator = new ProducerArgumentsMapperCreator( + Json.mapper().getSerializationConfig(), + contextFactories, + producerCls, + producerMethod, + swaggerOperation); + ProducerArgumentsMapper argsMapper = creator.createArgumentsMapper(); + ProducerResponseMapper responseMapper = producerResponseMapperFactories.createResponseMapper( + producerMethod.getGenericReturnType()); + + SwaggerProducerOperation op = new SwaggerProducerOperation(); + op.setProducerClass(producerCls); + op.setProducerInstance(producerInstance); + op.setProducerMethod(producerMethod); + op.setSwaggerOperation(swaggerOperation); + op.setSwaggerParameterTypes(creator.getSwaggerParameterTypes()); + op.setArgumentsMapper(argsMapper); + op.setResponseMapper(responseMapper); + + producer.addOperation(op); + } + + return producer; + } + + private OpenAPI checkAndGenerateSwagger(Class swaggerClass, OpenAPI swagger) { + if (swagger == null) { + swagger = SwaggerGenerator.generate(swaggerClass); + } + return swagger; + } + + private Class targetSwaggerClass(Object producerInstance, Class schemaInterface) { + if (schemaInterface != null && !Object.class.equals(schemaInterface)) { + return schemaInterface; + } + return BeanUtils.getImplClassFromBean(producerInstance); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerProducer.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerProducer.java new file mode 100644 index 00000000000..43d0577937a --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerProducer.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.engine; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import io.swagger.v3.oas.models.OpenAPI; + +public class SwaggerProducer { + private Class producerCls; + + private Object producerInstance; + + private OpenAPI swagger; + + // key is operationId + private final Map opMap = new HashMap<>(); + + public Class getProducerCls() { + return producerCls; + } + + public void setProducerCls(Class producerCls) { + this.producerCls = producerCls; + } + + public Object getProducerInstance() { + return producerInstance; + } + + public void setProducerInstance(Object producerInstance) { + this.producerInstance = producerInstance; + } + + public OpenAPI getSwagger() { + return swagger; + } + + public void setSwagger(OpenAPI swagger) { + this.swagger = swagger; + } + + public void addOperation(SwaggerProducerOperation op) { + opMap.put(op.getOperationId(), op); + } + + public SwaggerProducerOperation findOperation(String operationId) { + return opMap.get(operationId); + } + + public Collection getAllOperations() { + return opMap.values(); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerProducerOperation.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerProducerOperation.java new file mode 100644 index 00000000000..ad4728aacff --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerProducerOperation.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.engine; + +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Map; + +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; +import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; + +public class SwaggerProducerOperation { + + // 因为存在aop场景,所以,producerClass不一定等于producerInstance.getClass() + private Class producerClass; + + private Object producerInstance; + + private Method producerMethod; + + private SwaggerOperation swaggerOperation; + + // swagger parameter types relate to producer + // because features of @BeanParam/query wrapper/rpc mode parameter wrapper + // types is not direct equals to producerMethod parameter types + private Map swaggerParameterTypes; + + private ProducerArgumentsMapper argumentsMapper; + + private ProducerResponseMapper responseMapper; + + public String getOperationId() { + return swaggerOperation.getOperationId(); + } + + public Class getProducerClass() { + return producerClass; + } + + public void setProducerClass(Class producerClass) { + this.producerClass = producerClass; + } + + public Object getProducerInstance() { + return producerInstance; + } + + public void setProducerInstance(Object producerInstance) { + this.producerInstance = producerInstance; + } + + public Method getProducerMethod() { + return producerMethod; + } + + public void setProducerMethod(Method producerMethod) { + this.producerMethod = producerMethod; + } + + public SwaggerOperation getSwaggerOperation() { + return swaggerOperation; + } + + public void setSwaggerOperation(SwaggerOperation swaggerOperation) { + this.swaggerOperation = swaggerOperation; + } + + + public Map getSwaggerParameterTypes() { + return swaggerParameterTypes; + } + + public void setSwaggerParameterTypes(Map swaggerParameterTypes) { + this.swaggerParameterTypes = swaggerParameterTypes; + } + + public ProducerArgumentsMapper getArgumentsMapper() { + return argumentsMapper; + } + + public void setArgumentsMapper(ProducerArgumentsMapper argumentsMapper) { + this.argumentsMapper = argumentsMapper; + } + + public ProducerResponseMapper getResponseMapper() { + return responseMapper; + } + + public void setResponseMapper(ProducerResponseMapper responseMapper) { + this.responseMapper = responseMapper; + } + + public Type getSwaggerParameterType(String name) { + return this.swaggerParameterTypes.get(name); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/AsyncResponse.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/AsyncResponse.java new file mode 100644 index 00000000000..0e0bffa24cb --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/AsyncResponse.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.swagger.invocation; + +import jakarta.ws.rs.core.Response.StatusType; + +public interface AsyncResponse { + void handle(Response response); + + default void success(StatusType status, Object result) { + handle(Response.status(status).entity(result)); + } + + default void success(Object result) { + handle(Response.ok(result)); + } + + default void consumerFail(Throwable e) { + handle(Response.createConsumerFail(e)); + } + + default void producerFail(Throwable e) { + handle(Response.createProducerFail(e)); + } + + default void fail(InvocationType type, Throwable e) { + handle(Response.createFail(type, e)); + } + + default void complete(Response resp) { + handle(resp); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/InvocationType.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/InvocationType.java new file mode 100644 index 00000000000..351c876de71 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/InvocationType.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.swagger.invocation; + +public enum InvocationType { + CONSUMER, + PROVIDER, + EDGE +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/Response.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/Response.java new file mode 100644 index 00000000000..26cfb48d322 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/Response.java @@ -0,0 +1,239 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation; + +import java.util.List; + +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.StatusType; + +import org.apache.servicecomb.swagger.invocation.context.HttpStatus; +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 io.vertx.core.MultiMap; + +/** + * 用jaxrs的Response能表达所有概念 + * 但是那个是abstract类,在不引入jersey之类的库的情况下,我们需要补充的功能太多 + * + * 所以,使用一个简单的归一化类 + */ +public class Response { + private StatusType status; + + private MultiMap headers; + + // 失败场景中,result是Throwable + private Object result; + + public boolean isSucceed() { + return HttpStatus.isSuccess(status); + } + + public boolean isFailed() { + return !isSucceed(); + } + + public int getStatusCode() { + return status.getStatusCode(); + } + + public String getReasonPhrase() { + return status.getReasonPhrase(); + } + + public StatusType getStatus() { + return status; + } + + public void setStatus(StatusType status) { + this.status = status; + } + + public String getHeader(String name) { + if (headers == null) { + return null; + } + + return headers.get(name); + } + + public List getHeaders(String name) { + if (headers == null) { + return null; + } + + return headers.getAll(name); + } + + public MultiMap getHeaders() { + return headers; + } + + public Response setHeaders(MultiMap headers) { + this.headers = headers; + return this; + } + + public Response addHeader(String name, String value) { + if (value == null) { + return this; + } + if (headers == null) { + headers = MultiMap.caseInsensitiveMultiMap(); + } + + headers.add(name, value); + return this; + } + + public Response setHeader(String name, String value) { + if (value == null) { + return this; + } + if (headers == null) { + headers = MultiMap.caseInsensitiveMultiMap(); + } + + headers.set(name, value); + return this; + } + + @SuppressWarnings("unchecked") + public T getResult() { + return (T) result; + } + + public void setResult(Object result) { + this.result = result; + } + + // 如果是成功,body即是result + // 如果是失败,body可能是InvocationException,也可能是InvocationException中的errorData + public static Response create(int statusCode, String reasonPhrase, Object result) { + StatusType status = new HttpStatus(statusCode, reasonPhrase); + return create(status, result); + } + + public static Response create(StatusType status, Object result) { + Response response = Response.status(status); + if (response.isFailed()) { + result = ExceptionFactory.create(status, result); + } + return response.entity(result); + } + + // 有的场景下,需要返回非200的,其他2xx状态码,所以需要支持指定 + public static Response createSuccess(StatusType status, Object result) { + return Response.status(status).entity(result); + } + + public static Response createSuccess(Object result) { + return Response.status(Status.OK).entity(result); + } + + public static Response createFail(InvocationException exception) { + return Response.status(exception.getStatus()).entity(exception); + } + + public static Response createFail(InvocationType invocationType, String errorMsg) { + CommonExceptionData errorData = new CommonExceptionData(errorMsg); + if (InvocationType.CONSUMER.equals(invocationType)) { + return createFail(ExceptionFactory.createConsumerException(errorData)); + } + + return createFail(ExceptionFactory.createProducerException(errorData)); + } + + public static Response createFail(InvocationType invocationType, Throwable throwable) { + if (InvocationType.CONSUMER.equals(invocationType)) { + return createConsumerFail(throwable); + } + + return createProducerFail(throwable); + } + + public static Response createConsumerFail(Throwable throwable) { + InvocationException exception = ExceptionFactory.convertConsumerException(throwable); + return createFail(exception); + } + + public static Response createConsumerFail(Throwable throwable, String message) { + InvocationException exception = ExceptionFactory.convertConsumerException(throwable, message); + return createFail(exception); + } + + public static Response createProducerFail(Throwable throwable) { + InvocationException exception = ExceptionFactory.convertProducerException(throwable); + return createFail(exception); + } + + // 兼容接口 + + public static Response consumerFailResp(Throwable e) { + return createConsumerFail(e); + } + + public static Response producerFailResp(Throwable e) { + return createProducerFail(e); + } + + public static Response providerFailResp(Throwable e) { + return createProducerFail(e); + } + + public static Response success(Object result, StatusType status) { + return createSuccess(status, result); + } + + public static Response succResp(Object result) { + return createSuccess(result); + } + + public static Response failResp(InvocationException e) { + return createFail(e); + } + + public static Response failResp(InvocationType invocationType, Throwable e) { + return createFail(invocationType, e); + } + + // 下面是jaxrs Response的一些常见用法,照搬过来 + public Response entity(Object result) { + setResult(result); + return this; + } + + public Response build() { + return this; + } + + public static Response status(StatusType status) { + Response response = new Response(); + response.setStatus(status); + return response; + } + + public static Response ok(Object result) { + Response response = new Response(); + response.setStatus(Status.OK); + response.setResult(result); + return response; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/SwaggerInvocation.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/SwaggerInvocation.java new file mode 100644 index 00000000000..52386716189 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/SwaggerInvocation.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.swagger.invocation; + +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + +public class SwaggerInvocation extends InvocationContext { + // 本实例是在consumer端,还是在provider端 + protected InvocationType invocationType; + + protected InvocationContext parentContext; + + public SwaggerInvocation() { + parentContext = ContextUtils.getInvocationContext(); + if (parentContext != null) { + addContext(parentContext.getContext()); + addLocalContext(parentContext.getLocalContext()); + } + } + + public InvocationContext getParentContext() { + return parentContext; + } + + public InvocationType getInvocationType() { + return invocationType; + } + + public String getInvocationQualifiedName() { + return invocationType.name(); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/AbstractArgumentsMapperCreator.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/AbstractArgumentsMapperCreator.java new file mode 100644 index 00000000000..28022c898ed --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/AbstractArgumentsMapperCreator.java @@ -0,0 +1,297 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.arguments; + +import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.collectParameterName; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; + +import com.fasterxml.jackson.databind.SerializationConfig; + +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; + +/** + *
+ *   1.common
+ *     context type parameter is not swagger parameter:
+ *       InvocationContext
+ *       HttpServletRequest
+ *
+ *   2.same version
+ *     1) direct map (most scenes)
+ *       interface method:
+ *         class AddParam {
+ *           int x;
+ *           int y;
+ *         }
+ *         int add(InvocationContext context, AddParam param)
+ *       swagger parameters:
+ *         param
+ *
+ *       interface method:
+ *         int add(int x, int y)
+ *       swagger parameters:
+ *         x, y
+ *    2) swagger only one POJO parameter, extract all field to method parameters (POJO dev mode)
+ *      interface method:
+ *        int add(int x, int y)
+ *      swagger parameters:
+ *        param
+ *    3) wrap some simple continuously swagger parameters to POJO (springmvc query parameters)
+ *      interface method:
+ *        int add(String name, AddParam param, Body body)
+ *      swagger parameters:
+ *        name, x, y, body
+ *    4) wrap some simple and complex continuously swagger parameters to POJO  (JaxRS BeanParam)
+ *      interface method:
+ *        class BeanWrapper {
+ *          int x;
+ *          int y;
+ *          Body body;
+ *        }
+ *        int add(String name, AddParam param, Body body)
+ *      swagger parameters:
+ *        name, x, y, body
+ *  2. invoke old version
+ *    interface method:
+ *      int add(int x, int y, int z)
+ *    swagger parameter:
+ *      x, y
+ *  3. invoke new version
+ *    interface method:
+ *      int add(int x, int y)
+ *    swagger parameter:
+ *      x, y, z
+ * 
+ */ +@SuppressWarnings({"rawtypes", "unchecked"}) +public abstract class AbstractArgumentsMapperCreator { + protected boolean isSwaggerBodyField = false; + + protected SerializationConfig serializationConfig; + + // key is context class + protected Map, ContextArgumentMapperFactory> contextFactories; + + // consumer or producer + protected Method providerMethod; + + protected Class providerClass; + + protected SwaggerOperation swaggerOperation; + + protected List mappers = new ArrayList<>(); + + protected List swaggerParameters; + + protected RequestBody bodyParameter; + + // For pojo wrapped bodies only + protected Map swaggerBodyProperties; + + protected Set processedSwaggerParameters; + + public AbstractArgumentsMapperCreator(SerializationConfig serializationConfig, + Map, ContextArgumentMapperFactory> contextFactories, Class providerClass, + Method providerMethod, SwaggerOperation swaggerOperation) { + this.serializationConfig = serializationConfig; + this.contextFactories = contextFactories; + this.providerClass = providerClass; + this.providerMethod = providerMethod; + this.swaggerOperation = swaggerOperation; + + this.swaggerParameters = this.swaggerOperation.getOperation().getParameters(); + + this.bodyParameter = this.swaggerOperation.getOperation().getRequestBody(); + this.swaggerBodyProperties = readSwaggerBodyProperties(); + this.processedSwaggerParameters = new HashSet<>(); + } + + private Map readSwaggerBodyProperties() { + if (bodyParameter == null || bodyParameter.getContent() == null + || bodyParameter.getContent().size() == 0) { + return null; + } + // For pojo wrapped bodies only + if (bodyParameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE) != null || + bodyParameter.getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null) { + return null; + } + Schema schema = bodyParameter.getContent().entrySet().iterator().next().getValue().getSchema(); + if (schema != null && schema.get$ref() != null) { + schema = SwaggerUtils.getSchema(swaggerOperation.getSwagger(), schema.get$ref()); + } + if (schema != null && schema.getProperties() != null) { + return schema.getProperties(); + } + return null; + } + + protected void doCreateArgumentsMapper() { + java.lang.reflect.Parameter[] providerParameters = providerMethod.getParameters(); + for (int providerParamIdx = 0; providerParamIdx < providerParameters.length; providerParamIdx++) { + java.lang.reflect.Parameter providerParameter = providerParameters[providerParamIdx]; + if (processContextParameter(providerParameter)) { + continue; + } + + String parameterName = collectParameterName(providerParameter); + if (processKnownParameter(providerParamIdx, parameterName)) { + processedSwaggerParameters.add(parameterName); + continue; + } + + if (processSwaggerBodyField(providerParamIdx, parameterName)) { + processedSwaggerParameters.add(parameterName); + isSwaggerBodyField = true; + continue; + } + + if (processBeanParameter(providerParamIdx, providerParameter)) { + continue; + } + + processUnknownParameter(providerParamIdx, providerParameter, parameterName); + } + + // Process swagger parameters that not in method parameters + if (swaggerParameters != null) { + for (Parameter parameter : swaggerParameters) { + if (!processedSwaggerParameters.contains(parameter.getName())) { + processPendingSwaggerParameter(parameter); + } + } + } + if (bodyParameter != null) { + processPendingBodyParameter(bodyParameter); + } + } + + /** + * + * @param providerParameter processing provider parameter + * @return true means processed + */ + protected boolean processContextParameter(java.lang.reflect.Parameter providerParameter) { + ContextArgumentMapperFactory contextFactory = contextFactories.get(providerParameter.getType()); + if (contextFactory == null) { + return false; + } + + mappers.add(contextFactory + .create(providerParameter.getName(), providerParameter.getName())); + return true; + } + + /** + * Parameters has the same name in method and swagger. + */ + protected boolean processKnownParameter(int providerParamIdx, String invocationArgumentName) { + if (!parameterNameExistsInSwagger(invocationArgumentName)) { + return false; + } + + ArgumentMapper mapper = createKnownParameterMapper(providerParamIdx, invocationArgumentName); + mappers.add(mapper); + return true; + } + + protected boolean parameterNameExistsInSwagger(String parameterName) { + if (this.swaggerParameters != null) { + for (Parameter parameter : this.swaggerParameters) { + if (parameterName.equals(parameter.getName())) { + return true; + } + } + } + if (this.bodyParameter != null && this.bodyParameter.getContent() != null) { + if (this.bodyParameter.getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null && + this.bodyParameter.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema() != null && + this.bodyParameter.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties() != null) { + return this.bodyParameter.getContent() + .get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties().get(parameterName) != null; + } + if (this.bodyParameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE) != null && + this.bodyParameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE).getSchema() != null && + this.bodyParameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE).getSchema().getProperties() != null) { + return this.bodyParameter.getContent() + .get(SwaggerConst.FILE_MEDIA_TYPE).getSchema().getProperties().get(parameterName) != null; + } + } + if (this.bodyParameter != null && this.bodyParameter.getExtensions() != null) { + return parameterName.equals(this.bodyParameter.getExtensions().get(SwaggerConst.EXT_BODY_NAME)); + } + return false; + } + + protected abstract ArgumentMapper createKnownParameterMapper(int providerParamIdx, String parameterName); + + /** + * Process POJO wrapped parameters, e.g. + * method(int foo, int bar) + * and Form parameters, e.g. + * method(@FormParam("foo") int foo, @FormParam("bar") int bar) + */ + protected boolean processSwaggerBodyField(int providerParamIdx, String parameterName) { + if (swaggerBodyProperties == null || swaggerBodyProperties.get(parameterName) == null) { + return false; + } + + ArgumentMapper mapper = createSwaggerBodyFieldMapper(providerParamIdx, parameterName); + mappers.add(mapper); + return true; + } + + protected abstract ArgumentMapper createSwaggerBodyFieldMapper(int providerParamIdx, String parameterName); + + /** + * Bean parameters, e.g. + * + * method(QueryModels queries) + * + * where swagger should be: + * - in: query + * name: foo + * - in: query + * name: bar + */ + protected abstract boolean processBeanParameter(int providerParamIdx, java.lang.reflect.Parameter providerParameter); + + protected abstract void processUnknownParameter(int providerParamIdx, java.lang.reflect.Parameter providerParameter, + String parameterName); + + /** + * Process parameters that in swagger but not in method. + */ + protected abstract void processPendingSwaggerParameter(Parameter parameter); + + /** + * Process body parameter that in swagger but not in method. + */ + protected abstract void processPendingBodyParameter(RequestBody parameter); +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentMapper.java new file mode 100644 index 00000000000..0800ee136f6 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentMapper.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.swagger.invocation.arguments; + +import java.util.Map; + +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; + +public interface ArgumentMapper { + void swaggerArgumentToInvocationArguments(SwaggerInvocation swaggerInvocation, Map swaggerArguments, + Map invocationArguments); + + void invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, Map swaggerArguments, + Map invocationArguments); +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentsMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentsMapper.java new file mode 100644 index 00000000000..776dbff69d8 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ArgumentsMapper.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.swagger.invocation.arguments; + +import java.util.Map; + +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; + +public interface ArgumentsMapper { + default Map swaggerArgumentToInvocationArguments(SwaggerInvocation invocation, + Map swaggerArguments) { + throw new IllegalStateException("not implemented."); + } + + /** + *
+   * for scenes that consumer arguments not same to contract arguments, eg:
+   * 1.consumer: int add(QueryWrapper query)
+   *             class QueryWrapper {
+   *               public int x;
+   *               public int y;
+   *             }
+   *   contract: int add(int x, int y)
+   *
+   * 2.consumer: int add(InvocationContext context, int x, int y);
+   *   contract: int add(int x, int y)
+   *
+   * 3.consumer: int add(int x, int y)
+   *   contract: int add(BodyRequest body)
+   *             class BodyRequest {
+   *               public int x;
+   *               public int y;
+   *             }
+   *
+   * notice:
+   *   no convert logic when map arguments
+   *   map arguments by name, DO NOT use duplicated contract argument names
+   * 
+ * + */ + default Map invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, + Map invocationArguments) { + throw new IllegalStateException("not implemented."); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ContextArgumentMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ContextArgumentMapperFactory.java new file mode 100644 index 00000000000..6e67b7b2e62 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/ContextArgumentMapperFactory.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.swagger.invocation.arguments; + +public interface ContextArgumentMapperFactory { + Class getContextClass(); + + ArgumentMapper create(String invocationArgumentName, String swaggerArgumentName); +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ArgumentsMapperCommon.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ArgumentsMapperCommon.java new file mode 100644 index 00000000000..3beb4194236 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ArgumentsMapperCommon.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.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; + +/** + * map consumer arguments to swagger arguments + */ +public class ArgumentsMapperCommon implements ArgumentsMapper { + private final List mappers; + + public ArgumentsMapperCommon(List mappers) { + this.mappers = mappers; + } + + @Override + public Map invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, + Map invocationArguments) { + Map swaggerParameters = new HashMap<>(invocationArguments.size()); + for (ArgumentMapper argMapper : mappers) { + argMapper.invocationArgumentToSwaggerArguments(swaggerInvocation, swaggerParameters, invocationArguments); + } + return swaggerParameters; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ArgumentsMapperDirectReuse.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ArgumentsMapperDirectReuse.java new file mode 100644 index 00000000000..6a243407b26 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ArgumentsMapperDirectReuse.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.swagger.invocation.arguments.consumer; + +import java.util.Map; + +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; + +public class ArgumentsMapperDirectReuse implements ArgumentsMapper { + @Override + public Map invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, + Map invocationArguments) { + return invocationArguments; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentMapper.java new file mode 100644 index 00000000000..0034717ce64 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentMapper.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.swagger.invocation.arguments.consumer; + +import java.util.Map; + +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; + +public abstract class ConsumerArgumentMapper implements ArgumentMapper { + public void swaggerArgumentToInvocationArguments(SwaggerInvocation swaggerInvocation, + Map swaggerArguments, + Map invocationArguments) { + throw new IllegalStateException("not expected"); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentSame.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentSame.java new file mode 100644 index 00000000000..2768590a1de --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentSame.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.swagger.invocation.arguments.consumer; + +import java.util.Map; + +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; + +public final class ConsumerArgumentSame extends ConsumerArgumentMapper { + private final String invocationArgumentName; + + private final String swaggerArgumentName; + + public ConsumerArgumentSame(String invocationArgumentName, String swaggerArgumentName) { + this.invocationArgumentName = invocationArgumentName; + this.swaggerArgumentName = swaggerArgumentName; + } + + public boolean isSameMapping() { + return this.invocationArgumentName.equals(this.swaggerArgumentName); + } + + @Override + public void invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, + Map swaggerArguments, + Map invocationArguments) { + swaggerArguments.put(swaggerArgumentName, invocationArguments.get(invocationArgumentName)); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentToBodyField.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentToBodyField.java new file mode 100644 index 00000000000..1fd5fda66ed --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentToBodyField.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.swagger.invocation.arguments.consumer; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; + +/** + *
+ * Typical scene of transparent RPC
+ * all parameters of consumer method wrapped to a bean in contract
+ * 
+ */ +public final class ConsumerArgumentToBodyField extends ConsumerArgumentMapper { + private final String invocationArgumentName; + + private final String parameterName; + + private final String swaggerArgumentName; + + public ConsumerArgumentToBodyField(String invocationArgumentName, + String swaggerArgumentName, String parameterName) { + this.invocationArgumentName = invocationArgumentName; + this.parameterName = parameterName; + this.swaggerArgumentName = swaggerArgumentName; + } + + @SuppressWarnings("unchecked") + @Override + public void invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, + Map swaggerArguments, + Map invocationArguments) { + Object consumerArgument = invocationArguments.get(invocationArgumentName); + swaggerArguments.computeIfAbsent(swaggerArgumentName, k -> new LinkedHashMap()); + if (consumerArgument != null) { + ((Map) swaggerArguments.get(swaggerArgumentName)).put(parameterName, consumerArgument); + } + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapperCreator.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapperCreator.java new file mode 100644 index 00000000000..84c7a6fa926 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapperCreator.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.invocation.arguments.consumer; + +import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.collectParameterName; + +import java.lang.reflect.Method; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; +import org.apache.servicecomb.swagger.invocation.arguments.AbstractArgumentsMapperCreator; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; + +public class ConsumerArgumentsMapperCreator extends AbstractArgumentsMapperCreator { + private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerArgumentsMapperCreator.class); + + private int unknownConsumerParams = 0; + + public ConsumerArgumentsMapperCreator(SerializationConfig serializationConfig, + Map, ContextArgumentMapperFactory> contextFactories, Class consumerClass, + Method consumerMethod, SwaggerOperation swaggerOperation) { + super(serializationConfig, contextFactories, consumerClass, consumerMethod, swaggerOperation); + } + + private boolean isAllSameMapper() { + for (ArgumentMapper mapper : mappers) { + if (mapper instanceof ConsumerArgumentSame && ((ConsumerArgumentSame) mapper).isSameMapping()) { + continue; + } + + return false; + } + + return true; + } + + public ArgumentsMapper createArgumentsMapper() { + doCreateArgumentsMapper(); + + // if all mappers are SameMapper, then no need any mapper + if (unknownConsumerParams == 0 + && mappers.size() == swaggerOperation.parameterCount() + && isAllSameMapper()) { + return new ArgumentsMapperDirectReuse(); + } + + return new ArgumentsMapperCommon(mappers); + } + + @Override + protected void processUnknownParameter(int providerParamIdx, java.lang.reflect.Parameter providerParameter, + String parameterName) { + LOGGER.warn("Consumer parameter({}) is not exist in contract, method={}:{}.", + parameterName, providerMethod.getDeclaringClass().getName(), providerMethod.getName()); + unknownConsumerParams++; + } + + @Override + protected void processPendingSwaggerParameter(Parameter parameter) { + + } + + @Override + protected void processPendingBodyParameter(RequestBody parameter) { + + } + + @Override + protected ArgumentMapper createKnownParameterMapper(int providerParamIdx, String invocationArgumentName) { + return new ConsumerArgumentSame(this.providerMethod.getParameters()[providerParamIdx].getName(), + invocationArgumentName); + } + + @Override + protected ArgumentMapper createSwaggerBodyFieldMapper(int consumerParamIdx, String parameterName) { + return new ConsumerArgumentToBodyField(this.providerMethod.getParameters()[consumerParamIdx].getName(), + (String) this.bodyParameter.getExtensions().get(SwaggerConst.EXT_BODY_NAME), parameterName); + } + + @Override + protected boolean processBeanParameter(int consumerParamIdx, java.lang.reflect.Parameter consumerParameter) { + JavaType consumerType = TypeFactory.defaultInstance().constructType(consumerParameter.getParameterizedType()); + if (!SwaggerUtils.isBean(consumerType)) { + return false; + } + boolean result = false; + ConsumerBeanParamMapper mapper = new ConsumerBeanParamMapper( + this.providerMethod.getParameters()[consumerParamIdx].getName()); + for (BeanPropertyDefinition propertyDefinition : serializationConfig.introspect(consumerType).findProperties()) { + String parameterName = collectParameterName(providerMethod, propertyDefinition); + if (!parameterNameExistsInSwagger(parameterName)) { + continue; + } + + mapper.addField(parameterName, LambdaMetafactoryUtils.createObjectGetter(propertyDefinition)); + processedSwaggerParameters.add(parameterName); + result = true; + } + mappers.add(mapper); + return result; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerBeanParamMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerBeanParamMapper.java new file mode 100644 index 00000000000..ae5468ce5b2 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerBeanParamMapper.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.swagger.invocation.arguments.consumer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.utils.bean.Getter; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; + +/** + *
+ * consumer: void add(QueryWrapper query)
+ *           class QueryWrapper {
+ *             int x;
+ *             int y;
+ *           }
+ * contract; void add(int x, int y)
+ * 
+ */ +public final class ConsumerBeanParamMapper extends ConsumerArgumentMapper { + private static class FieldMeta { + String swaggerArgumentName; + + Getter getter; + + public FieldMeta(String swaggerArgumentName, Getter getter) { + this.swaggerArgumentName = swaggerArgumentName; + this.getter = getter; + } + } + + private final String invocationArgumentName; + + private final List fields = new ArrayList<>(); + + public ConsumerBeanParamMapper(String invocationArgumentName) { + this.invocationArgumentName = invocationArgumentName; + } + + public void addField(String invocationArgumentName, Getter getter) { + fields.add(new FieldMeta(invocationArgumentName, getter)); + } + + @Override + public void invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, + Map swaggerArguments, + Map invocationArguments) { + Object consumerArgument = invocationArguments.get(invocationArgumentName); + if (consumerArgument == null) { + return; + } + for (FieldMeta fieldMeta : fields) { + swaggerArguments.put(fieldMeta.swaggerArgumentName, fieldMeta.getter.get(consumerArgument)); + } + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerContextArgumentMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerContextArgumentMapperFactory.java new file mode 100644 index 00000000000..dd472db3b77 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerContextArgumentMapperFactory.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.swagger.invocation.arguments.consumer; + +import org.apache.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; + +public interface ConsumerContextArgumentMapperFactory extends ContextArgumentMapperFactory { +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapper.java new file mode 100644 index 00000000000..df324635bb2 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapper.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.swagger.invocation.arguments.consumer; + +import java.util.Map; + +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + +public class ConsumerInvocationContextMapper extends ConsumerArgumentMapper { + protected String invocationArgumentName; + + public ConsumerInvocationContextMapper(String invocationArgumentName) { + this.invocationArgumentName = invocationArgumentName; + } + + @Override + public void invocationArgumentToSwaggerArguments(SwaggerInvocation invocation, + Map swaggerArguments, + Map invocationArguments) { + InvocationContext context = (InvocationContext) invocationArguments.get(invocationArgumentName); + invocation.addContext(context.getContext()); + invocation.addLocalContext(context.getLocalContext()); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapperFactory.java new file mode 100644 index 00000000000..0de0f2e9595 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerInvocationContextMapperFactory.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.swagger.invocation.arguments.consumer; + +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + +public class ConsumerInvocationContextMapperFactory implements ConsumerContextArgumentMapperFactory { + @Override + public Class getContextClass() { + return InvocationContext.class; + } + + @Override + public ArgumentMapper create(String invocationArgumentName, String swaggerArgumentName) { + return new ConsumerInvocationContextMapper(invocationArgumentName); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/AbstractProducerContextArgMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/AbstractProducerContextArgMapper.java new file mode 100644 index 00000000000..83ca5b06185 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/AbstractProducerContextArgMapper.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.swagger.invocation.arguments.producer; + +import java.util.Map; + +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; + +public abstract class AbstractProducerContextArgMapper extends ProducerArgumentMapper { + protected String invocationArgumentName; + + protected String swaggerArgumentName; + + public AbstractProducerContextArgMapper(String invocationArgumentName, String swaggerArgumentName) { + this.invocationArgumentName = invocationArgumentName; + this.swaggerArgumentName = swaggerArgumentName; + } + + @Override + public void swaggerArgumentToInvocationArguments(SwaggerInvocation invocation, + Map swaggerArguments, Map invocationArguments) { + Object producerArg = createContextArg(invocation); + invocationArguments.put(this.invocationArgumentName, producerArg); + } + + public abstract Object createContextArg(SwaggerInvocation invocation); +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentMapper.java new file mode 100644 index 00000000000..21eae4ea842 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentMapper.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.swagger.invocation.arguments.producer; + +import java.util.Map; + +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; + +public abstract class ProducerArgumentMapper implements ArgumentMapper { + public void invocationArgumentToSwaggerArguments(SwaggerInvocation swaggerInvocation, + Map swaggerArguments, + Map invocationArguments) { + throw new IllegalStateException("not expected"); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentSame.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentSame.java new file mode 100644 index 00000000000..f22456a83e5 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentSame.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.swagger.invocation.arguments.producer; + +import java.util.Map; + +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; + +public class ProducerArgumentSame extends ProducerArgumentMapper { + protected String invocationArgumentName; + + protected String swaggerArgumentName; + + public ProducerArgumentSame(String invocationArgumentName, String swaggerArgumentName) { + this.invocationArgumentName = invocationArgumentName; + this.swaggerArgumentName = swaggerArgumentName; + } + + @Override + public void swaggerArgumentToInvocationArguments(SwaggerInvocation invocation, + Map swaggerArguments, Map invocationArguments) { + invocationArguments.put(invocationArgumentName, swaggerArguments.get(swaggerArgumentName)); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapper.java new file mode 100644 index 00000000000..91339cca0e9 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapper.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.swagger.invocation.arguments.producer; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; + +/** + * map swagger arguments to producer arguments + */ +public class ProducerArgumentsMapper implements ArgumentsMapper { + private final List producerArgMapperList; + + public ProducerArgumentsMapper(List producerArgMapperList) { + this.producerArgMapperList = producerArgMapperList; + } + + @Override + public Map swaggerArgumentToInvocationArguments(SwaggerInvocation invocation, + Map swaggerArguments) { + Map invocationArguments = new HashMap<>(swaggerArguments.size()); + for (ArgumentMapper argMapper : producerArgMapperList) { + argMapper.swaggerArgumentToInvocationArguments(invocation, swaggerArguments, invocationArguments); + } + + return invocationArguments; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperCreator.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperCreator.java new file mode 100644 index 00000000000..9aea4bec37d --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperCreator.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.invocation.arguments.producer; + +import static org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils.collectParameterName; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.utils.LambdaMetafactoryUtils; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; +import org.apache.servicecomb.swagger.invocation.arguments.AbstractArgumentsMapperCreator; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; +import org.apache.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.google.common.reflect.TypeToken; + +import io.swagger.v3.oas.models.parameters.RequestBody; + +@SuppressWarnings("unchecked") +public class ProducerArgumentsMapperCreator extends AbstractArgumentsMapperCreator { + // swagger parameter types relate to producer + // because features of @BeanParam/query, and rpc mode parameter wrapper + // types is not always equals to producerMethod parameter types directly + private final Map swaggerParameterTypes; + + public ProducerArgumentsMapperCreator(SerializationConfig serializationConfig, + Map, ContextArgumentMapperFactory> contextFactories, Class producerClass, + Method producerMethod, SwaggerOperation swaggerOperation) { + super(serializationConfig, contextFactories, producerClass, producerMethod, swaggerOperation); + + swaggerParameterTypes = new HashMap<>(); + } + + public Map getSwaggerParameterTypes() { + return swaggerParameterTypes; + } + + public ProducerArgumentsMapper createArgumentsMapper() { + doCreateArgumentsMapper(); + return new ProducerArgumentsMapper(mappers); + } + + @Override + protected void processUnknownParameter(int providerParamIdx, java.lang.reflect.Parameter providerParameter, + String parameterName) { + throw new IllegalStateException(String + .format("failed to find producer parameter in contract, method=%s:%s, parameter name=%s.", + providerMethod.getDeclaringClass().getSimpleName(), providerMethod.getName(), parameterName)); + } + + @Override + protected void processPendingSwaggerParameter(io.swagger.v3.oas.models.parameters.Parameter parameter) { + swaggerParameterTypes.put(parameter.getName(), Object.class); + } + + @Override + protected void processPendingBodyParameter(RequestBody bodyParameter) { + if (bodyParameter.getContent().get(SwaggerConst.DEFAULT_MEDIA_TYPE) != null + && !processedSwaggerParameters.contains( + (String) bodyParameter.getExtensions().get(SwaggerConst.EXT_BODY_NAME))) { + swaggerParameterTypes.put((String) bodyParameter + .getExtensions().get(SwaggerConst.EXT_BODY_NAME), Object.class); + } + if (bodyParameter.getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null) { + bodyParameter.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties().forEach((k, v) -> { + if (!processedSwaggerParameters.contains((String) k)) { + swaggerParameterTypes.put((String) k, Object.class); + } + }); + } + } + + @Override + protected ArgumentMapper createKnownParameterMapper(int providerParamIdx, String invocationArgumentName) { + Type providerType = TypeToken.of(providerClass) + .resolveType(providerMethod.getGenericParameterTypes()[providerParamIdx]) + .getType(); + swaggerParameterTypes + .put(invocationArgumentName, providerType); + return new ProducerArgumentSame(providerMethod.getParameters()[providerParamIdx].getName(), invocationArgumentName); + } + + @Override + protected ArgumentMapper createSwaggerBodyFieldMapper(int producerParamIdx, String parameterName) { + String swaggerArgumentName = (String) this.bodyParameter.getExtensions().get(SwaggerConst.EXT_BODY_NAME); + swaggerParameterTypes.put(swaggerArgumentName, Object.class); + Type parameterType = TypeToken.of(providerClass) + .resolveType(providerMethod.getGenericParameterTypes()[producerParamIdx]) + .getType(); + return new SwaggerBodyFieldToProducerArgument(providerMethod.getParameters()[producerParamIdx].getName(), + swaggerArgumentName, + parameterName, parameterType); + } + + @Override + protected boolean processBeanParameter(int producerParamIdx, Parameter producerParameter) { + JavaType providerType = TypeFactory.defaultInstance().constructType(producerParameter.getParameterizedType()); + if (!SwaggerUtils.isBean(providerType)) { + return false; + } + ProducerBeanParamMapper mapper = new ProducerBeanParamMapper( + providerMethod.getParameters()[producerParamIdx].getName(), producerParameter.getType()); + boolean result = false; + for (BeanPropertyDefinition propertyDefinition : serializationConfig.introspect(providerType) + .findProperties()) { + String parameterName = collectParameterName(providerMethod, propertyDefinition); + + if (!parameterNameExistsInSwagger(parameterName)) { + continue; + } + + swaggerParameterTypes.put(parameterName, propertyDefinition.getPrimaryType()); + mapper.addField(parameterName, LambdaMetafactoryUtils.createObjectSetter(propertyDefinition)); + processedSwaggerParameters.add(parameterName); + result = true; + } + mappers.add(mapper); + return result; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerBeanParamMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerBeanParamMapper.java new file mode 100644 index 00000000000..3b4778f778d --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerBeanParamMapper.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.swagger.invocation.arguments.producer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.utils.bean.Setter; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; + +public class ProducerBeanParamMapper extends ProducerArgumentMapper { + private static class FieldMeta { + String swaggerParameterName; + + Setter setter; + + public FieldMeta(String swaggerParameterName, Setter setter) { + this.swaggerParameterName = swaggerParameterName; + this.setter = setter; + } + } + + protected String invocationArgumentName; + + private final Class producerParamType; + + private final List fields = new ArrayList<>(); + + public ProducerBeanParamMapper(String invocationArgumentName, Class producerParamType) { + this.invocationArgumentName = invocationArgumentName; + this.producerParamType = producerParamType; + } + + public void addField(String swaggerParameterName, Setter setter) { + fields.add(new FieldMeta(swaggerParameterName, setter)); + } + + @Override + public void swaggerArgumentToInvocationArguments(SwaggerInvocation invocation, + Map swaggerArguments, Map invocationArguments) { + try { + Object paramInstance = producerParamType.getDeclaredConstructor().newInstance(); + invocationArguments.put(invocationArgumentName, paramInstance); + + for (FieldMeta fieldMeta : fields) { + Object value = swaggerArguments.get(fieldMeta.swaggerParameterName); + if (value != null) { + // can not set primitive data + fieldMeta.setter.set(paramInstance, value); + } + } + } catch (Throwable e) { + throw new IllegalStateException("failed to map bean param.", e); + } + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerContextArgumentMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerContextArgumentMapperFactory.java new file mode 100644 index 00000000000..64e569fc9ad --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerContextArgumentMapperFactory.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.swagger.invocation.arguments.producer; + +import org.apache.servicecomb.swagger.invocation.arguments.ContextArgumentMapperFactory; + +public interface ProducerContextArgumentMapperFactory extends ContextArgumentMapperFactory { +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapper.java new file mode 100644 index 00000000000..5e1454cad1e --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapper.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.swagger.invocation.arguments.producer; + +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; + +public class ProducerInvocationContextMapper extends AbstractProducerContextArgMapper { + public ProducerInvocationContextMapper(String invocationArgumentName, String swaggerArgumentName) { + super(invocationArgumentName, swaggerArgumentName); + } + + @Override + public Object createContextArg(SwaggerInvocation invocation) { + return invocation; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapperFactory.java new file mode 100644 index 00000000000..1e99e896d15 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerInvocationContextMapperFactory.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.swagger.invocation.arguments.producer; + +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + +public class ProducerInvocationContextMapperFactory implements ProducerContextArgumentMapperFactory { + @Override + public Class getContextClass() { + return InvocationContext.class; + } + + @Override + public ArgumentMapper create(String invocationArgumentName, String swaggerArgumentName) { + return new ProducerInvocationContextMapper(invocationArgumentName, swaggerArgumentName); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/SwaggerBodyFieldToProducerArgument.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/SwaggerBodyFieldToProducerArgument.java new file mode 100644 index 00000000000..254c47949dc --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/SwaggerBodyFieldToProducerArgument.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.swagger.invocation.arguments.producer; + +import java.lang.reflect.Type; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; + +public class SwaggerBodyFieldToProducerArgument extends ProducerArgumentMapper { + public static ObjectMapper mapper = JsonUtils.OBJ_MAPPER; + + private final String invocationArgumentName; + + private final String parameterName; + + private final JavaType producerParamType; + + private final String swaggerArgumentName; + + public SwaggerBodyFieldToProducerArgument(String invocationArgumentName, + String swaggerArgumentName, String parameterName, Type producerParamType) { + this.invocationArgumentName = invocationArgumentName; + this.parameterName = parameterName; + this.producerParamType = TypeFactory.defaultInstance().constructType(producerParamType); + this.swaggerArgumentName = swaggerArgumentName; + } + + @Override + @SuppressWarnings("unchecked") + public void swaggerArgumentToInvocationArguments(SwaggerInvocation invocation, + Map swaggerArguments, Map invocationArguments) { + Map body = (Map) swaggerArguments.get(swaggerArgumentName); + invocationArguments.put(invocationArgumentName, + body == null ? null : mapper.convertValue(body.get(parameterName), producerParamType)); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/ContextUtils.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/ContextUtils.java new file mode 100644 index 00000000000..c0b20d0d9ea --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/ContextUtils.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.swagger.invocation.context; + +import java.util.concurrent.CompletableFuture; + +/** + * 传递调用过程的上下文数据 + */ +public final class ContextUtils { + private ContextUtils() { + } + + private static final ThreadLocal contextMgr = new ThreadLocal<>(); + + public static InvocationContext getInvocationContext() { + return contextMgr.get(); + } + + public static InvocationContext getAndRemoveInvocationContext() { + InvocationContext context = contextMgr.get(); + if (context != null) { + contextMgr.remove(); + } + return context; + } + + public static void setInvocationContext(InvocationContext invocationContext) { + contextMgr.set(invocationContext); + } + + public static void removeInvocationContext() { + contextMgr.remove(); + } + + /** + * + * @param future must be InvocationContextCompletableFuture, that is returned from consumer api + * @return + */ + public static InvocationContext getFromCompletableFuture(CompletableFuture future) { + if (future instanceof InvocationContextCompletableFuture) { + return ((InvocationContextCompletableFuture) future).getContext(); + } + + return null; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/HttpStatus.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/HttpStatus.java new file mode 100644 index 00000000000..fe6a74443e8 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/HttpStatus.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.swagger.invocation.context; + +public class HttpStatus extends org.apache.servicecomb.foundation.common.http.HttpStatus { + public HttpStatus(int statusCode, String reasonPhrase) { + super(statusCode, reasonPhrase); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/HttpStatusManager.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/HttpStatusManager.java new file mode 100644 index 00000000000..db1f920eaf9 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/HttpStatusManager.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.swagger.invocation.context; + +public class HttpStatusManager extends org.apache.servicecomb.foundation.common.http.HttpStatusManager { +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/InvocationContext.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/InvocationContext.java new file mode 100644 index 00000000000..8f7da8d0bd3 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/InvocationContext.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.invocation.context; + +import java.util.HashMap; +import java.util.Map; + +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.StatusType; + +/** + * InvocationContext is used to pass data between microservices or in microservice different layer. + */ +public class InvocationContext { + private static final HttpStatusManager statusMgr = new HttpStatusManager(); + + protected StatusType httpStatus; + + protected final Map context = new HashMap<>(); + + protected final Map localContext = new HashMap<>(); + + protected TransportContext transportContext; + + public InvocationContext() { + httpStatus = Status.OK; + } + + public Map getContext() { + return context; + } + + public void setContext(Map context) { + this.context.clear(); + this.addContext(context); + } + + public void addContext(String key, String value) { + context.put(key, value); + } + + public String getContext(String key) { + return context.get(key); + } + + public void addContext(InvocationContext otherContext) { + addContext(otherContext.getContext()); + } + + public void addContext(Map otherContext) { + if (otherContext == null) { + return; + } + + context.putAll(otherContext); + } + + public void mergeContext(InvocationContext otherContext) { + mergeContext(otherContext.getContext()); + } + + public void mergeContext(Map otherContext) { + if (otherContext == null) { + return; + } + context.putAll(otherContext); + } + + public Map getLocalContext() { + return localContext; + } + + public void setLocalContext(Map localContext) { + this.localContext.clear(); + this.addLocalContext(localContext); + } + + public void addLocalContext(String key, Object value) { + localContext.put(key, value); + } + + @SuppressWarnings("unchecked") + public T getLocalContext(String key) { + return (T) localContext.get(key); + } + + public void addLocalContext(Map otherContext) { + if (otherContext == null) { + return; + } + + localContext.putAll(otherContext); + } + + public StatusType getStatus() { + return httpStatus; + } + + public void setStatus(StatusType status) { + this.httpStatus = status; + } + + public void setStatus(int statusCode, String reason) { + httpStatus = new HttpStatus(statusCode, reason); + } + + public void setStatus(int statusCode) { + this.httpStatus = statusMgr.getOrCreateByStatusCode(statusCode); + } + + @SuppressWarnings("unchecked") + public T getTransportContext() { + return (T) transportContext; + } + + public void setTransportContext(TransportContext transportContext) { + this.transportContext = transportContext; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/InvocationContextCompletableFuture.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/InvocationContextCompletableFuture.java new file mode 100644 index 00000000000..363025afae7 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/InvocationContextCompletableFuture.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.swagger.invocation.context; + +import java.util.concurrent.CompletableFuture; + +public class InvocationContextCompletableFuture extends CompletableFuture { + private final InvocationContext context; + + public InvocationContextCompletableFuture(InvocationContext context) { + this.context = context; + } + + public InvocationContext getContext() { + return context; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/TransportContext.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/TransportContext.java new file mode 100644 index 00000000000..7991e6b5fa3 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/TransportContext.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.swagger.invocation.context; + +/** + * currently, only for server side + */ +public interface TransportContext { +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/VertxTransportContext.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/VertxTransportContext.java new file mode 100644 index 00000000000..103af476331 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/context/VertxTransportContext.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.swagger.invocation.context; + +import io.vertx.core.Context; + +public interface VertxTransportContext extends TransportContext { + Context getVertxContext(); +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/Converter.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/Converter.java new file mode 100644 index 00000000000..f3b27538409 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/Converter.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.swagger.invocation.converter; + +import java.lang.reflect.Type; + +public interface Converter { + Type getSrcType(); + + Type getTargetType(); + + Object convert(Object value); +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/BytesToPartConverter.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/BytesToPartConverter.java new file mode 100644 index 00000000000..affccb1e104 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/BytesToPartConverter.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.swagger.invocation.converter.impl.part; + +import java.io.ByteArrayInputStream; +import java.lang.reflect.Type; + +import jakarta.servlet.http.Part; + +import org.apache.servicecomb.foundation.common.part.InputStreamPart; +import org.apache.servicecomb.swagger.invocation.converter.Converter; + +public class BytesToPartConverter implements Converter { + @Override + public Type getSrcType() { + return byte[].class; + } + + @Override + public Type getTargetType() { + return Part.class; + } + + @Override + public Object convert(Object value) { + // not set name, because not easy to get parameter name in this place + // org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl not depend on the name + return new InputStreamPart(null, new ByteArrayInputStream((byte[]) value)); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/FileToPartConverter.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/FileToPartConverter.java new file mode 100644 index 00000000000..66964377cbb --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/FileToPartConverter.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.swagger.invocation.converter.impl.part; + +import java.io.File; +import java.lang.reflect.Type; + +import jakarta.servlet.http.Part; + +import org.apache.servicecomb.foundation.common.part.FilePart; +import org.apache.servicecomb.swagger.invocation.converter.Converter; + +public class FileToPartConverter implements Converter { + @Override + public Type getSrcType() { + return File.class; + } + + @Override + public Type getTargetType() { + return Part.class; + } + + @Override + public Object convert(Object value) { + // not set name, because not easy to get parameter name in this place + // org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl not depend on the name + return new FilePart(null, (File) value); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/InputStreamToPartConverter.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/InputStreamToPartConverter.java new file mode 100644 index 00000000000..81216cadda0 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/InputStreamToPartConverter.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.swagger.invocation.converter.impl.part; + +import java.io.InputStream; +import java.lang.reflect.Type; + +import jakarta.servlet.http.Part; + +import org.apache.servicecomb.foundation.common.part.InputStreamPart; +import org.apache.servicecomb.swagger.invocation.converter.Converter; + +public class InputStreamToPartConverter implements Converter { + @Override + public Type getSrcType() { + return InputStream.class; + } + + @Override + public Type getTargetType() { + return Part.class; + } + + @Override + public Object convert(Object value) { + // not set name, because not easy to get parameter name in this place + // org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl not depend on the name + return new InputStreamPart(null, (InputStream) value); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartArrayConverter.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartArrayConverter.java new file mode 100644 index 00000000000..bbdb244f58e --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartArrayConverter.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.swagger.invocation.converter.impl.part; + +import java.lang.reflect.Type; +import java.util.List; + +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; +import org.apache.servicecomb.swagger.invocation.converter.Converter; + +import jakarta.servlet.http.Part; + +public class PartListToPartArrayConverter implements Converter { + @Override + public Type getSrcType() { + return ParameterizedTypeUtil.make(List.class, Part.class); + } + + @Override + public Type getTargetType() { + return Part[].class; + } + + @Override + public Object convert(Object value) { + if (value == null) { + return null; + } + + @SuppressWarnings("unchecked") + List partList = (List) value; + return partList.toArray(new Part[0]); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartListConverter.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartListConverter.java new file mode 100644 index 00000000000..5c2ee3284ac --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartListConverter.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.swagger.invocation.converter.impl.part; + +import java.lang.reflect.Type; +import java.util.List; + +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; +import org.apache.servicecomb.swagger.invocation.converter.Converter; + +import jakarta.servlet.http.Part; + +public class PartListToPartListConverter implements Converter { + @Override + public Type getSrcType() { + return ParameterizedTypeUtil.make(List.class, Part.class); + } + + @Override + public Type getTargetType() { + return ParameterizedTypeUtil.make(List.class, Part.class); + } + + @Override + public Object convert(Object value) { + return value; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartToPartConverter.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartToPartConverter.java new file mode 100644 index 00000000000..912f18852c7 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartToPartConverter.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.swagger.invocation.converter.impl.part; + +import java.lang.reflect.Type; + +import jakarta.servlet.http.Part; + +import org.apache.servicecomb.swagger.invocation.converter.Converter; + +public class PartToPartConverter implements Converter { + @Override + public Type getSrcType() { + return Part.class; + } + + @Override + public Type getTargetType() { + return Part.class; + } + + @Override + public Object convert(Object value) { + return value; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/ResourceToPartConverter.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/ResourceToPartConverter.java new file mode 100644 index 00000000000..d85d1bf64c0 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/ResourceToPartConverter.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.swagger.invocation.converter.impl.part; + +import java.lang.reflect.Type; + +import jakarta.servlet.http.Part; + +import org.apache.servicecomb.foundation.common.part.ResourcePart; +import org.apache.servicecomb.swagger.invocation.converter.Converter; +import org.springframework.core.io.Resource; + +public class ResourceToPartConverter implements Converter { + @Override + public Type getSrcType() { + return Resource.class; + } + + @Override + public Type getTargetType() { + return Part.class; + } + + @Override + public Object convert(Object value) { + // not set name, because not easy to get parameter name in this place + // org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl not depend on the name + return new ResourcePart(null, (Resource) value); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/CommonExceptionData.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/CommonExceptionData.java new file mode 100644 index 00000000000..fdec294eca6 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/CommonExceptionData.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.invocation.exception; + +import org.apache.servicecomb.foundation.common.DynamicObject; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; + +/** + * 将普通异常转换为InvocationException时,保存message信息 + */ +public class CommonExceptionData extends DynamicObject { + @JsonInclude(Include.NON_NULL) + private String code; + + private String message; + + public CommonExceptionData() { + } + + public CommonExceptionData(String message) { + this.message = message; + } + + public CommonExceptionData(String code, String message) { + this.code = code; + this.message = message; + } + + public String getCode() { + return code; + } + + public CommonExceptionData setCode(String code) { + this.code = code; + return this; + } + + public String getMessage() { + return message; + } + + public CommonExceptionData setMessage(String message) { + this.message = message; + return this; + } + + @Override + public String toString() { + if (code == null) { + return "CommonExceptionData [message=" + message + "]"; + } + + return "CommonExceptionData{" + + "code='" + code + '\'' + + ", message='" + message + '\'' + + ", dynamic=" + dynamic + + '}'; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionFactory.java new file mode 100644 index 00000000000..a4050b2f7ec --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionFactory.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.swagger.invocation.exception; + +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; + +import org.apache.servicecomb.swagger.invocation.context.HttpStatus; + +import jakarta.ws.rs.core.Response.StatusType; + +public final class ExceptionFactory { + // cse内置的错误 + // 考虑到springmvc不支持自定义http错误码,所以只能使用“标准”错误码 + // 510是ws.rs中未定义,在springmvc中定义为not extended + // 在我们的流程中不可能用到这个错误定义,所以将之转义为cse的provider内部错误 + // private static final int PROVIDER_INNER_STATUS_CODE = 510; + public static final int PRODUCER_INNER_STATUS_CODE = 590; + + public static final String PRODUCER_INNER_REASON_PHRASE = "Unexpected producer error, please check logs for details"; + + public static final StatusType PRODUCER_INNER_STATUS = + new HttpStatus(PRODUCER_INNER_STATUS_CODE, PRODUCER_INNER_REASON_PHRASE); + + // 420是ws.rs中未定义,在springmvc中定义为Method Failure + // 在我们的流程中不可能用到这个错误定义,所以将之转义为cse的consumer内部错误 + // private static final int CONSUMER_INNER_STATUS_CODE = 420; + public static final int CONSUMER_INNER_STATUS_CODE = 490; + + public static final String CONSUMER_INNER_REASON_PHRASE = "Unexpected consumer error, please check logs for details"; + + public static final StatusType CONSUMER_INNER_STATUS = + new HttpStatus(CONSUMER_INNER_STATUS_CODE, CONSUMER_INNER_REASON_PHRASE); + + private ExceptionFactory() { + } + + public static InvocationException create(StatusType status, + Object exceptionOrErrorData) { + if (exceptionOrErrorData instanceof InvocationException) { + return (InvocationException) exceptionOrErrorData; + } + + return doCreate(status, exceptionOrErrorData); + } + + public static InvocationException createConsumerException(Object errorData) { + return create(CONSUMER_INNER_STATUS, errorData); + } + + public static InvocationException createProducerException(Object errorData) { + return create(PRODUCER_INNER_STATUS, errorData); + } + + private static InvocationException doCreate(StatusType status, + Object errorData) { + return new InvocationException(status, errorData); + } + + private static InvocationException doCreate(int statusCode, String reasonPhrase, CommonExceptionData data, + Throwable e) { + return new InvocationException(statusCode, reasonPhrase, data, e); + } + + public static InvocationException convertConsumerException(Throwable e) { + return convertException(CONSUMER_INNER_STATUS_CODE, + CONSUMER_INNER_REASON_PHRASE, + e, + CONSUMER_INNER_REASON_PHRASE); + } + + public static InvocationException convertConsumerException(Throwable e, String errorMsg) { + return convertException(CONSUMER_INNER_STATUS_CODE, + CONSUMER_INNER_REASON_PHRASE, + e, + errorMsg); + } + + public static InvocationException convertProducerException(Throwable e) { + return convertException(PRODUCER_INNER_STATUS_CODE, + PRODUCER_INNER_REASON_PHRASE, + e, + PRODUCER_INNER_REASON_PHRASE); + } + + public static InvocationException convertProducerException(Throwable e, String errorMsg) { + return convertException(PRODUCER_INNER_STATUS_CODE, + PRODUCER_INNER_REASON_PHRASE, + e, + errorMsg); + } + + // 如果e中取不出可以直接返回的InvocationException + // 则需要创建新的InvocationException实例,此时不允许使用e.getMessage,因为可能会涉及关键信息被传给远端 + // 新创建的InvocationException,会使用errorMsg来构建CommonExceptionData + private static InvocationException convertException(int statusCode, String reasonPhrase, Throwable e, + String errorMsg) { + e = unwrap(e); + + if (e instanceof InvocationException) { + return (InvocationException) e; + } + + CommonExceptionData data = new CommonExceptionData(errorMsg); + return doCreate(statusCode, reasonPhrase, data, e); + } + + public static Throwable unwrapIncludeInvocationException(Throwable throwable) { + throwable = unwrap(throwable); + if (throwable instanceof InvocationException) { + throwable = throwable.getCause(); + } + return throwable; + } + + @SuppressWarnings("unchecked") + public static T unwrap(Throwable throwable) { + if (throwable instanceof InvocationTargetException) { + throwable = ((InvocationTargetException) throwable).getTargetException(); + } + if (throwable instanceof CompletionException) { + throwable = throwable.getCause(); + } + if (throwable instanceof ExecutionException) { + throwable = throwable.getCause(); + } + return (T) throwable; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/InvocationException.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/InvocationException.java new file mode 100644 index 00000000000..bd6ba5cc194 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/InvocationException.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.swagger.invocation.exception; + +import jakarta.ws.rs.core.Response.StatusType; + +import org.apache.servicecomb.swagger.invocation.context.HttpStatus; + +/** + * 业务在provider端,想返回在swagger中定义好的错误 + * 通过抛出本类型的异常来实现 + * 如果不是本类型的异常,则强制认为是PRODUCER_INNER_STATUS_CODE错误 + */ +public class InvocationException extends RuntimeException { + private static final long serialVersionUID = 8027482777502649656L; + + /** + * http header中的statusCode + * 不直接使用Status类型,是为了支持业务自定义code + */ + private final StatusType status; + + private final Object errorData; + + public InvocationException(StatusType status, Object errorData) { + this.status = status; + this.errorData = errorData; + } + + public InvocationException(StatusType status, String msg) { + this(status, new CommonExceptionData(msg)); + } + + public InvocationException(int statusCode, String reasonPhrase, Object errorData, Throwable cause) { + this(new HttpStatus(statusCode, reasonPhrase), errorData, cause); + } + + public InvocationException(int statusCode, String reasonPhrase, Object errorData) { + this(new HttpStatus(statusCode, reasonPhrase), errorData); + } + + public InvocationException(StatusType status, Object errorData, Throwable cause) { + super(cause); + this.status = status; + this.errorData = errorData; + } + + public InvocationException(StatusType status, String code, String msg) { + this(status, new CommonExceptionData(code, msg)); + } + + public InvocationException(StatusType status, String code, String msg, Throwable cause) { + this(status, new CommonExceptionData(code, msg), cause); + } + + public StatusType getStatus() { + return status; + } + + public int getStatusCode() { + return status.getStatusCode(); + } + + public String getReasonPhrase() { + return status.getReasonPhrase(); + } + + public Object getErrorData() { + return errorData; + } + + @SuppressWarnings("unchecked") + public T getError() { + return (T) errorData; + } + + @Override + public String getMessage() { + return this.toString(); + } + + @Override + public String toString() { + return "InvocationException: code=" + getStatusCode() + + ";msg=" + getErrorData(); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/generator/InvocationContextProcessor.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/generator/InvocationContextProcessor.java new file mode 100644 index 00000000000..13e93b23dc6 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/generator/InvocationContextProcessor.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.swagger.invocation.generator; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.SwaggerContextRegister; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + +public class InvocationContextProcessor implements SwaggerContextRegister { + @Override + public Type getContextType() { + return InvocationContext.class; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/generator/ScbResponseProcessor.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/generator/ScbResponseProcessor.java new file mode 100644 index 00000000000..f77062d4add --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/generator/ScbResponseProcessor.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.swagger.invocation.generator; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.generator.core.processor.response.DefaultResponseTypeProcessor; +import org.apache.servicecomb.swagger.invocation.Response; + +public class ScbResponseProcessor extends DefaultResponseTypeProcessor { + @Override + public Class getProcessType() { + return Response.class; + } + + @Override + public Type extractResponseType(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + Type genericResponseType) { + return null; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactories.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactories.java new file mode 100644 index 00000000000..761149ae24e --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactories.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.swagger.invocation.response; + +import java.lang.reflect.Type; +import java.util.List; + +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; + +public class ResponseMapperFactories { + private final List> factories; + + @SuppressWarnings("unchecked") + public ResponseMapperFactories(Class> factoryCls) { + factories = (List>) SPIServiceUtils.getSortedService(factoryCls); + } + + public MAPPER createResponseMapper(Type providerType) { + for (ResponseMapperFactory factory : factories) { + if (!factory.isMatch(providerType)) { + continue; + } + + return factory.createResponseMapper(this, providerType); + } + + throw new IllegalStateException( + String.format("can not find response mapper for %s, this should never happened.", + providerType.getTypeName())); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactory.java new file mode 100644 index 00000000000..917a3a835b9 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactory.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.swagger.invocation.response; + +import java.lang.reflect.Type; + +public interface ResponseMapperFactory { + default int getOrder() { + return 0; + } + + boolean isMatch(Type providerType); + + MAPPER createResponseMapper(ResponseMapperFactories factories, Type providerType); +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMetaMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMetaMapper.java new file mode 100644 index 00000000000..8de01afe630 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMetaMapper.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.swagger.invocation.response; + +import java.util.Map; + +import com.fasterxml.jackson.databind.JavaType; + +public interface ResponseMetaMapper { + default int getOrder() { + return 0; + } + + Map getMapper(); +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponsesMeta.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponsesMeta.java new file mode 100644 index 00000000000..aa9e75796c4 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponsesMeta.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.response; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.swagger.converter.ConverterMgr; +import org.apache.servicecomb.swagger.invocation.context.HttpStatus; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; + +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.type.SimpleType; + +import io.swagger.v3.core.util.ReflectionUtils; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.responses.ApiResponse; +import jakarta.ws.rs.core.Response.Status; + +/** + *
+ * two Scenes:
+ * 1.consumer interface + swagger
+ *   interface declare success response type
+ *   and can declare exceptions response type by annotations
+ *   consumer interface meta never changed and has high priority
+ *
+ *   so, merge them to be one ResponsesMeta
+ *
+ * 2.restTemplate + swagger
+ *   can only declare success response type
+ *   and not stable
+ *
+ *   so, will wrap swagger meta
+ *
+ *   note:
+ *   old version support: List<User> users = restTemplate.postForObject(...., List.class)
+ *     in fact, in this time, type is determined by swagger meta
+ *   new version:
+ *     1) if request response type is List/Set/Map, and there is element type defined, then use swagger type,
+ *     2) other times use request response type
+ *     3) compare to old version, add support of ParameterizedTypeReference
+ * 
+ */ +public class ResponsesMeta { + private static final JavaType COMMON_EXCEPTION_JAVA_TYPE = SimpleType.constructUnsafe(CommonExceptionData.class); + + private static final JavaType OBJECT_JAVA_TYPE = SimpleType.constructUnsafe(Object.class); + + private static final ResponseMetaMapper GLOBAL_DEFAULT_MAPPER = SPIServiceUtils + .getPriorityHighestService(ResponseMetaMapper.class); + + private final Map responseMap = new HashMap<>(); + + private JavaType defaultResponse; + + public void init(OpenAPI swagger, Operation operation) { + if (responseMap.isEmpty()) { + responseMap.put(Status.OK.getStatusCode(), OBJECT_JAVA_TYPE); + initGlobalDefaultMapper(); + } + + for (Entry entry : operation.getResponses().entrySet()) { + if (entry.getValue().getContent() == null || entry.getValue().getContent().size() == 0) { + continue; + } + + String mediaType = entry.getValue().getContent().keySet().iterator().next(); + + JavaType javaType = ConverterMgr.findJavaType(swagger, + SwaggerUtils.getSchema(swagger, + entry.getValue().getContent().get(mediaType).getSchema())); + + if ("default".equals(entry.getKey())) { + defaultResponse = javaType; + continue; + } + + Integer statusCode = Integer.parseInt(entry.getKey()); + JavaType existing = responseMap.get(statusCode); + if (existing == null || !ReflectionUtils.isVoid(javaType)) { + responseMap.put(statusCode, javaType); + } + } + + responseMap.putIfAbsent(ExceptionFactory.CONSUMER_INNER_STATUS_CODE, COMMON_EXCEPTION_JAVA_TYPE); + responseMap.putIfAbsent(ExceptionFactory.PRODUCER_INNER_STATUS_CODE, COMMON_EXCEPTION_JAVA_TYPE); + responseMap.putIfAbsent(Status.TOO_MANY_REQUESTS.getStatusCode(), COMMON_EXCEPTION_JAVA_TYPE); + responseMap.putIfAbsent(Status.REQUEST_TIMEOUT.getStatusCode(), COMMON_EXCEPTION_JAVA_TYPE); + responseMap.putIfAbsent(Status.SERVICE_UNAVAILABLE.getStatusCode(), COMMON_EXCEPTION_JAVA_TYPE); + + if (defaultResponse == null) { + // swagger中没有定义default,加上default专用于处理exception + defaultResponse = OBJECT_JAVA_TYPE; + } + } + + public void cloneTo(ResponsesMeta target) { + target.defaultResponse = defaultResponse; + target.responseMap.putAll(responseMap); + } + + protected void initGlobalDefaultMapper() { + if (GLOBAL_DEFAULT_MAPPER != null) { + Map mappers = GLOBAL_DEFAULT_MAPPER.getMapper(); + if (mappers != null) { + responseMap.putAll(mappers); + } + } + } + + public JavaType findResponseType(int statusCode) { + JavaType responseType = responseMap.get(statusCode); + if (responseType == null) { + if (HttpStatus.isSuccess(statusCode)) { + return responseMap.get(Status.OK.getStatusCode()); + } + + return defaultResponse; + } + + return responseType; + } + + public void setResponseType(int statusCode, JavaType javaType) { + this.responseMap.put(statusCode, javaType); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CompletableFutureConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CompletableFutureConsumerResponseMapperFactory.java new file mode 100644 index 00000000000..0a7d5a976ed --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CompletableFutureConsumerResponseMapperFactory.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.swagger.invocation.response.consumer; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; + +public class CompletableFutureConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { + @Override + public boolean isMatch(Type consumerType) { + if (!ParameterizedType.class.isAssignableFrom(consumerType.getClass())) { + return false; + } + + return ((ParameterizedType) consumerType).getRawType().equals(CompletableFuture.class); + } + + @Override + public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, + Type consumerType) { + Type realConsumerType = ((ParameterizedType) consumerType).getActualTypeArguments()[0]; + return factories.createResponseMapper(realConsumerType); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapper.java new file mode 100644 index 00000000000..74eeb63b48c --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapper.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.swagger.invocation.response.consumer; + +import org.apache.servicecomb.swagger.invocation.Response; + +public interface ConsumerResponseMapper { + Object mapResponse(Response response); +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapperFactory.java new file mode 100644 index 00000000000..538df04bf35 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/ConsumerResponseMapperFactory.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.swagger.invocation.response.consumer; + +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactory; + +public interface ConsumerResponseMapperFactory extends ResponseMapperFactory { +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CseResponseConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CseResponseConsumerResponseMapperFactory.java new file mode 100644 index 00000000000..76b90185dcd --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CseResponseConsumerResponseMapperFactory.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.swagger.invocation.response.consumer; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; + +public class CseResponseConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { + private static final ConsumerResponseMapper SAME = response -> response; + + @Override + public boolean isMatch(Type consumerType) { + return Response.class.equals(consumerType); + } + + @Override + public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, + Type consumerType) { + return SAME; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/DefaultConsumerResponseMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/DefaultConsumerResponseMapper.java new file mode 100644 index 00000000000..d430050b0ca --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/DefaultConsumerResponseMapper.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.swagger.invocation.response.consumer; + +import org.apache.servicecomb.swagger.invocation.Response; + +public class DefaultConsumerResponseMapper implements ConsumerResponseMapper { + @Override + public Object mapResponse(Response response) { + return response.getResult(); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/DefaultConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/DefaultConsumerResponseMapperFactory.java new file mode 100644 index 00000000000..e60f05604e0 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/DefaultConsumerResponseMapperFactory.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.swagger.invocation.response.consumer; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; + +public class DefaultConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { + @Override + public int getOrder() { + return Integer.MAX_VALUE; + } + + @Override + public boolean isMatch(Type consumerType) { + return true; + } + + @Override + public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, + Type consumerType) { + return new DefaultConsumerResponseMapper(); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/OptionalConsumerResponseMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/OptionalConsumerResponseMapper.java new file mode 100644 index 00000000000..d90be2d52a0 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/OptionalConsumerResponseMapper.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.swagger.invocation.response.consumer; + +import java.util.Optional; + +import org.apache.servicecomb.swagger.invocation.Response; + +public class OptionalConsumerResponseMapper implements ConsumerResponseMapper { + private final ConsumerResponseMapper realMapper; + + public OptionalConsumerResponseMapper(ConsumerResponseMapper realMapper) { + this.realMapper = realMapper; + } + + @Override + public Object mapResponse(Response response) { + Object realResult = realMapper.mapResponse(response); + return Optional.ofNullable(realResult); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/OptionalConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/OptionalConsumerResponseMapperFactory.java new file mode 100644 index 00000000000..eddd30e407a --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/OptionalConsumerResponseMapperFactory.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.swagger.invocation.response.consumer; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Optional; + +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; + +public class OptionalConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { + @Override + public boolean isMatch(Type consumerType) { + if (!ParameterizedType.class.isAssignableFrom(consumerType.getClass())) { + return false; + } + + return ((ParameterizedType) consumerType).getRawType().equals(Optional.class); + } + + @Override + public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, + Type consumerType) { + Type realConsumerType = ((ParameterizedType) consumerType).getActualTypeArguments()[0]; + ConsumerResponseMapper realMapper = factories.createResponseMapper(realConsumerType); + return new OptionalConsumerResponseMapper(realMapper); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CompletableFutureProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CompletableFutureProducerResponseMapperFactory.java new file mode 100644 index 00000000000..fc87f5728b7 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CompletableFutureProducerResponseMapperFactory.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.swagger.invocation.response.producer; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; + +public class CompletableFutureProducerResponseMapperFactory implements ProducerResponseMapperFactory { + @Override + public boolean isMatch(Type producerType) { + if (!ParameterizedType.class.isAssignableFrom(producerType.getClass())) { + return false; + } + + return ((ParameterizedType) producerType).getRawType().equals(CompletableFuture.class); + } + + @Override + public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, + Type producerType) { + Type realProducerType = ((ParameterizedType) producerType).getActualTypeArguments()[0]; + return factories.createResponseMapper(realProducerType); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CseResponseProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CseResponseProducerResponseMapperFactory.java new file mode 100644 index 00000000000..02f840cbb59 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CseResponseProducerResponseMapperFactory.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.swagger.invocation.response.producer; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; + +public class CseResponseProducerResponseMapperFactory implements ProducerResponseMapperFactory { + private static final ProducerResponseMapper SAME = (status, response) -> (Response) response; + + @Override + public boolean isMatch(Type producerType) { + return producerType.equals(Response.class); + } + + @Override + public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, + Type producerType) { + return SAME; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/DefaultProducerResponseMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/DefaultProducerResponseMapper.java new file mode 100644 index 00000000000..aa12dad9be9 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/DefaultProducerResponseMapper.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.swagger.invocation.response.producer; + +import jakarta.ws.rs.core.Response.StatusType; + +import org.apache.servicecomb.swagger.invocation.Response; + +public class DefaultProducerResponseMapper implements ProducerResponseMapper { + @Override + public Response mapResponse(StatusType status, Object response) { + return Response.create(status, response); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/DefaultProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/DefaultProducerResponseMapperFactory.java new file mode 100644 index 00000000000..1b58d6cbb08 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/DefaultProducerResponseMapperFactory.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.swagger.invocation.response.producer; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; + +public class DefaultProducerResponseMapperFactory implements ProducerResponseMapperFactory { + @Override + public int getOrder() { + return Integer.MAX_VALUE; + } + + @Override + public boolean isMatch(Type producerType) { + return true; + } + + @Override + public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, + Type producerType) { + return new DefaultProducerResponseMapper(); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/OptionalProducerResponseMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/OptionalProducerResponseMapper.java new file mode 100644 index 00000000000..1e63a537140 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/OptionalProducerResponseMapper.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.swagger.invocation.response.producer; + +import java.util.Optional; + +import jakarta.ws.rs.core.Response.StatusType; + +import org.apache.servicecomb.swagger.invocation.Response; + +public class OptionalProducerResponseMapper implements ProducerResponseMapper { + private final ProducerResponseMapper realMapper; + + public OptionalProducerResponseMapper(ProducerResponseMapper realMapper) { + this.realMapper = realMapper; + } + + @Override + public Response mapResponse(StatusType status, Object response) { + @SuppressWarnings("unchecked") + Optional optional = (Optional) response; + return realMapper.mapResponse(status, optional.orElse(null)); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/OptionalProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/OptionalProducerResponseMapperFactory.java new file mode 100644 index 00000000000..39d46b8b777 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/OptionalProducerResponseMapperFactory.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.swagger.invocation.response.producer; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Optional; + +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; + +public class OptionalProducerResponseMapperFactory implements ProducerResponseMapperFactory { + @Override + public boolean isMatch(Type producerType) { + if (!ParameterizedType.class.isAssignableFrom(producerType.getClass())) { + return false; + } + + return ((ParameterizedType) producerType).getRawType().equals(Optional.class); + } + + @Override + public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, + Type producerType) { + Type realProducerType = ((ParameterizedType) producerType).getActualTypeArguments()[0]; + ProducerResponseMapper realMapper = factories.createResponseMapper(realProducerType); + return new OptionalProducerResponseMapper(realMapper); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/ProducerResponseMapper.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/ProducerResponseMapper.java new file mode 100644 index 00000000000..68617b87c1d --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/ProducerResponseMapper.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.response.producer; + +import jakarta.ws.rs.core.Response.StatusType; + +import org.apache.servicecomb.swagger.invocation.Response; + +public interface ProducerResponseMapper { + Response mapResponse(StatusType status, Object response); +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/ProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/ProducerResponseMapperFactory.java new file mode 100644 index 00000000000..bcc5c29ac50 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/ProducerResponseMapperFactory.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.swagger.invocation.response.producer; + +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactory; + +public interface ProducerResponseMapperFactory extends ResponseMapperFactory { + +} diff --git a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.CommonParameterTypeProcessor b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.CommonParameterTypeProcessor deleted file mode 100644 index 13b06a1f94e..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/io.servicecomb.swagger.generator.core.CommonParameterTypeProcessor +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -io.servicecomb.swagger.invocation.generator.InvocationContextProcessor \ No newline at end of file diff --git a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor new file mode 100644 index 00000000000..f1f15cdd7a0 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor @@ -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.swagger.invocation.generator.ScbResponseProcessor diff --git a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister new file mode 100644 index 00000000000..e248e16db89 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister @@ -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.swagger.invocation.generator.InvocationContextProcessor diff --git a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory new file mode 100644 index 00000000000..66a48f5a85c --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory @@ -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.swagger.invocation.arguments.consumer.ConsumerInvocationContextMapperFactory diff --git a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory new file mode 100644 index 00000000000..fa524012dc5 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory @@ -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.swagger.invocation.arguments.producer.ProducerInvocationContextMapperFactory diff --git a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter new file mode 100644 index 00000000000..c91943b26bc --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter @@ -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. +# + +org.apache.servicecomb.swagger.invocation.converter.impl.part.InputStreamToPartConverter +org.apache.servicecomb.swagger.invocation.converter.impl.part.BytesToPartConverter +org.apache.servicecomb.swagger.invocation.converter.impl.part.FileToPartConverter +org.apache.servicecomb.swagger.invocation.converter.impl.part.PartToPartConverter +org.apache.servicecomb.swagger.invocation.converter.impl.part.PartListToPartArrayConverter +org.apache.servicecomb.swagger.invocation.converter.impl.part.PartListToPartListConverter diff --git a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory new file mode 100644 index 00000000000..0474058a9e3 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory @@ -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. +# + +org.apache.servicecomb.swagger.invocation.response.consumer.CseResponseConsumerResponseMapperFactory +org.apache.servicecomb.swagger.invocation.response.consumer.CompletableFutureConsumerResponseMapperFactory +org.apache.servicecomb.swagger.invocation.response.consumer.DefaultConsumerResponseMapperFactory +org.apache.servicecomb.swagger.invocation.response.consumer.OptionalConsumerResponseMapperFactory diff --git a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory new file mode 100644 index 00000000000..dac2a90e393 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory @@ -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. +# + +org.apache.servicecomb.swagger.invocation.response.producer.CseResponseProducerResponseMapperFactory +org.apache.servicecomb.swagger.invocation.response.producer.CompletableFutureProducerResponseMapperFactory +org.apache.servicecomb.swagger.invocation.response.producer.DefaultProducerResponseMapperFactory +org.apache.servicecomb.swagger.invocation.response.producer.OptionalProducerResponseMapperFactory diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/core/TestException.java b/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/core/TestException.java deleted file mode 100644 index c554dc38ebd..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/core/TestException.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.core; - -import java.lang.reflect.InvocationTargetException; - -import javax.ws.rs.core.Response.Status; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.servicecomb.swagger.invocation.exception.ExceptionFactory; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -public class TestException { - @Test - public void testCommonExceptionData() { - CommonExceptionData oData = new CommonExceptionData(); - oData.setMessage("this is Common exception message"); - Assert.assertEquals("this is Common exception message", oData.getMessage()); - - oData = new CommonExceptionData("this is a test"); - Assert.assertEquals("this is a test", oData.getMessage()); - Assert.assertEquals("CommonExceptionData [message=this is a test]", oData.toString()); - } - - @Test - public void testInvocationException() { - InvocationException oExceptionIn = new InvocationException(Status.OK, "I am gone now"); - oExceptionIn = ExceptionFactory.convertConsumerException(new Throwable()); - Assert.assertEquals(490, oExceptionIn.getStatusCode()); - - oExceptionIn = ExceptionFactory.convertConsumerException(new Throwable(), "abc"); - Assert.assertEquals(490, oExceptionIn.getStatusCode()); - Assert.assertEquals("abc", ((CommonExceptionData) oExceptionIn.getErrorData()).getMessage()); - - oExceptionIn = ExceptionFactory.convertProducerException(new Throwable()); - Assert.assertEquals(590, oExceptionIn.getStatusCode()); - - oExceptionIn = ExceptionFactory.convertProducerException(new Throwable(), "abcd"); - Assert.assertEquals(590, oExceptionIn.getStatusCode()); - Assert.assertEquals("abcd", ((CommonExceptionData) oExceptionIn.getErrorData()).getMessage()); - - oExceptionIn = - ExceptionFactory.convertConsumerException(new InvocationException(Status.OK, new String("fake-object"))); - Assert.assertEquals(200, oExceptionIn.getStatusCode()); - - oExceptionIn = ExceptionFactory.convertConsumerException(new InvocationTargetException(new Throwable())); - Assert.assertNotEquals("java.lang.Throwable", oExceptionIn.getMessage()); - - InvocationException oTemp = new InvocationException(Status.OK, new CommonExceptionData("testObject")); - Assert.assertEquals("OK", oTemp.getReasonPhrase()); - Assert.assertEquals("CommonExceptionData [message=testObject]", (oTemp.getErrorData().toString())); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/core/TestResponse.java b/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/core/TestResponse.java deleted file mode 100644 index bedb5d14f44..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/core/TestResponse.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.core; - -import javax.ws.rs.core.Response.Status; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.InvocationType; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.servicecomb.swagger.invocation.exception.ExceptionFactory; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -public class TestResponse { - Response response; - - AsyncResponse ar = new AsyncResponse() { - @Override - public void handle(Response resp) { - response = resp; - } - }; - - @Test - public void testAr() { - ar.success(Status.ACCEPTED, 1); - Assert.assertEquals(true, response.isSuccessed()); - Assert.assertEquals(false, response.isFailed()); - Assert.assertEquals(1, (int) response.getResult()); - Assert.assertEquals(Status.ACCEPTED.getStatusCode(), response.getStatusCode()); - Assert.assertEquals(Status.ACCEPTED.getReasonPhrase(), response.getReasonPhrase()); - Assert.assertEquals(Status.ACCEPTED, response.getStatus()); - - ar.success(2); - Assert.assertEquals(2, (int) response.getResult()); - Assert.assertEquals(Status.OK.getStatusCode(), response.getStatusCode()); - - Response r = Response.succResp(3); - ar.complete(r); - Assert.assertEquals(r, response); - - ar.consumerFail(new Error("abc")); - CommonExceptionData data = (CommonExceptionData) ((InvocationException) response.getResult()).getErrorData(); - Assert.assertEquals("Cse Internal Bad Request", data.getMessage()); - Assert.assertEquals(ExceptionFactory.CONSUMER_INNER_STATUS_CODE, response.getStatusCode()); - - ar.fail(InvocationType.CONSUMER, new Error("abc")); - data = (CommonExceptionData) ((InvocationException) response.getResult()).getErrorData(); - Assert.assertEquals("Cse Internal Bad Request", data.getMessage()); - Assert.assertEquals(ExceptionFactory.CONSUMER_INNER_STATUS_CODE, response.getStatusCode()); - - InvocationException consumerException = new InvocationException(300, "abc", "def"); - ar.consumerFail(consumerException); - Assert.assertEquals("def", ((InvocationException) response.getResult()).getErrorData()); - Assert.assertEquals(300, response.getStatusCode()); - - ar.fail(InvocationType.CONSUMER, consumerException); - Assert.assertEquals("def", ((InvocationException) response.getResult()).getErrorData()); - Assert.assertEquals(300, response.getStatusCode()); - - ar.producerFail(new Error("abc")); - data = (CommonExceptionData) ((InvocationException) response.getResult()).getErrorData(); - Assert.assertEquals("Cse Internal Server Error", data.getMessage()); - Assert.assertEquals(ExceptionFactory.PRODUCER_INNER_STATUS_CODE, response.getStatusCode()); - - ar.fail(InvocationType.PRODUCER, new Error("abc")); - data = (CommonExceptionData) ((InvocationException) response.getResult()).getErrorData(); - Assert.assertEquals("Cse Internal Server Error", data.getMessage()); - Assert.assertEquals(ExceptionFactory.PRODUCER_INNER_STATUS_CODE, response.getStatusCode()); - - InvocationException producerException = new InvocationException(500, "abc", "def"); - ar.producerFail(producerException); - Assert.assertEquals("def", ((InvocationException) response.getResult()).getErrorData()); - Assert.assertEquals(500, response.getStatusCode()); - - ar.fail(InvocationType.PRODUCER, producerException); - Assert.assertEquals("def", ((InvocationException) response.getResult()).getErrorData()); - Assert.assertEquals(500, response.getStatusCode()); - } - - @Test - public void test() { - Response r = Response.create(200, "200", 2); - Assert.assertEquals(200, r.getStatusCode()); - Assert.assertEquals(2, (int) r.getResult()); - Response r1 = r.build(); - Assert.assertEquals(r, r1); - - r = Response.create(300, "300", 3); - Assert.assertEquals(300, r.getStatusCode()); - Assert.assertEquals("300", r.getReasonPhrase()); - Assert.assertEquals(3, ((InvocationException) r.getResult()).getErrorData()); - - r = Response.createSuccess(Status.OK, 2); - Assert.assertEquals(200, r.getStatusCode()); - Assert.assertEquals(2, (int) r.getResult()); - - r = Response.success(2, Status.OK); - Assert.assertEquals(200, r.getStatusCode()); - Assert.assertEquals(2, (int) r.getResult()); - - r = Response.createFail(InvocationType.CONSUMER, "abc"); - Assert.assertEquals("CommonExceptionData [message=abc]", - ((InvocationException) r.getResult()).getErrorData().toString()); - Assert.assertEquals(490, r.getStatusCode()); - - r = Response.createFail(InvocationType.PRODUCER, "def"); - Assert.assertEquals("CommonExceptionData [message=def]", - ((InvocationException) r.getResult()).getErrorData().toString()); - Assert.assertEquals(590, r.getStatusCode()); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/core/context/TestHttpStatusManager.java b/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/core/context/TestHttpStatusManager.java deleted file mode 100644 index 326b471b271..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/core/context/TestHttpStatusManager.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.core.context; - -import javax.ws.rs.core.Response.StatusType; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.swagger.invocation.context.HttpStatus; -import io.servicecomb.swagger.invocation.context.HttpStatusManager; - -public class TestHttpStatusManager { - @Test - public void test() { - HttpStatusManager mgr = new HttpStatusManager(); - - StatusType st = mgr.getOrCreateByStatusCode(200); - Assert.assertEquals(200, st.getStatusCode()); - - st = mgr.getOrCreateByStatusCode(250); - Assert.assertEquals(250, st.getStatusCode()); - - try { - mgr.addStatusType(new HttpStatus(250, "250")); - throw new Error("not allowed"); - } catch (Throwable e) { - Assert.assertEquals("repeated status code: 250", e.getMessage()); - } - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/arguments/TestPojoConsumerEqualProducer.java b/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/arguments/TestPojoConsumerEqualProducer.java deleted file mode 100644 index 5ad5d18181f..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/arguments/TestPojoConsumerEqualProducer.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments; - -import java.util.Arrays; -import java.util.List; - -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -import io.servicecomb.swagger.engine.SwaggerConsumer; -import io.servicecomb.swagger.engine.SwaggerEnvironment; -import io.servicecomb.swagger.engine.SwaggerProducer; -import io.servicecomb.swagger.engine.bootstrap.BootstrapNormal; -import io.servicecomb.swagger.engine.unittest.LocalProducerInvoker; -import io.servicecomb.swagger.invocation.arguments.utils.Utils; -import io.servicecomb.swagger.invocation.context.ContextUtils; -import io.servicecomb.swagger.invocation.context.InvocationContext; -import io.servicecomb.swagger.invocation.models.Person; -import io.servicecomb.swagger.invocation.models.PojoConsumerIntf; -import io.servicecomb.swagger.invocation.models.PojoImpl; - -public class TestPojoConsumerEqualProducer { - private static SwaggerEnvironment env; - - private static SwaggerProducer producer; - - private static SwaggerConsumer consumer; - - private static LocalProducerInvoker invoker; - - private static PojoConsumerIntf proxy; - - @BeforeClass - public static void init() { - env = new BootstrapNormal().boot(); - producer = env.createProducer(new PojoImpl()); - consumer = env.createConsumer(PojoConsumerIntf.class, producer.getSwaggerIntf()); - invoker = new LocalProducerInvoker(consumer, producer); - proxy = invoker.getProxy(); - } - - @Test - public void testSimple() throws Exception { - int result = proxy.testSimple(1, 2, 3); - - Object body = invoker.getSwaggerArgument(0); - Assert.assertEquals(1, Utils.getFieldValue(body, "a")); - Assert.assertEquals(2, Utils.getFieldValue(body, "b")); - Assert.assertEquals(3, Utils.getFieldValue(body, "c")); - - Assert.assertEquals(-4, result); - } - - @Test - public void testObject() throws Exception { - Person person = new Person(); - person.setName("abc"); - - Person result = proxy.testObject(person); - - Person swaggerPerson = invoker.getSwaggerArgument(0); - Assert.assertEquals(person, swaggerPerson); - - Assert.assertEquals("hello abc", result.getName()); - } - - @Test - public void testSimpleAndObject() throws Exception { - Person person = new Person(); - person.setName("abc"); - - String result = proxy.testSimpleAndObject("prefix", person); - - Object body = invoker.getSwaggerArgument(0); - Assert.assertEquals("prefix", Utils.getFieldValue(body, "prefix")); - Assert.assertEquals(person, Utils.getFieldValue(body, "user")); - - Assert.assertEquals("prefix abc", result); - } - - @Test - public void testContext() throws Exception { - InvocationContext threadContext = new InvocationContext(); - threadContext.addContext("ta", "tvalue"); - ContextUtils.setInvocationContext(threadContext); - - InvocationContext context = new InvocationContext(); - context.addContext("a", "value"); - - String result = proxy.testContext(context, "name"); - - Object body = invoker.getSwaggerArgument(0); - Assert.assertEquals(3, invoker.getInvocation().getContext().size()); - Assert.assertEquals("tvalue", invoker.getContext("ta")); - Assert.assertEquals("value", invoker.getContext("a")); - Assert.assertEquals("name", invoker.getContext("name")); - Assert.assertEquals("name", Utils.getFieldValue(body, "name")); - - Assert.assertEquals("name sayhi", result); - } - - @Test - public void testBytes() throws Exception { - byte[] bytes = new byte[] {1, 2}; - - byte[] result = proxy.testBytes(bytes); - - Object body = invoker.getSwaggerArgument(0); - Assert.assertEquals(bytes, Utils.getFieldValue(body, "bytes")); - - Assert.assertArrayEquals(bytes, (byte[]) result); - } - - @Test - public void testArrayArray() throws Exception { - String[] array = new String[] {"a", "b"}; - List list = Arrays.asList(array); - - String[] result = proxy.testArrayArray(array); - - Object body = invoker.getSwaggerArgument(0); - Assert.assertEquals(list, Utils.getFieldValue(body, "s")); - - Assert.assertArrayEquals(array, (String[]) result); - } - - @Test - public void testArrayList() throws Exception { - String[] array = new String[] {"a", "b"}; - List list = Arrays.asList(array); - List result = proxy.testArrayList(array); - - Object body = invoker.getSwaggerArgument(0); - Assert.assertEquals(list, Utils.getFieldValue(body, "s")); - - Assert.assertEquals(list, result); - } - - @Test - public void testListArray() throws Exception { - String[] array = new String[] {"a", "b"}; - List list = Arrays.asList(array); - - String[] result = proxy.testListArray(list); - - Object body = invoker.getSwaggerArgument(0); - Assert.assertEquals(list, Utils.getFieldValue(body, "s")); - - Assert.assertArrayEquals(array, result); - } - - @Test - public void testListList() throws Exception { - List list = Arrays.asList("a", "b"); - - List result = proxy.testListList(list); - - Object body = invoker.getSwaggerArgument(0); - Assert.assertEquals(list, Utils.getFieldValue(body, "s")); - - Assert.assertEquals(list, result); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/arguments/TestPojoConsumerEqualSwagger.java b/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/arguments/TestPojoConsumerEqualSwagger.java deleted file mode 100644 index 9dadb54bff9..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/arguments/TestPojoConsumerEqualSwagger.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments; - -import java.util.Arrays; -import java.util.List; - -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -import io.servicecomb.swagger.engine.SwaggerConsumer; -import io.servicecomb.swagger.engine.SwaggerEnvironment; -import io.servicecomb.swagger.engine.SwaggerProducer; -import io.servicecomb.swagger.engine.bootstrap.BootstrapNormal; -import io.servicecomb.swagger.engine.unittest.LocalProducerInvoker; -import io.servicecomb.swagger.invocation.context.ContextUtils; -import io.servicecomb.swagger.invocation.context.InvocationContext; -import io.servicecomb.swagger.invocation.models.JaxrsImpl; -import io.servicecomb.swagger.invocation.models.Person; -import io.servicecomb.swagger.invocation.models.PojoConsumerIntf; - -public class TestPojoConsumerEqualSwagger { - private static SwaggerEnvironment env; - - private static SwaggerProducer producer; - - private static SwaggerConsumer consumer; - - private static LocalProducerInvoker invoker; - - private static PojoConsumerIntf proxy; - - @BeforeClass - public static void init() { - env = new BootstrapNormal().boot(); - producer = env.createProducer(new JaxrsImpl()); - consumer = env.createConsumer(PojoConsumerIntf.class, producer.getSwaggerIntf()); - invoker = new LocalProducerInvoker(consumer, producer); - proxy = invoker.getProxy(); - } - - @Test - public void testSimple() throws Exception { - int result = proxy.testSimple(1, 2, 3); - - Assert.assertEquals(1, (int) invoker.getSwaggerArgument(0)); - Assert.assertEquals(2, (int) invoker.getSwaggerArgument(1)); - Assert.assertEquals(3, (int) invoker.getSwaggerArgument(2)); - - Assert.assertEquals(-4, result); - } - - @Test - public void testObject() throws Exception { - Person person = new Person(); - person.setName("abc"); - - Person result = proxy.testObject(person); - - Person swaggerPerson = invoker.getSwaggerArgument(0); - Assert.assertEquals(person.getName(), swaggerPerson.getName()); - - Assert.assertEquals("hello abc", result.getName()); - } - - @Test - public void testSimpleAndObject() throws Exception { - Person person = new Person(); - person.setName("abc"); - - String result = proxy.testSimpleAndObject("prefix", person); - - Assert.assertEquals("prefix", (String) invoker.getSwaggerArgument(0)); - Assert.assertEquals(person.getName(), ((Person) invoker.getSwaggerArgument(1)).getName()); - - Assert.assertEquals("prefix abc", result); - } - - @Test - public void testContext() throws Exception { - InvocationContext threadContext = new InvocationContext(); - threadContext.addContext("ta", "tvalue"); - ContextUtils.setInvocationContext(threadContext); - - InvocationContext context = new InvocationContext(); - context.addContext("a", "value"); - - String result = proxy.testContext(context, "name"); - - Assert.assertEquals(3, invoker.getInvocation().getContext().size()); - Assert.assertEquals("tvalue", invoker.getContext("ta")); - Assert.assertEquals("value", invoker.getContext("a")); - Assert.assertEquals("name", invoker.getContext("name")); - Assert.assertEquals("name", (String) invoker.getSwaggerArgument(0)); - - Assert.assertEquals("name sayhi", result); - } - - @Test - public void testBytes() throws Exception { - byte[] bytes = new byte[] {1, 2}; - - byte[] result = proxy.testBytes(bytes); - - Assert.assertEquals(bytes, invoker.getSwaggerArgument(0)); - - Assert.assertArrayEquals(bytes, (byte[]) result); - } - - @Test - public void testArrayArray() throws Exception { - String[] array = new String[] {"a", "b"}; - List list = Arrays.asList(array); - - String[] result = proxy.testArrayArray(array); - - Assert.assertEquals(list, (List) invoker.getSwaggerArgument(0)); - - Assert.assertArrayEquals(array, (String[]) result); - } - - @Test - public void testArrayList() throws Exception { - String[] array = new String[] {"a", "b"}; - List list = Arrays.asList(array); - List result = proxy.testArrayList(array); - - Assert.assertEquals(list, invoker.getSwaggerArgument(0)); - - Assert.assertEquals(list, result); - } - - @Test - public void testListArray() throws Exception { - String[] array = new String[] {"a", "b"}; - List list = Arrays.asList(array); - - String[] result = proxy.testListArray(list); - - Assert.assertEquals(list, invoker.getSwaggerArgument(0)); - - Assert.assertArrayEquals(array, result); - } - - @Test - public void testListList() throws Exception { - List list = Arrays.asList("a", "b"); - - List result = proxy.testListList(list); - - Assert.assertEquals(list, invoker.getSwaggerArgument(0)); - - Assert.assertEquals(list, result); - } - - @Test - public void testObjectArrayArray() throws Exception { - Person[] array = new Person[] {new Person("a"), new Person("b")}; - List list = Arrays.asList(array); - - Person[] result = proxy.testObjectArrayArray(array); - - Assert.assertEquals(list, (List) invoker.getSwaggerArgument(0)); - - Assert.assertArrayEquals(array, (Person[]) result); - } - - @Test - public void testObjectArrayList() throws Exception { - Person[] array = new Person[] {new Person("a"), new Person("b")}; - List list = Arrays.asList(array); - List result = proxy.testObjectArrayList(array); - - Assert.assertEquals(list, invoker.getSwaggerArgument(0)); - - Assert.assertEquals(list, result); - } - - @Test - public void testObjectListArray() throws Exception { - Person[] array = new Person[] {new Person("a"), new Person("b")}; - List list = Arrays.asList(array); - - Person[] result = proxy.testObjectListArray(list); - - Assert.assertEquals(list, invoker.getSwaggerArgument(0)); - Assert.assertEquals(list, invoker.getProducerResponse().getResult()); - - Assert.assertArrayEquals(array, result); - } - - @Test - public void testObjectListList() throws Exception { - Person[] array = new Person[] {new Person("a"), new Person("b")}; - List list = Arrays.asList(array); - - List result = proxy.testObjectListList(list); - - Assert.assertEquals(list, invoker.getSwaggerArgument(0)); - - Assert.assertEquals(list, result); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/arguments/utils/Utils.java b/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/arguments/utils/Utils.java deleted file mode 100644 index 7fe385e2772..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/arguments/utils/Utils.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.arguments.utils; - -import org.springframework.util.ReflectionUtils; - -public class Utils { - public static Object getFieldValue(Object instance, String fieldName) { - try { - return ReflectionUtils.findField(instance.getClass(), fieldName).get(instance); - } catch (Exception e) { - throw new Error(e); - } - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/JaxrsImpl.java b/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/JaxrsImpl.java deleted file mode 100644 index 29933c65adb..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/JaxrsImpl.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.models; - -import java.util.Arrays; -import java.util.List; - -import javax.ws.rs.CookieParam; -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.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; - -import io.servicecomb.swagger.invocation.context.InvocationContext; - -@Path("/JaxrsImpl") -@Produces(MediaType.APPLICATION_JSON) -public class JaxrsImpl { - @Path("/testTwoSimple") - @GET - public int testSimple(@PathParam("a") int a, @QueryParam("b") int b, @HeaderParam("c") int c) { - return a - b - c; - } - - @Path("/testObject") - @POST - public Person testObject(Person user) { - user.setName("hello " + user.getName()); - return user; - } - - @Path("/testSimpleAndObject") - @POST - public String testSimpleAndObject(@CookieParam("prefix") String prefix, Person user) { - return prefix + " " + user.getName(); - } - - @Path("/testContext") - @POST - public String testContext(InvocationContext context, @FormParam("form") String name) { - context.addContext("name", name); - return name + " sayhi"; - } - - @Path("/bytes") - @POST - public byte[] testBytes(byte[] input) { - return input; - } - - @Path("/testArrayArray") - @POST - public String[] testArrayArray(String[] s) { - return s; - } - - @Path("/testArrayList") - @POST - public List testArrayList(String[] s) { - return Arrays.asList(s); - } - - @Path("/testListArray") - @POST - public String[] testListArray(List s) { - return s.toArray(new String[s.size()]); - } - - @Path("/testListList") - @POST - public List testListList(List s) { - return s; - } - - @Path("/testObjectArrayArray") - @POST - public Person[] testObjectArrayArray(Person[] s) { - return s; - } - - @Path("/testObjectArrayList") - @POST - public List testObjectArrayList(Person[] s) { - return Arrays.asList(s); - } - - @Path("/testObjectListArray") - @POST - public Person[] testObjectListArray(List s) { - return s.toArray(new Person[s.size()]); - } - - @Path("/testObjectListList") - @POST - public List testObjectListList(List s) { - return s; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/Person.java b/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/Person.java deleted file mode 100644 index 1b13becfe9d..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/Person.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.models; - -public class Person { - private String name; - - public Person() { - } - - public Person(String name) { - this.name = name; - } - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return this.name; - } - - @Override - public String toString() { - return name; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/PojoConsumerIntf.java b/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/PojoConsumerIntf.java deleted file mode 100644 index 2cba4a10561..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/PojoConsumerIntf.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.models; - -import java.util.List; - -import io.servicecomb.swagger.invocation.context.InvocationContext; - -public interface PojoConsumerIntf { - int testSimple(int a, int b, int c); - - Person testObject(Person user); - - String testSimpleAndObject(String prefix, Person user); - - String testContext(InvocationContext context, String name); - - byte[] testBytes(byte[] bytes); - - String[] testArrayArray(String[] s); - - List testArrayList(String[] s); - - String[] testListArray(List s); - - List testListList(List s); - - Person[] testObjectArrayArray(Person[] s); - - List testObjectArrayList(Person[] s); - - Person[] testObjectListArray(List s); - - List testObjectListList(List s); -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/PojoImpl.java b/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/PojoImpl.java deleted file mode 100644 index a687305af82..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/PojoImpl.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.models; - -import java.util.Arrays; -import java.util.List; - -import io.servicecomb.swagger.invocation.context.InvocationContext; - -public class PojoImpl { - public int testSimple(int a, int b, int c) { - return a - b - c; - } - - public Person testObject(Person user) { - user.setName("hello " + user.getName()); - return user; - } - - public String testSimpleAndObject(String prefix, Person user) { - return prefix + " " + user.getName(); - } - - public String testContext(InvocationContext context, String name) { - context.addContext("name", name); - return name + " sayhi"; - } - - public byte[] testBytes(byte[] bytes) { - return bytes; - } - - public String[] testArrayArray(String[] s) { - return s; - } - - public List testArrayList(String[] s) { - return Arrays.asList(s); - } - - public String[] testListArray(List s) { - return s.toArray(new String[s.size()]); - } - - public List testListList(List s) { - return s; - } - - public Person[] testObjectArrayArray(Person[] s) { - return s; - } - - public List testObjectArrayList(Person[] s) { - return Arrays.asList(s); - } - - public Person[] testObjectListArray(List s) { - return s.toArray(new Person[s.size()]); - } - - public List testObjectListList(List s) { - return s; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/SpringmvcImpl.java b/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/SpringmvcImpl.java deleted file mode 100644 index c2e156de104..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/models/SpringmvcImpl.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.swagger.invocation.models; - -public class SpringmvcImpl { - -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/response/TestHeaders.java b/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/response/TestHeaders.java deleted file mode 100644 index dfa129e6952..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/response/TestHeaders.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - -public class TestHeaders { - @Test - public void test1() { - Headers headers = new Headers(); - Assert.assertEquals(null, headers.getFirst("h1")); - Assert.assertEquals(null, headers.getHeader("h1")); - - Map> headerMap = new HashMap<>(); - List h1Value = Arrays.asList("h1v1", "h1v2"); - headerMap.put("h1", h1Value); - headerMap.put("h2", null); - headerMap.put("h3", Arrays.asList()); - headers.setHeaderMap(headerMap); - - Assert.assertEquals(headerMap, headers.getHeaderMap()); - Assert.assertEquals("h1v1", headers.getFirst("h1")); - Assert.assertEquals(null, headers.getFirst("h2")); - Assert.assertEquals(null, headers.getFirst("h3")); - Assert.assertEquals(h1Value, headers.getHeader("h1")); - } - - @Test - public void test2() { - Headers headers = new Headers(); - headers.addHeader("h1", "h1v1"); - headers.addHeader("h1", "h1v2"); - - Assert.assertEquals("h1v1", headers.getFirst("h1")); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/response/TestResponsesMeta.java b/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/response/TestResponsesMeta.java deleted file mode 100644 index 50a71a05ab5..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/io/servicecomb/swagger/invocation/response/TestResponsesMeta.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.response; - -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.swagger.generator.core.SwaggerGenerator; -import io.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; -import io.swagger.annotations.ResponseHeader; -import io.swagger.models.Operation; -import io.swagger.models.Swagger; - -public class TestResponsesMeta { - class ResponseMetaImpl { - @ApiResponses({@ApiResponse(code = 400, response = String.class, message = ""), - @ApiResponse( - code = 401, - response = long.class, - message = "", - responseHeaders = {@ResponseHeader(name = "h1", response = int.class)}) - }) - public int add(int x, int y) { - return x + y; - } - } - - @Test - public void test() { - SwaggerGenerator generator = UnitTestSwaggerUtils.generateSwagger(ResponseMetaImpl.class); - Swagger swagger = generator.getSwagger(); - Operation operation = swagger.getPath("/add").getPost(); - - ResponsesMeta meta = new ResponsesMeta(); - meta.init(null, "gen", swagger, operation, int.class); - - ResponseMeta resp = meta.findResponseMeta(200); - Assert.assertEquals(int.class, resp.getJavaType().getRawClass()); - - resp = meta.findResponseMeta(201); - Assert.assertEquals(int.class, resp.getJavaType().getRawClass()); - - resp = meta.findResponseMeta(400); - Assert.assertEquals(String.class, resp.getJavaType().getRawClass()); - - resp = meta.findResponseMeta(401); - Assert.assertEquals(Long.class, resp.getJavaType().getRawClass()); - Assert.assertEquals(Integer.class, resp.getHeaders().get("h1").getRawClass()); - - resp = meta.findResponseMeta(500); - Assert.assertEquals(CommonExceptionData.class, resp.getJavaType().getRawClass()); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/core/TestException.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/core/TestException.java new file mode 100644 index 00000000000..743b77f8acd --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/core/TestException.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.core; + +import java.lang.reflect.InvocationTargetException; + +import jakarta.ws.rs.core.Response.Status; + +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.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class TestException { + @Test + public void testCommonExceptionData() { + CommonExceptionData oData = new CommonExceptionData(); + oData.setMessage("this is Common exception message"); + Assertions.assertEquals("this is Common exception message", oData.getMessage()); + + oData = new CommonExceptionData("this is a test"); + Assertions.assertEquals("this is a test", oData.getMessage()); + Assertions.assertEquals("CommonExceptionData [message=this is a test]", oData.toString()); + } + + @Test + public void testInvocationException() { + InvocationException oExceptionIn = new InvocationException(Status.OK, "I am gone now"); + oExceptionIn = ExceptionFactory.convertConsumerException(new Throwable()); + Assertions.assertEquals(490, oExceptionIn.getStatusCode()); + + oExceptionIn = ExceptionFactory.convertConsumerException(new Throwable(), "abc"); + Assertions.assertEquals(490, oExceptionIn.getStatusCode()); + Assertions.assertEquals("abc", ((CommonExceptionData) oExceptionIn.getErrorData()).getMessage()); + + oExceptionIn = ExceptionFactory.convertProducerException(new Throwable()); + Assertions.assertEquals(590, oExceptionIn.getStatusCode()); + + oExceptionIn = ExceptionFactory.convertProducerException(new Throwable(), "abcd"); + Assertions.assertEquals(590, oExceptionIn.getStatusCode()); + Assertions.assertEquals("abcd", ((CommonExceptionData) oExceptionIn.getErrorData()).getMessage()); + + oExceptionIn = + ExceptionFactory.convertConsumerException(new InvocationException(Status.OK, new String("fake-object"))); + Assertions.assertEquals(200, oExceptionIn.getStatusCode()); + + oExceptionIn = ExceptionFactory.convertConsumerException(new InvocationTargetException(new Throwable())); + Assertions.assertNotEquals("java.lang.Throwable", oExceptionIn.getMessage()); + + InvocationException oTemp = new InvocationException(Status.OK, new CommonExceptionData("testObject")); + Assertions.assertEquals("OK", oTemp.getReasonPhrase()); + Assertions.assertEquals("CommonExceptionData [message=testObject]", (oTemp.getErrorData().toString())); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/core/TestResponse.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/core/TestResponse.java new file mode 100644 index 00000000000..1eaa0dfcc09 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/core/TestResponse.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF 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 jakarta.ws.rs.core.Response.Status; + +import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; +import org.apache.servicecomb.swagger.invocation.AsyncResponse; +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.ExceptionFactory; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class TestResponse { + Response response; + + AsyncResponse ar = new AsyncResponse() { + @Override + public void handle(Response resp) { + response = resp; + } + }; + + @Test + public void testAr() { + ar.success(Status.ACCEPTED, 1); + Assertions.assertTrue(response.isSucceed()); + Assertions.assertFalse(response.isFailed()); + Assertions.assertEquals(1, (int) response.getResult()); + Assertions.assertEquals(Status.ACCEPTED.getStatusCode(), response.getStatusCode()); + Assertions.assertEquals(Status.ACCEPTED.getReasonPhrase(), response.getReasonPhrase()); + Assertions.assertEquals(Status.ACCEPTED, response.getStatus()); + + ar.success(2); + Assertions.assertEquals(2, (int) response.getResult()); + Assertions.assertEquals(Status.OK.getStatusCode(), response.getStatusCode()); + + Response r = Response.succResp(3); + ar.complete(r); + Assertions.assertEquals(r, response); + + ar.consumerFail(new RuntimeExceptionWithoutStackTrace("abc")); + CommonExceptionData data = (CommonExceptionData) ((InvocationException) response.getResult()).getErrorData(); + Assertions.assertEquals("Unexpected consumer error, please check logs for details", data.getMessage()); + Assertions.assertEquals(ExceptionFactory.CONSUMER_INNER_STATUS_CODE, response.getStatusCode()); + + ar.fail(InvocationType.CONSUMER, new RuntimeExceptionWithoutStackTrace("abc")); + data = (CommonExceptionData) ((InvocationException) response.getResult()).getErrorData(); + Assertions.assertEquals("Unexpected consumer error, please check logs for details", data.getMessage()); + Assertions.assertEquals(ExceptionFactory.CONSUMER_INNER_STATUS_CODE, response.getStatusCode()); + + InvocationException consumerException = new InvocationException(300, "abc", "def"); + ar.consumerFail(consumerException); + Assertions.assertEquals("def", ((InvocationException) response.getResult()).getErrorData()); + Assertions.assertEquals(300, response.getStatusCode()); + + ar.fail(InvocationType.CONSUMER, consumerException); + Assertions.assertEquals("def", ((InvocationException) response.getResult()).getErrorData()); + Assertions.assertEquals(300, response.getStatusCode()); + + ar.producerFail(new RuntimeExceptionWithoutStackTrace("abc")); + data = (CommonExceptionData) ((InvocationException) response.getResult()).getErrorData(); + Assertions.assertEquals("Unexpected producer error, please check logs for details", data.getMessage()); + Assertions.assertEquals(ExceptionFactory.PRODUCER_INNER_STATUS_CODE, response.getStatusCode()); + + ar.fail(InvocationType.PROVIDER, new RuntimeExceptionWithoutStackTrace("abc")); + data = (CommonExceptionData) ((InvocationException) response.getResult()).getErrorData(); + Assertions.assertEquals("Unexpected producer error, please check logs for details", data.getMessage()); + Assertions.assertEquals(ExceptionFactory.PRODUCER_INNER_STATUS_CODE, response.getStatusCode()); + + InvocationException producerException = new InvocationException(500, "abc", "def"); + ar.producerFail(producerException); + Assertions.assertEquals("def", ((InvocationException) response.getResult()).getErrorData()); + Assertions.assertEquals(500, response.getStatusCode()); + + ar.fail(InvocationType.PROVIDER, producerException); + Assertions.assertEquals("def", ((InvocationException) response.getResult()).getErrorData()); + Assertions.assertEquals(500, response.getStatusCode()); + } + + @Test + public void test() { + Response r = Response.create(200, "200", 2); + Assertions.assertEquals(200, r.getStatusCode()); + Assertions.assertEquals(2, (int) r.getResult()); + Response r1 = r.build(); + Assertions.assertEquals(r, r1); + + r = Response.create(300, "300", 3); + Assertions.assertEquals(300, r.getStatusCode()); + Assertions.assertEquals("300", r.getReasonPhrase()); + Assertions.assertEquals(3, ((InvocationException) r.getResult()).getErrorData()); + + r = Response.createSuccess(Status.OK, 2); + Assertions.assertEquals(200, r.getStatusCode()); + Assertions.assertEquals(2, (int) r.getResult()); + + r = Response.success(2, Status.OK); + Assertions.assertEquals(200, r.getStatusCode()); + Assertions.assertEquals(2, (int) r.getResult()); + + r = Response.createFail(InvocationType.CONSUMER, "abc"); + Assertions.assertEquals("CommonExceptionData [message=abc]", + ((InvocationException) r.getResult()).getErrorData().toString()); + Assertions.assertEquals(490, r.getStatusCode()); + + r = Response.createFail(InvocationType.PROVIDER, "def"); + Assertions.assertEquals("CommonExceptionData [message=def]", + ((InvocationException) r.getResult()).getErrorData().toString()); + Assertions.assertEquals(590, r.getStatusCode()); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/engine/TestSwaggerEnvironment.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/engine/TestSwaggerEnvironment.java new file mode 100644 index 00000000000..0393af06ddb --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/engine/TestSwaggerEnvironment.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.swagger.engine; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.hamcrest.core.IsNull.nullValue; + +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.models.ProducerImpl; +import org.hamcrest.MatcherAssert; +import org.junit.BeforeClass; +import org.junit.Test; + +import io.swagger.v3.oas.models.OpenAPI; +import org.junit.jupiter.api.Assertions; + +public class TestSwaggerEnvironment { + private static final SwaggerEnvironment env = new SwaggerEnvironment(); + + private static SwaggerProducer producer; + + @BeforeClass + public static void init() { + producer = env.createProducer(new ProducerImpl(), null); + } + + @Test + public void ableToFindVisibleMethod() { + MatcherAssert.assertThat(producer.findOperation("visibleMethod"), is(notNullValue())); + } + + @Test + public void unableToFindHiddenMethod() { + MatcherAssert.assertThat(producer.findOperation("hiddenMethod"), is(nullValue())); + } + + interface ConsumerIntf { + void exist(); + + void notExist(); + } + + interface ContractIntf { + void exist(); + } + + @Test + public void createConsumer_consumerMethodSetBigger() { + OpenAPI swagger = SwaggerGenerator.generate(ContractIntf.class); + SwaggerConsumer swaggerConsumer = env.createConsumer(ConsumerIntf.class, swagger); + + Assertions.assertNotNull(swaggerConsumer.findOperation("exist")); + Assertions.assertNull(swaggerConsumer.findOperation("notExist")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/engine/TestSwaggerProducerOperation.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/engine/TestSwaggerProducerOperation.java new file mode 100644 index 00000000000..8f4d557bc67 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/engine/TestSwaggerProducerOperation.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.swagger.engine; + +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.invocation.models.PojoImpl; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestSwaggerProducerOperation { + private static final SwaggerEnvironment env = new SwaggerEnvironment(); + + private static SwaggerProducer producer; + + @Test + public void testGetParameterType() { + PojoImpl pojo = new PojoImpl(); + producer = env.createProducer(pojo, null); + + SwaggerProducerOperation swaggerProducerOperation = producer.findOperation("testBytes"); + Assertions.assertEquals(true, + swaggerProducerOperation.getSwaggerOperation().getOperation().getRequestBody() != null); + Assertions.assertEquals(null, swaggerProducerOperation.getSwaggerParameterType("bytes")); + + swaggerProducerOperation = producer.findOperation("testSimple"); + Assertions.assertEquals(true, + swaggerProducerOperation.getSwaggerOperation().getOperation().getRequestBody() != null); + Assertions.assertEquals(Object.class, swaggerProducerOperation.getSwaggerParameterType( + (String) swaggerProducerOperation.getSwaggerOperation().getOperation().getRequestBody().getExtensions() + .get(SwaggerConst.EXT_BODY_NAME))); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/TestSwaggerInvocation.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/TestSwaggerInvocation.java new file mode 100644 index 00000000000..d5533d84cca --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/TestSwaggerInvocation.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.swagger.invocation; + +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestSwaggerInvocation { + @Test + public void construct_withContext() { + InvocationContext parentContext = new InvocationContext(); + parentContext.addContext("k", "v"); + parentContext.addLocalContext("k", 1); + ContextUtils.setInvocationContext(parentContext); + + try { + SwaggerInvocation invocation = new SwaggerInvocation(); + Assertions.assertSame(parentContext, invocation.getParentContext()); + Assertions.assertEquals("v", invocation.getContext("k")); + Assertions.assertEquals(1, (int) invocation.getLocalContext("k")); + } finally { + ContextUtils.removeInvocationContext(); + } + } + + @Test + public void construct_noContext() { + SwaggerInvocation invocation = new SwaggerInvocation(); + Assertions.assertNull(invocation.getParentContext()); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV1V1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV1V1.java new file mode 100644 index 00000000000..a330f59beb7 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV1V1.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBeanParamV1; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import io.swagger.v3.oas.models.OpenAPI; + +@SuppressWarnings("unchecked") +public class TestJaxrsV1V1 { + @Test + public void should_mapper_consumer_multi_args_to_swagger_multi_args() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + interface ConsumerAddV1_diff_order { + int add(int y, int x); + } + + @Test + public void should_mapper_consumer_multi_args_to_swagger_multi_args_with_diff_order() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1_diff_order.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void should_mapper_consumer_multi_args_to_swagger_multi_args_gen_by_BeanParam() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void should_mapper_consumer_multi_args_to_swagger_body() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void should_mapper_consumer_wrapped_body_to_swagger_multi_args() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void should_mapper_consumer_wrapped_body_to_swagger_multi_args_gen_by_BeanParam() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void should_mapper_consumer_body_to_swagger_body() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV1.class); + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV1V2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV1V2.java new file mode 100644 index 00000000000..f1800371ef1 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV1V2.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.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBeanParamV2; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBodyV2; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddV2; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import io.swagger.v3.oas.models.OpenAPI; + +@SuppressWarnings("unchecked") +public class TestJaxrsV1V2 { + @Test + public void add_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertNull(result.get("x-z")); + } + + @Test + public void add_addBeanParam() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertNull(result.get("x-z")); + } + + @Test + public void add_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertNull(result.get("x-z")); + } + + @Test + public void addBody_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertNull(result.get("x-z")); + } + + @Test + public void addBody_addBeanParam() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertNull(result.get("x-z")); + } + + @Test + public void addBody_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV2.class); + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV2V1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV2V1.java new file mode 100644 index 00000000000..d344be6a193 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV2V1.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV2; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV2; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBeanParamV1; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import io.swagger.v3.oas.models.OpenAPI; + +@SuppressWarnings("unchecked") +public class TestJaxrsV2V1 { + @Test + public void add_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("x-z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void add_addBeanParam() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("x-z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void add_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("x-z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBody_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBody_addBeanParam() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBody_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV1.class); + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV2V2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV2V2.java new file mode 100644 index 00000000000..44f16497a27 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV2V2.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV2; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV2; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBeanParamV2; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBodyV2; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddV2; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import io.swagger.v3.oas.models.OpenAPI; + +@SuppressWarnings("unchecked") +public class TestJaxrsV2V2 { + @Test + public void add_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertEquals(3, (int) result.get("x-z")); + } + + @Test + public void add_addBeanParam() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertEquals(3, (int) result.get("x-z")); + } + + @Test + public void add_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertEquals(3, (int) result.get("x-z")); + } + + @Test + public void addBody_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertEquals(3, (int) result.get("x-z")); + } + + @Test + public void addBody_addBeanParam() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBeanParamV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertEquals(3, (int) result.get("x-z")); + } + + @Test + public void addBody_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(JaxrsAddBodyV2.class); + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoOneArg.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoOneArg.java new file mode 100644 index 00000000000..35ebc5ccaa9 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoOneArg.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; +import org.apache.servicecomb.foundation.test.scaffolding.model.User; +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerOneArg; +import org.apache.servicecomb.swagger.invocation.schemas.PojoOneArg; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.swagger.v3.oas.models.OpenAPI; + +@SuppressWarnings("unchecked") +public class TestPojoOneArg { + @Test + public void should_mapper_consumer_simple_to_swagger_body() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoOneArg.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerOneArg.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("simple").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("name", "name"); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("simpleBody"); + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals("name", result.get("name")); + } + + @Test + public void should_mapper_consumer_bean_to_swagger_body() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoOneArg.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerOneArg.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("bean").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("user", new User()); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + Assertions.assertSame(arguments.get("user"), result.get("user")); + } + + @Test + public void should_mapper_consumer_enum_to_swagger_body_field() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoOneArg.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerOneArg.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("enumBody").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("color", Color.BLUE); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("enumBodyBody"); + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals(Color.BLUE, result.get("color")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV1V1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV1V1.java new file mode 100644 index 00000000000..ca716ec2cdb --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV1V1.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddWithContext; +import org.apache.servicecomb.swagger.invocation.schemas.PojoAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.PojoAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import io.swagger.v3.oas.models.OpenAPI; + +@SuppressWarnings("unchecked") +public class TestPojoV1V1 { + @Test + public void add_add_class() { + add_add(PojoAddV1.class); + } + + @Test + public void add_add_interface() { + add_add(ConsumerAddV1.class); + } + + public void add_add(Class clazz) { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(clazz); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void add_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBody_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); + } + + @Test + public void addBody_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV1.class); + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + AddWrapperV1 wrapperV1 = (AddWrapperV1) result.get("addBody"); + Assertions.assertEquals(1, wrapperV1.getX()); + Assertions.assertEquals(2, wrapperV1.y); + + Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); + } + + @Test + public void addWithContext_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddWithContext.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + InvocationContext invocationContext = new InvocationContext(); + invocationContext.addContext("k1", "v1"); + invocationContext.addContext("k2", "v2"); + invocationContext.addLocalContext("k3", "v3"); + invocationContext.addLocalContext("k4", "v4"); + + Map arguments = new HashMap<>(); + arguments.put("context", invocationContext); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, result.get("x")); + Assertions.assertEquals(2, result.get("y")); + + Assertions.assertEquals(2, invocation.getContext().size()); + Assertions.assertEquals("v1", invocation.getContext().get("k1")); + Assertions.assertEquals("v2", invocation.getContext().get("k2")); + + Assertions.assertEquals(2, invocation.getLocalContext().size()); + Assertions.assertEquals("v3", invocation.getLocalContext().get("k3")); + Assertions.assertEquals("v4", invocation.getLocalContext().get("k4")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV1V2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV1V2.java new file mode 100644 index 00000000000..ab7f3a0bd36 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV1V2.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.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.PojoAddBodyV2; +import org.apache.servicecomb.swagger.invocation.schemas.PojoAddV2; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import io.swagger.v3.oas.models.OpenAPI; + +@SuppressWarnings("unchecked") +public class TestPojoV1V2 { + @Test + public void add_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void add_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBody_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); + } + + @Test + public void addBody_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV2V1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV2V1.java new file mode 100644 index 00000000000..6eb38a81679 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV2V1.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV2; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV2; +import org.apache.servicecomb.swagger.invocation.schemas.PojoAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.PojoAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.swagger.v3.oas.models.OpenAPI; + +@SuppressWarnings("unchecked") +public class TestPojoV2V1 { + @Test + public void add_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("x-z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void add_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("x-z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBody_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); + } + + @Test + public void addBody_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV2V2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV2V2.java new file mode 100644 index 00000000000..3e85c77c035 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestPojoV2V2.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV2; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV2; +import org.apache.servicecomb.swagger.invocation.schemas.PojoAddBodyV2; +import org.apache.servicecomb.swagger.invocation.schemas.PojoAddV2; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import io.swagger.v3.oas.models.OpenAPI; + +@SuppressWarnings("unchecked") +public class TestPojoV2V2 { + @Test + public void add_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertEquals(3, (int) result.get("x-z")); + } + + @Test + public void add_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertEquals(3, (int) result.get("x-z")); + } + + @Test + public void addBody_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); + } + + @Test + public void addBody_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(PojoAddBodyV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(arguments.get("addBody"), result.get("addBody")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV1V1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV1V1.java new file mode 100644 index 00000000000..b2f905b3090 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV1V1.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddWrapperV1; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; +import org.junit.Test; + +import io.swagger.v3.oas.models.OpenAPI; +import org.junit.jupiter.api.Assertions; + +@SuppressWarnings("unchecked") +public class TestSpringmvcV1V1 { + @Test + public void add_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void add_addWrapper() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void add_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBody_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBody_addWrapper() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBody_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV1V2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV1V2.java new file mode 100644 index 00000000000..e81c1f70424 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV1V2.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.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddBodyV2; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddV2; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddWrapperV2; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; +import org.junit.Test; + +import io.swagger.v3.oas.models.OpenAPI; +import org.junit.jupiter.api.Assertions; + +@SuppressWarnings("unchecked") +public class TestSpringmvcV1V2 { + @Test + public void add_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertNull(result.get("x-z")); + } + + @Test + public void add_addWrapper() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertNull(result.get("x-z")); + } + + @Test + public void add_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertNull(result.get("x-z")); + } + + @Test + public void addBody_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertNull(result.get("x-z")); + } + + @Test + public void addBody_addWrapper() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertNull(result.get("x-z")); + } + + @Test + public void addBody_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV1.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV1(1, 2)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV2V1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV2V1.java new file mode 100644 index 00000000000..65e3475fb39 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV2V1.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV2; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV2; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddWrapperV1; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; +import org.junit.Test; + +import io.swagger.v3.oas.models.OpenAPI; +import org.junit.jupiter.api.Assertions; + +@SuppressWarnings("unchecked") +public class TestSpringmvcV2V1 { + @Test + public void add_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("x-z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void add_addWrapper() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("x-z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void add_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBody_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBody_addWrapper() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBody_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV1.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV2V2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV2V2.java new file mode 100644 index 00000000000..0e5fcb440f6 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV2V2.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.arguments.consumer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentsMapper; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddBodyV2; +import org.apache.servicecomb.swagger.invocation.schemas.ConsumerAddV2; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddBodyV2; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddV2; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddWrapperV2; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.swagger.v3.oas.models.OpenAPI; + +@SuppressWarnings("unchecked") +public class TestSpringmvcV2V2 { + @Test + public void add_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertEquals(3, (int) result.get("x-z")); + } + + @Test + public void add_addWrapper() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertEquals(3, (int) result.get("x-z")); + } + + @Test + public void add_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + arguments.put("z", 3); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + result = (Map) result.get("addBody"); + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertEquals(3, (int) result.get("x-z")); + } + + @Test + public void addBody_add() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertEquals(3, (int) result.get("x-z")); + } + + @Test + public void addBody_addWrapper() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddWrapperV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + Assertions.assertEquals(3, (int) result.get("x-z")); + } + + @Test + public void addBody_addBody() { + SwaggerEnvironment environment = new SwaggerEnvironment(); + OpenAPI swagger = SwaggerGenerator.generate(SpringmvcAddBodyV2.class); + + SwaggerConsumer swaggerConsumer = environment.createConsumer(ConsumerAddBodyV2.class, swagger); + ArgumentsMapper mapper = swaggerConsumer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("addBody", new AddWrapperV2(1, 2, 3)); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); + + Assertions.assertSame(result.get("addBody"), arguments.get("addBody")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestJaxrs.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestJaxrs.java new file mode 100644 index 00000000000..fa9739866b8 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestJaxrs.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.swagger.invocation.arguments.producer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.engine.SwaggerProducer; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBeanParamV1; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.JaxrsAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddBeanParamV1; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestJaxrs { + @Test + public void add() { + SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new JaxrsAddV1(), null); + ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBeanParam() { + SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new JaxrsAddBeanParamV1(), null); + ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + AddBeanParamV1 paramV1 = (AddBeanParamV1) result.get("wrapper"); + Assertions.assertEquals(1, paramV1.getX()); + Assertions.assertEquals(2, paramV1.y); + } + + @Test + public void addBody() { + SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new JaxrsAddBodyV1(), null); + ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); + + AddWrapperV1 addBody = new AddWrapperV1(); + Map arguments = new HashMap<>(); + arguments.put("addBody", addBody); + + SwaggerInvocation invocation = new SwaggerInvocation(); + Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + Assertions.assertSame(addBody, result.get("addBody")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestPojo.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestPojo.java new file mode 100644 index 00000000000..a42a58c8388 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestPojo.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.swagger.invocation.arguments.producer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.engine.SwaggerProducer; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.schemas.PojoAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.PojoAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.PojoAddWithContextV1; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class TestPojo { + static Map addBody = new HashMap<>(); + + static { + addBody.put("x", 1); + addBody.put("y", 2); + } + + @Test + public void add() { + SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new PojoAddV1(), null); + ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); + + SwaggerInvocation invocation = new SwaggerInvocation(); + Map arguments = new HashMap<>(); + arguments.put("addBody", addBody); + + Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addBody() { + SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new PojoAddBodyV1(), null); + ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); + + SwaggerInvocation invocation = new SwaggerInvocation(); + Map arguments = new HashMap<>(); + arguments.put("addBody", addBody); + + Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + Assertions.assertSame(addBody, result.get("addBody")); + } + + @Test + public void addWithContext_add() { + SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new PojoAddWithContextV1(), null); + ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); + + SwaggerInvocation invocation = new SwaggerInvocation(); + Map arguments = new HashMap<>(); + arguments.put("addBody", addBody); + + Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); + + Assertions.assertEquals(3, result.size()); + Assertions.assertSame(invocation, result.get("context")); + Assertions.assertEquals(1, result.get("x")); + Assertions.assertEquals(2, result.get("y")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestPojoOneArg.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestPojoOneArg.java new file mode 100644 index 00000000000..cd01c6a6bba --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestPojoOneArg.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.swagger.invocation.arguments.producer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.engine.SwaggerProducer; +import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.schemas.PojoOneArg; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class TestPojoOneArg { + @Test + public void should_mapper_swagger_wrapped_body_field_to_producer_enum() { + SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new PojoOneArg(), null); + SwaggerProducerOperation swaggerProducerOperation = swaggerProducer.findOperation("enumBody"); + Assertions.assertEquals("enumBodyBody", + swaggerProducerOperation.getSwaggerOperation().getOperation().getRequestBody().getExtensions().get(SwaggerConst.EXT_BODY_NAME)); + + ProducerArgumentsMapper mapper = swaggerProducerOperation.getArgumentsMapper(); + + SwaggerInvocation invocation = new SwaggerInvocation(); + Map swaggerArguments = new HashMap<>(); + Map arguments = new HashMap<>(); + arguments.put("color", "BLUE"); + swaggerArguments.put("enumBodyBody", arguments); + + Map result = mapper.swaggerArgumentToInvocationArguments(invocation, swaggerArguments); + + Assertions.assertEquals(1, result.size()); + Assertions.assertSame(Color.BLUE, result.get("color")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestSpringmvc.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestSpringmvc.java new file mode 100644 index 00000000000..cc4119aa480 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/producer/TestSpringmvc.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.swagger.invocation.arguments.producer; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.engine.SwaggerProducer; +import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddBodyV1; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddV1; +import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddWrapperV1; +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestSpringmvc { + @Test + public void add() { + SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new SpringmvcAddV1(), null); + ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); + + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(1, (int) result.get("x")); + Assertions.assertEquals(2, (int) result.get("y")); + } + + @Test + public void addWrapper() { + SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new SpringmvcAddWrapperV1(), null); + ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); + + Map arguments = new HashMap<>(); + arguments.put("x", 1); + arguments.put("y", 2); + SwaggerInvocation invocation = new SwaggerInvocation(); + + Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + AddWrapperV1 paramV1 = (AddWrapperV1) result.get("wrapper"); + Assertions.assertEquals(1, paramV1.getX()); + Assertions.assertEquals(2, paramV1.y); + } + + @Test + public void addBody() { + SwaggerProducer swaggerProducer = new SwaggerEnvironment().createProducer(new SpringmvcAddBodyV1(), null); + ProducerArgumentsMapper mapper = swaggerProducer.findOperation("add").getArgumentsMapper(); + + AddWrapperV1 addBody = new AddWrapperV1(); + Map arguments = new HashMap<>(); + arguments.put("addBody", addBody); + + SwaggerInvocation invocation = new SwaggerInvocation(); + Map result = mapper.swaggerArgumentToInvocationArguments(invocation, arguments); + + Assertions.assertEquals(1, result.size()); + Assertions.assertSame(addBody, result.get("addBody")); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/context/TestContextUtils.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/context/TestContextUtils.java new file mode 100644 index 00000000000..725114cf5eb --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/context/TestContextUtils.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.swagger.invocation.context; + +import java.util.concurrent.CompletableFuture; + +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class TestContextUtils { + @Test + public void getFromCompletableFuture() { + Assertions.assertNull(ContextUtils.getFromCompletableFuture(new CompletableFuture<>())); + + InvocationContext context = new InvocationContext(); + Assertions + .assertSame(context, ContextUtils.getFromCompletableFuture(new InvocationContextCompletableFuture<>(context))); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/context/TestInvocationContext.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/context/TestInvocationContext.java new file mode 100644 index 00000000000..b52a3054056 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/context/TestInvocationContext.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.context; + +import java.util.HashMap; +import java.util.Map; + +import jakarta.ws.rs.core.Response.Status.Family; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestInvocationContext { + @Test + public void addContext() { + InvocationContext invocationContext = new InvocationContext(); + invocationContext.addContext("key1", "value1"); + Assertions.assertEquals(1, invocationContext.getContext().size()); + Assertions.assertEquals("value1", invocationContext.getContext("key1")); + + Map otherContext = new HashMap<>(); + otherContext.put("key2", "value2"); + invocationContext.addContext(otherContext); + Assertions.assertEquals(2, invocationContext.getContext().size()); + Assertions.assertEquals("value2", invocationContext.getContext("key2")); + + InvocationContext invocationContext2 = new InvocationContext(); + Map otherContext2 = new HashMap<>(); + otherContext2.put("key3", "value3"); + invocationContext2.setContext(otherContext2); + invocationContext.addContext(invocationContext2); + Assertions.assertEquals(3, invocationContext.getContext().size()); + } + + @Test + public void mergeMapContext() { + InvocationContext invocationContext = new InvocationContext(); + Map otherContext = new HashMap<>(); + otherContext.put("key1", "value1"); + //otherContext's size is large than old. + invocationContext.mergeContext(otherContext); + Assertions.assertEquals(1, invocationContext.getContext().size()); + Assertions.assertEquals("value1", invocationContext.getContext("key1")); + + otherContext.put("key1", "value2"); + //otherContext's size is not large than old. + invocationContext.mergeContext(otherContext); + Assertions.assertEquals(1, invocationContext.getContext().size()); + Assertions.assertEquals("value2", invocationContext.getContext("key1")); + } + + @Test + public void mergeInvocationContext() { + InvocationContext invocationContext = new InvocationContext(); + Map otherContext = new HashMap<>(); + otherContext.put("key1", "value1"); + InvocationContext context2 = new InvocationContext(); + context2.setContext(otherContext); + invocationContext.mergeContext(context2); + Assertions.assertEquals(1, invocationContext.getContext().size()); + Assertions.assertEquals("value1", invocationContext.getContext("key1")); + } + + @Test + public void addLocalContext() { + InvocationContext invocationContext = new InvocationContext(); + invocationContext.addLocalContext("key1", "value1"); + Assertions.assertEquals(1, invocationContext.getLocalContext().size()); + Assertions.assertEquals("value1", invocationContext.getLocalContext("key1")); + + Map otherContext = new HashMap<>(); + otherContext.put("key2", "value2"); + invocationContext.addLocalContext(otherContext); + Assertions.assertEquals(2, invocationContext.getLocalContext().size()); + Assertions.assertEquals("value2", invocationContext.getLocalContext("key2")); + } + + @Test + public void setStatus() { + InvocationContext invocationContext = new InvocationContext(); + invocationContext.setStatus(200); + System.out.println(invocationContext.getStatus().getFamily()); + Assertions.assertEquals(200, invocationContext.getStatus().getStatusCode()); + Assertions.assertEquals("OK", invocationContext.getStatus().getReasonPhrase()); + Assertions.assertEquals(Family.SUCCESSFUL, invocationContext.getStatus().getFamily()); + + invocationContext.setStatus(200, "TEST"); + Assertions.assertEquals(200, invocationContext.getStatus().getStatusCode()); + Assertions.assertEquals("TEST", invocationContext.getStatus().getReasonPhrase()); + Assertions.assertEquals(Family.SUCCESSFUL, invocationContext.getStatus().getFamily()); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartArrayConverterTest.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartArrayConverterTest.java new file mode 100644 index 00000000000..908d6ece56a --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartArrayConverterTest.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.swagger.invocation.converter.impl.part; + +import java.util.Arrays; + +import jakarta.servlet.http.Part; + +import org.apache.servicecomb.foundation.common.part.FilePart; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class PartListToPartArrayConverterTest { + PartListToPartArrayConverter converter = new PartListToPartArrayConverter(); + + @Test + public void getSrcType() { + Assertions.assertEquals("java.util.List", converter.getSrcType().getTypeName()); + } + + @Test + public void getTargetType() { + Assertions.assertEquals(Part[].class.getCanonicalName(), converter.getTargetType().getTypeName()); + } + + @Test + public void convert() { + Object parts = converter.convert(Arrays.asList(new FilePart("name", "file"))); + MatcherAssert.assertThat(parts, Matchers.instanceOf(Part[].class)); + } + + @Test + public void should_got_null_when_convert_null() { + Assertions.assertNull(converter.convert(null)); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartListConverterTest.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartListConverterTest.java new file mode 100644 index 00000000000..33f96ba9e25 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartListConverterTest.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.swagger.invocation.converter.impl.part; + +import java.util.Arrays; +import java.util.List; + +import jakarta.servlet.http.Part; + +import org.apache.servicecomb.foundation.common.part.FilePart; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PartListToPartListConverterTest { + PartListToPartListConverter converter = new PartListToPartListConverter(); + + @Test + public void getSrcType() { + Assertions.assertEquals("java.util.List", converter.getSrcType().getTypeName()); + } + + @Test + public void getTargetType() { + Assertions.assertEquals("java.util.List", converter.getTargetType().getTypeName()); + } + + @Test + public void convert() { + List parts = Arrays.asList(new FilePart("name", "file")); + Assertions.assertSame(parts, converter.convert(parts)); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartToPartConverterTest.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartToPartConverterTest.java new file mode 100644 index 00000000000..02e20c5f94a --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartToPartConverterTest.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.swagger.invocation.converter.impl.part; + +import jakarta.servlet.http.Part; + +import org.apache.servicecomb.foundation.common.part.FilePart; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class PartToPartConverterTest { + PartToPartConverter converter = new PartToPartConverter(); + + @Test + public void getSrcType() { + Assertions.assertEquals(Part.class.getName(), converter.getSrcType().getTypeName()); + } + + @Test + public void getTargetType() { + Assertions.assertEquals(Part.class.getName(), converter.getTargetType().getTypeName()); + } + + @Test + public void convert() { + Part part = new FilePart("name", "file"); + Assertions.assertSame(part, converter.convert(part)); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestBytesToPartConverter.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestBytesToPartConverter.java new file mode 100644 index 00000000000..409e53e2d3c --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestBytesToPartConverter.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.swagger.invocation.converter.impl.part; + +import jakarta.servlet.http.Part; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class TestBytesToPartConverter { + BytesToPartConverter converter = new BytesToPartConverter(); + + @Test + public void getSrcType() { + Assertions.assertSame(byte[].class, converter.getSrcType()); + } + + @Test + public void getTargetType() { + Assertions.assertEquals(Part.class.getName(), converter.getTargetType().getTypeName()); + } + + @Test + public void convert() { + Object part = converter.convert(new byte[] {}); + MatcherAssert.assertThat(part, Matchers.instanceOf(Part.class)); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestFileToPartConverter.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestFileToPartConverter.java new file mode 100644 index 00000000000..38b2378ab59 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestFileToPartConverter.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.swagger.invocation.converter.impl.part; + +import java.io.File; + +import jakarta.servlet.http.Part; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestFileToPartConverter { + FileToPartConverter converter = new FileToPartConverter(); + + @Test + public void getSrcType() { + Assertions.assertEquals(File.class.getName(), converter.getSrcType().getTypeName()); + } + + @Test + public void getTargetType() { + Assertions.assertEquals(Part.class.getName(), converter.getTargetType().getTypeName()); + } + + @Test + public void convert() { + File file = new File("abc"); + Object part = converter.convert(file); + MatcherAssert.assertThat(part, Matchers.instanceOf(Part.class)); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestInputStreamToPartConverter.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestInputStreamToPartConverter.java new file mode 100644 index 00000000000..a846900eeb2 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestInputStreamToPartConverter.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.swagger.invocation.converter.impl.part; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import jakarta.servlet.http.Part; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestInputStreamToPartConverter { + InputStreamToPartConverter converter = new InputStreamToPartConverter(); + + @Test + public void getSrcType() { + Assertions.assertEquals(InputStream.class.getName(), converter.getSrcType().getTypeName()); + } + + @Test + public void getTargetType() { + Assertions.assertEquals(Part.class.getName(), converter.getTargetType().getTypeName()); + } + + @Test + public void convert() { + Object part = converter.convert(new ByteArrayInputStream(new byte[] {})); + MatcherAssert.assertThat(part, Matchers.instanceOf(Part.class)); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestResourceToPartConverter.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestResourceToPartConverter.java new file mode 100644 index 00000000000..20dd2a757ee --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/TestResourceToPartConverter.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.swagger.invocation.converter.impl.part; + +import java.io.ByteArrayInputStream; + +import jakarta.servlet.http.Part; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.core.io.InputStreamResource; +import org.springframework.core.io.Resource; + +public class TestResourceToPartConverter { + ResourceToPartConverter converter = new ResourceToPartConverter(); + + @Test + public void getSrcType() { + Assertions.assertEquals(Resource.class.getName(), converter.getSrcType().getTypeName()); + } + + @Test + public void getTargetType() { + Assertions.assertEquals(Part.class.getName(), converter.getTargetType().getTypeName()); + } + + @Test + public void convert() { + Object part = converter.convert(new InputStreamResource(new ByteArrayInputStream(new byte[] {}))); + MatcherAssert.assertThat(part, Matchers.instanceOf(Part.class)); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/CommonExceptionDataTest.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/CommonExceptionDataTest.java new file mode 100644 index 00000000000..c12f149b825 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/CommonExceptionDataTest.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.swagger.invocation.exception; + +import static com.google.common.collect.ImmutableMap.of; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import io.vertx.core.json.Json; + +class CommonExceptionDataTest { + @Test + void should_not_include_code_in_json_when_code_is_null() { + CommonExceptionData data = new CommonExceptionData("msg"); + + assertThat(Json.encode(data)).isEqualTo("{\"message\":\"msg\"}"); + } + + @Test + void should_include_code_in_json_when_code_is_not_null() { + CommonExceptionData data = new CommonExceptionData("code", "msg"); + + String json = Json.encode(data); + @SuppressWarnings("unchecked") + Map obj = Json.decodeValue(json, Map.class); + + assertThat(obj).containsEntry("code", "code").containsEntry("message", "msg").hasSize(2); + } + + @Test + void should_include_dynamic_field_in_json() { + CommonExceptionData data = new CommonExceptionData("msg"); + data.putDynamic("k", "v"); + + assertThat(Json.encode(data)).isEqualTo("{\"message\":\"msg\",\"k\":\"v\"}"); + } + + @Test + void should_decode_dynamic_field_from_json() { + String json = "{\"message\":\"msg\",\"k\":\"v\"}"; + CommonExceptionData data = Json.decodeValue(json, CommonExceptionData.class); + + assertThat(data.getMessage()).isEqualTo("msg"); + assertThat(data.getDynamic()).isEqualTo(of("k", "v")); + assertThat(Json.encode(data)).isEqualTo(json); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/JaxrsImpl.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/JaxrsImpl.java new file mode 100644 index 00000000000..c1e9bd23f7b --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/JaxrsImpl.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.swagger.invocation.models; + +import java.util.Arrays; +import java.util.List; + +import jakarta.ws.rs.CookieParam; +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.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; + +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + +@Path("/JaxrsImpl") +@Produces(MediaType.APPLICATION_JSON) +public class JaxrsImpl { + @Path("/testTwoSimple") + @GET + public int testSimple(@PathParam("a") int a, @QueryParam("b") int b, @HeaderParam("c") int c) { + return a - b - c; + } + + @Path("/testObject") + @POST + public Person testObject(Person user) { + user.setName("hello " + user.getName()); + return user; + } + + @Path("/testSimpleAndObject") + @POST + public String testSimpleAndObject(@CookieParam("prefix") String prefix, Person user) { + return prefix + " " + user.getName(); + } + + @Path("/testContext") + @POST + public String testContext(InvocationContext context, @FormParam("form") String name) { + context.addContext("name", name); + return name + " sayhi"; + } + + @Path("/bytes") + @POST + public byte[] testBytes(byte[] input) { + return input; + } + + @Path("/listBytes") + @POST + public List testListBytes(List bytes) { + return bytes; + } + + @Path("/testArrayArray") + @POST + public String[] testArrayArray(String[] s) { + return s; + } + + @Path("/testArrayList") + @POST + public List testArrayList(String[] s) { + return Arrays.asList(s); + } + + @Path("/testListArray") + @POST + public String[] testListArray(List s) { + return s.toArray(new String[0]); + } + + @Path("/testListList") + @POST + public List testListList(List s) { + return s; + } + + @Path("/testObjectArrayArray") + @POST + public Person[] testObjectArrayArray(Person[] s) { + return s; + } + + @Path("/testObjectArrayList") + @POST + public List testObjectArrayList(Person[] s) { + return Arrays.asList(s); + } + + @Path("/testObjectListArray") + @POST + public Person[] testObjectListArray(List s) { + return s.toArray(new Person[0]); + } + + @Path("/testObjectListList") + @POST + public List testObjectListList(List s) { + return s; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/Person.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/Person.java new file mode 100644 index 00000000000..f013ad70e78 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/Person.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.swagger.invocation.models; + +public class Person { + private String name; + + public Person() { + } + + public Person(String name) { + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/PojoConsumerIntf.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/PojoConsumerIntf.java new file mode 100644 index 00000000000..c7a4f8f3877 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/PojoConsumerIntf.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.swagger.invocation.models; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + +import io.swagger.v3.oas.annotations.Operation; + +public interface PojoConsumerIntf { + int testSimple(int a, int b, int c); + + Person testObject(Person user); + + @Operation(operationId = "testObject", summary = "") + CompletableFuture testObjectAsync(Person user); + + String testSimpleAndObject(String prefix, Person user); + + String testContext(InvocationContext context, String name); + + List testListBytes(List bytes); + + byte[] testBytes(byte[] bytes); + + String[] testArrayArray(String[] s); + + List testArrayList(String[] s); + + String[] testListArray(List s); + + List testListList(List s); + + Person[] testObjectArrayArray(Person[] s); + + List testObjectArrayList(Person[] s); + + Person[] testObjectListArray(List s); + + List testObjectListList(List s); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/PojoImpl.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/PojoImpl.java new file mode 100644 index 00000000000..ba0760d21bb --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/PojoImpl.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.invocation.models; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + +import io.swagger.v3.oas.annotations.Operation; + +public class PojoImpl { + public int testSimple(int a, int b, int c) { + return a - b - c; + } + + public Person testObject(Person user) { + user.setName("hello " + user.getName()); + return user; + } + + @Operation(operationId = "testSimpleAndObject", summary = "") + public CompletableFuture testSimpleAndObjectAsync(String prefix, Person user) { + CompletableFuture future = new CompletableFuture<>(); + future.complete(prefix + " " + user.getName()); + return future; + } + + public String testContext(InvocationContext context, String name) { + context.addContext("name", name); + return name + " sayhi"; + } + + public List testListBytes(List bytes) { + return bytes; + } + + public byte[] testBytes(byte[] bytes) { + return bytes; + } + + public String[] testArrayArray(String[] s) { + return s; + } + + public List testArrayList(String[] s) { + return Arrays.asList(s); + } + + public String[] testListArray(List s) { + return s.toArray(new String[0]); + } + + public List testListList(List s) { + return s; + } + + public Person[] testObjectArrayArray(Person[] s) { + return s; + } + + public List testObjectArrayList(Person[] s) { + return Arrays.asList(s); + } + + public Person[] testObjectListArray(List s) { + return s.toArray(new Person[0]); + } + + public List testObjectListList(List s) { + return s; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/ProducerImpl.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/ProducerImpl.java new file mode 100644 index 00000000000..3461d823376 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/ProducerImpl.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.swagger.invocation.models; + + +import io.swagger.v3.oas.annotations.Operation; + +public class ProducerImpl { + @Operation(summary = "", hidden = true) + public int hiddenMethod(int a) { + return a; + } + + @Operation(summary = "") + public int visibleMethod(int a) { + return a; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/SpringmvcImpl.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/SpringmvcImpl.java new file mode 100644 index 00000000000..3013b9bb111 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/models/SpringmvcImpl.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.swagger.invocation.models; + +public class SpringmvcImpl { + +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/TestResponsesMeta.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/TestResponsesMeta.java new file mode 100644 index 00000000000..972ce6d7edd --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/TestResponsesMeta.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.response; + +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import com.fasterxml.jackson.databind.JavaType; + +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.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; + +public class TestResponsesMeta { + static class ResponseMetaImpl { + @ApiResponses({@ApiResponse(responseCode = "400", description = "", + content = {@Content(schema = @Schema(implementation = String.class))}), + @ApiResponse(responseCode = "401", description = "", + content = {@Content(schema = @Schema(implementation = String.class))}, + headers = {@Header(name = "h1", schema = @Schema(implementation = String.class))}) + }) + public int add(int x, int y) { + return x + y; + } + } + + @Test + public void test() { + OpenAPI swagger = SwaggerGenerator.generate(ResponseMetaImpl.class); + Operation operation = swagger.getPaths().get("/add").getPost(); + + ResponsesMeta meta = new ResponsesMeta(); + meta.init(swagger, operation); + + JavaType resp = meta.findResponseType(200); + Assertions.assertEquals(Integer.class, resp.getRawClass()); + + resp = meta.findResponseType(201); + Assertions.assertEquals(Integer.class, resp.getRawClass()); + + resp = meta.findResponseType(400); + Assertions.assertEquals(String.class, resp.getRawClass()); + + resp = meta.findResponseType(401); + Assertions.assertEquals(String.class, resp.getRawClass()); + + resp = meta.findResponseType(500); + // changed to Object for new version to keep user defined error data not lose and can be parsed. + Assertions.assertEquals(Object.class, resp.getRawClass()); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/consumer/TestConsumerResponseMapperFactories.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/consumer/TestConsumerResponseMapperFactories.java new file mode 100644 index 00000000000..6a198088e4d --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/consumer/TestConsumerResponseMapperFactories.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.swagger.invocation.response.consumer; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.Response; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +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.models.OpenAPI; + +public class TestConsumerResponseMapperFactories { + interface ConsumerResponseForTest { + String normal(); + + CompletableFuture async(); + + @ApiResponse(responseCode = "200", description = "", content = + {@Content(schema = @Schema(implementation = String.class))}) + Response scbResponse(); + + @ApiResponse(responseCode = "200", description = "", content = + {@Content(schema = @Schema(implementation = String.class))}) + jakarta.ws.rs.core.Response jaxrsResponse(); + + Optional optional(); + } + + SwaggerEnvironment environment = new SwaggerEnvironment(); + + SwaggerConsumer swaggerConsumer; + + String result = "abc"; + + Response response = Response.ok(result); + + @Before + public void setup() { + OpenAPI swagger = SwaggerGenerator.generate(ConsumerResponseForTest.class); + swaggerConsumer = environment.createConsumer(ConsumerResponseForTest.class, swagger); + } + + @Test + public void should_mapper_to_normal_string() { + SwaggerConsumerOperation operation = swaggerConsumer.findOperation("normal"); + assertThat(operation.getResponseMapper()).isInstanceOf(DefaultConsumerResponseMapper.class); + Assertions.assertEquals(result, operation.getResponseMapper().mapResponse(response)); + } + + @Test + public void should_mapper_to_completableFuture_element_string() { + SwaggerConsumerOperation operation = swaggerConsumer.findOperation("async"); + assertThat(operation.getResponseMapper()).isInstanceOf(DefaultConsumerResponseMapper.class); + Assertions.assertEquals(result, operation.getResponseMapper().mapResponse(response)); + } + + @Test + public void should_mapper_to_scbResponse_string() { + SwaggerConsumerOperation operation = swaggerConsumer.findOperation("scbResponse"); + assertThat(operation.getResponseMapper().getClass().getName()) + .startsWith(CseResponseConsumerResponseMapperFactory.class.getName()); + Response scbResponse = (Response) operation.getResponseMapper().mapResponse(response); + Assertions.assertEquals(result, scbResponse.getResult()); + } + + @Test + public void should_mapper_to_optional_string() { + SwaggerConsumerOperation operation = swaggerConsumer.findOperation("optional"); + assertThat(operation.getResponseMapper()).isInstanceOf(OptionalConsumerResponseMapper.class); + @SuppressWarnings("unchecked") + Optional optional = (Optional) operation.getResponseMapper().mapResponse(response); + Assertions.assertEquals(result, optional.get()); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/producer/TestProducerResponseMapperFactories.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/producer/TestProducerResponseMapperFactories.java new file mode 100644 index 00000000000..fd0fec59387 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/producer/TestProducerResponseMapperFactories.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.response.producer; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.engine.SwaggerProducer; +import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; +import org.apache.servicecomb.swagger.invocation.Response; +import org.junit.BeforeClass; +import org.junit.Test; + +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 jakarta.ws.rs.core.Response.Status; + +public class TestProducerResponseMapperFactories { + static class ResponseForTest { + public String normal() { + return "normal"; + } + + public CompletableFuture async() { + return CompletableFuture.completedFuture("async"); + } + + @ApiResponse(responseCode = "200", description = "", + content = {@Content(schema = @Schema(implementation = String.class))}) + public Response scbResponse() { + return Response.ok("scb"); + } + + @ApiResponse(responseCode = "200", description = "", + content = {@Content(schema = @Schema(implementation = String.class))}) + public jakarta.ws.rs.core.Response jaxrsResponse() { + return jakarta.ws.rs.core.Response.ok("jaxrs").build(); + } + + public Optional optional() { + return Optional.of("optional"); + } + } + + static SwaggerEnvironment environment = new SwaggerEnvironment(); + + static SwaggerProducer swaggerProducer; + + static ResponseForTest instance = new ResponseForTest(); + + static String result = "abc"; + + static Response response = Response.ok(result); + + @BeforeClass + public static void setup() { + swaggerProducer = environment.createProducer(instance); + } + + @Test + public void should_mapper_to_normal_string() { + SwaggerProducerOperation operation = swaggerProducer.findOperation("normal"); + assertThat(operation.getResponseMapper()).isInstanceOf(DefaultProducerResponseMapper.class); + assertThat((String) operation.getResponseMapper().mapResponse(Status.OK, instance.normal()).getResult()) + .isEqualTo("normal"); + } + + @Test + public void should_mapper_to_completableFuture_element_string() throws ExecutionException, InterruptedException { + SwaggerProducerOperation operation = swaggerProducer.findOperation("async"); + assertThat(operation.getResponseMapper()).isInstanceOf(DefaultProducerResponseMapper.class); + assertThat((String) operation.getResponseMapper().mapResponse(Status.OK, instance.async().get()).getResult()) + .isEqualTo("async"); + } + + @Test + public void should_mapper_to_scbResponse_string() { + SwaggerProducerOperation operation = swaggerProducer.findOperation("scbResponse"); + assertThat(operation.getResponseMapper().getClass().getName()) + .startsWith(CseResponseProducerResponseMapperFactory.class.getName()); + assertThat((String) operation.getResponseMapper().mapResponse(Status.OK, instance.scbResponse()).getResult()) + .isEqualTo("scb"); + } + + @Test + public void should_mapper_to_optional_string() { + SwaggerProducerOperation operation = swaggerProducer.findOperation("optional"); + assertThat(operation.getResponseMapper()).isInstanceOf(OptionalProducerResponseMapper.class); + assertThat((String) operation.getResponseMapper().mapResponse(Status.OK, instance.optional()).getResult()) + .isEqualTo("optional"); + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddBodyV1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddBodyV1.java new file mode 100644 index 00000000000..05b433d124c --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddBodyV1.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.swagger.invocation.schemas; + +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; + +public interface ConsumerAddBodyV1 { + int add(AddWrapperV1 addBody); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddBodyV2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddBodyV2.java new file mode 100644 index 00000000000..fa9f4d93594 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddBodyV2.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.swagger.invocation.schemas; + +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; + +public interface ConsumerAddBodyV2 { + int add(AddWrapperV2 addBody); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddV1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddV1.java new file mode 100644 index 00000000000..202bbfc39a3 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddV1.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.swagger.invocation.schemas; + +public interface ConsumerAddV1 { + int add(int x, int y); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddV2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddV2.java new file mode 100644 index 00000000000..9a86119ef2d --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddV2.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.swagger.invocation.schemas; + + +import io.swagger.v3.oas.annotations.Parameter; + +public interface ConsumerAddV2 { + int add(int x, int y, @Parameter(name = "x-z") int z); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddWithContext.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddWithContext.java new file mode 100644 index 00000000000..a797ec0b42b --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerAddWithContext.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.swagger.invocation.schemas; + +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + +public interface ConsumerAddWithContext { + int add(InvocationContext context, int x, int y); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerOneArg.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerOneArg.java new file mode 100644 index 00000000000..876b952d481 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/ConsumerOneArg.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.swagger.invocation.schemas; + +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; +import org.apache.servicecomb.foundation.test.scaffolding.model.User; + +public interface ConsumerOneArg { + void simple(String name); + + void bean(User user); + + void enumBody(Color color); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBeanParamV1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBeanParamV1.java new file mode 100644 index 00000000000..440fa289a98 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBeanParamV1.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.swagger.invocation.schemas; + +import jakarta.ws.rs.BeanParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.apache.servicecomb.swagger.invocation.schemas.models.AddBeanParamV1; + +@Path("/") +public class JaxrsAddBeanParamV1 { + @Path("/add") + @GET + public int add(@BeanParam AddBeanParamV1 wrapper) { + return wrapper.getX() + wrapper.y; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBeanParamV2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBeanParamV2.java new file mode 100644 index 00000000000..94a677a276e --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBeanParamV2.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.swagger.invocation.schemas; + +import jakarta.ws.rs.BeanParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.apache.servicecomb.swagger.invocation.schemas.models.AddBeanParamV2; + +@Path("/") +public interface JaxrsAddBeanParamV2 { + @Path("/add") + @GET + int add(@BeanParam AddBeanParamV2 wrapper); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBodyV1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBodyV1.java new file mode 100644 index 00000000000..274f3846bed --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBodyV1.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.swagger.invocation.schemas; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; + +@Path("/") +public class JaxrsAddBodyV1 { + @Path("/add") + @POST + public int add(AddWrapperV1 addBody) { + return addBody.getX() + addBody.y; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBodyV2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBodyV2.java new file mode 100644 index 00000000000..a1dc40c9b57 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddBodyV2.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.swagger.invocation.schemas; + +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; + +@Path("/") +public interface JaxrsAddBodyV2 { + @Path("/add") + @POST + int add(AddWrapperV2 addBody); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddV1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddV1.java new file mode 100644 index 00000000000..4051f5551c4 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddV1.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.swagger.invocation.schemas; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; + +@Path("/") +public class JaxrsAddV1 { + @Path("/add") + @GET + public int add(@QueryParam("x") int x, @QueryParam("y") int y) { + return x + y; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddV2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddV2.java new file mode 100644 index 00000000000..661dd749113 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/JaxrsAddV2.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.swagger.invocation.schemas; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; + +@Path("/") +public interface JaxrsAddV2 { + @Path("/add") + @GET + int add(@QueryParam("x") int x, @QueryParam("y") int y, @HeaderParam("x-z") int z); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddBodyV1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddBodyV1.java new file mode 100644 index 00000000000..c3c3907ce36 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddBodyV1.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.schemas; + +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; + +public class PojoAddBodyV1 { + public int add(AddWrapperV1 addBody) { + return addBody.getX() + addBody.y; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddBodyV2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddBodyV2.java new file mode 100644 index 00000000000..aa0531dd126 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddBodyV2.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.swagger.invocation.schemas; + +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; + +public interface PojoAddBodyV2 { + int add(AddWrapperV2 addBody); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddV1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddV1.java new file mode 100644 index 00000000000..32916c2a541 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddV1.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.swagger.invocation.schemas; + +public class PojoAddV1 { + public int add(int x, int y) { + return x + y; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddV2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddV2.java new file mode 100644 index 00000000000..4dba4abfa6e --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddV2.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.swagger.invocation.schemas; + +import io.swagger.v3.oas.annotations.Parameter; + +public interface PojoAddV2 { + int add(int x, int y, @Parameter(name = "x-z") int z); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddWithContextV1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddWithContextV1.java new file mode 100644 index 00000000000..26694feb425 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoAddWithContextV1.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.schemas; + +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + +public class PojoAddWithContextV1 { + public int add(InvocationContext context, int x, int y) { + return x + y; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoOneArg.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoOneArg.java new file mode 100644 index 00000000000..6172f47083b --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/PojoOneArg.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.swagger.invocation.schemas; + +import org.apache.servicecomb.foundation.test.scaffolding.model.Color; +import org.apache.servicecomb.foundation.test.scaffolding.model.User; + +public class PojoOneArg { + public void simple(String name) { + + } + + public void bean(User user) { + + } + + public void enumBody(Color color) { + + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddBodyV1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddBodyV1.java new file mode 100644 index 00000000000..de34aff2d79 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddBodyV1.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.swagger.invocation.schemas; + +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping +public class SpringmvcAddBodyV1 { + @PostMapping(path = "/add") + public int add(@RequestBody AddWrapperV1 addBody) { + return addBody.getX() + addBody.y; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddBodyV2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddBodyV2.java new file mode 100644 index 00000000000..a67a2e00da5 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddBodyV2.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.swagger.invocation.schemas; + +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping +public interface SpringmvcAddBodyV2 { + @PostMapping(path = "/add") + int add(@RequestBody AddWrapperV2 addBody); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddV1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddV1.java new file mode 100644 index 00000000000..c1e1d15b5b4 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddV1.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.swagger.invocation.schemas; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping +public class SpringmvcAddV1 { + @GetMapping(path = "/add") + public int add(int x, int y) { + return x + y; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddV2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddV2.java new file mode 100644 index 00000000000..5934af56f1a --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddV2.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.swagger.invocation.schemas; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping +public interface SpringmvcAddV2 { + @GetMapping(path = "/add") + int add(int x, int y, @RequestHeader(name = "x-z") int z); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddWrapperV1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddWrapperV1.java new file mode 100644 index 00000000000..76c435a311b --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddWrapperV1.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.swagger.invocation.schemas; + +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV1; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping +public class SpringmvcAddWrapperV1 { + @GetMapping(path = "/add") + public int add(AddWrapperV1 wrapper) { + return wrapper.getX() + wrapper.y; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddWrapperV2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddWrapperV2.java new file mode 100644 index 00000000000..8d2de4b48cb --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/SpringmvcAddWrapperV2.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.swagger.invocation.schemas; + +import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping +public interface SpringmvcAddWrapperV2 { + @GetMapping(path = "/add") + int add(AddWrapperV2 wrapper); +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddBeanParamV1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddBeanParamV1.java new file mode 100644 index 00000000000..130e00ba79a --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddBeanParamV1.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.swagger.invocation.schemas.models; + +import jakarta.ws.rs.QueryParam; + +public class AddBeanParamV1 { + @QueryParam("x") + private int x; + + @QueryParam("y") + public int y; + + public AddBeanParamV1() { + } + + public AddBeanParamV1(int x, int y) { + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddBeanParamV2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddBeanParamV2.java new file mode 100644 index 00000000000..42d64834631 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddBeanParamV2.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.swagger.invocation.schemas.models; + +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.QueryParam; + +public class AddBeanParamV2 { + @QueryParam("x") + private int x; + + @QueryParam("y") + public int y; + + @HeaderParam("x-z") + public int z; + + public AddBeanParamV2(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddWrapperV1.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddWrapperV1.java new file mode 100644 index 00000000000..5bd078b20e1 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddWrapperV1.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.swagger.invocation.schemas.models; + +public class AddWrapperV1 { + private int x; + + public int y; + + public AddWrapperV1() { + } + + public AddWrapperV1(int x, int y) { + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddWrapperV2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddWrapperV2.java new file mode 100644 index 00000000000..1a2b3608665 --- /dev/null +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/schemas/models/AddWrapperV2.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.swagger.invocation.schemas.models; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class AddWrapperV2 { + private int x; + + public int y; + + @JsonProperty(value = "x-z") + public int z; + + public AddWrapperV2(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } +} diff --git a/swagger/swagger-invocation/invocation-core/src/test/resources/log4j.properties b/swagger/swagger-invocation/invocation-core/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/swagger/swagger-invocation/invocation-jaxrs/pom.xml b/swagger/swagger-invocation/invocation-jaxrs/pom.xml index 8a707736616..50d12dd243f 100644 --- a/swagger/swagger-invocation/invocation-jaxrs/pom.xml +++ b/swagger/swagger-invocation/invocation-jaxrs/pom.xml @@ -1,11 +1,12 @@ - + - 4.0.0 - - io.servicecomb - swagger-invocation - 0.3.0-SNAPSHOT - - swagger-invocation-jaxrs + 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 + swagger-invocation + 3.4.0-SNAPSHOT + + swagger-invocation-jaxrs + Java Chassis::Swagger::Invocation::JAXRS + + + + org.apache.servicecomb + swagger-invocation-core + + + org.apache.servicecomb + swagger-generator-jaxrs + - - - io.servicecomb - swagger-invocation-core - - - \ No newline at end of file + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.glassfish.jersey.core + jersey-client + test + + + diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/main/java/io/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapper.java b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/io/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapper.java deleted file mode 100644 index a904b93e253..00000000000 --- a/swagger/swagger-invocation/invocation-jaxrs/src/main/java/io/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapper.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.jaxrs.response; - -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import javax.ws.rs.core.Response.ResponseBuilder; - -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; - -@Component -public class JaxrsConsumerResponseMapper implements ConsumerResponseMapper { - @Override - public Class getResponseClass() { - return javax.ws.rs.core.Response.class; - } - - @Override - public Object mapResponse(Response response) { - ResponseBuilder responseBuilder = - javax.ws.rs.core.Response.status(response.getStatus()).entity(response.getResult()); - - Map> headers = response.getHeaders().getHeaderMap(); - if (headers != null) { - for (Entry> entry : headers.entrySet()) { - responseBuilder.header(entry.getKey(), entry.getValue()); - } - } - - return responseBuilder.build(); - } -} diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/main/java/io/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapper.java b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/io/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapper.java deleted file mode 100644 index 4b178599879..00000000000 --- a/swagger/swagger-invocation/invocation-jaxrs/src/main/java/io/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapper.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.jaxrs.response; - -import java.util.List; -import java.util.Map.Entry; - -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response.StatusType; - -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; - -@Component -public class JaxrsProducerResponseMapper implements ProducerResponseMapper { - @Override - public Class getResponseClass() { - return javax.ws.rs.core.Response.class; - } - - @Override - public Response mapResponse(StatusType status, Object response) { - javax.ws.rs.core.Response jaxrsResponse = (javax.ws.rs.core.Response) response; - - Response cseResponse = Response.status(jaxrsResponse.getStatusInfo()).entity(jaxrsResponse.getEntity()); - MultivaluedMap headers = jaxrsResponse.getHeaders(); - for (Entry> entry : headers.entrySet()) { - if (entry.getValue() == null || entry.getValue().isEmpty()) { - continue; - } - - cseResponse.getHeaders().getHeaderMap().put(entry.getKey(), entry.getValue()); - } - return cseResponse; - } -} diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapper.java b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapper.java new file mode 100644 index 00000000000..549f4fc7151 --- /dev/null +++ b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapper.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.swagger.invocation.jaxrs.response; + +import java.util.Map.Entry; + +import jakarta.ws.rs.core.Response.ResponseBuilder; + +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; + +import io.vertx.core.MultiMap; + +public class JaxrsConsumerResponseMapper implements ConsumerResponseMapper { + @Override + public Object mapResponse(Response response) { + ResponseBuilder responseBuilder = + jakarta.ws.rs.core.Response.status(response.getStatus()).entity(response.getResult()); + + MultiMap headers = response.getHeaders(); + if (headers == null) { + return responseBuilder.build(); + } + + for (Entry entry : headers.entries()) { + responseBuilder.header(entry.getKey(), entry.getValue()); + } + + return responseBuilder.build(); + } +} diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapperFactory.java new file mode 100644 index 00000000000..307cdb26c20 --- /dev/null +++ b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapperFactory.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.swagger.invocation.jaxrs.response; + +import java.lang.reflect.Type; + +import jakarta.ws.rs.core.Response; + +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; +import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; +import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory; + +public class JaxrsConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { + @Override + public boolean isMatch(Type consumerType) { + return Response.class.equals(consumerType); + } + + @Override + public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, + Type consumerType) { + return new JaxrsConsumerResponseMapper(); + } +} diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapper.java b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapper.java new file mode 100644 index 00000000000..e1625da79d0 --- /dev/null +++ b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapper.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.swagger.invocation.jaxrs.response; + +import java.util.List; +import java.util.Map.Entry; + +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response.StatusType; + +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; + +public class JaxrsProducerResponseMapper implements ProducerResponseMapper { + @Override + public Response mapResponse(StatusType status, Object response) { + jakarta.ws.rs.core.Response jaxrsResponse = (jakarta.ws.rs.core.Response) response; + + Response cseResponse = Response.status(jaxrsResponse.getStatusInfo()).entity(jaxrsResponse.getEntity()); + MultivaluedMap headers = jaxrsResponse.getHeaders(); + for (Entry> entry : headers.entrySet()) { + if (entry.getValue() == null) { + continue; + } + + for (Object value : entry.getValue()) { + if (value == null) { + continue; + } + cseResponse.addHeader(entry.getKey(), value.toString()); + } + } + return cseResponse; + } +} diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapperFactory.java new file mode 100644 index 00000000000..a8e1e064fdd --- /dev/null +++ b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapperFactory.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.swagger.invocation.jaxrs.response; + +import java.lang.reflect.Type; + +import jakarta.ws.rs.core.Response; + +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; +import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; +import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; + +public class JaxrsProducerResponseMapperFactory implements ProducerResponseMapperFactory { + @Override + public boolean isMatch(Type producerType) { + return Response.class.equals(producerType); + } + + @Override + public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, + Type producerType) { + return new JaxrsProducerResponseMapper(); + } +} diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory b/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory new file mode 100644 index 00000000000..b15dde97c09 --- /dev/null +++ b/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory @@ -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.swagger.invocation.jaxrs.response.JaxrsConsumerResponseMapperFactory diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory b/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory new file mode 100644 index 00000000000..2598b38b3d6 --- /dev/null +++ b/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory @@ -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.swagger.invocation.jaxrs.response.JaxrsProducerResponseMapperFactory diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/test/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/TestJaxrsConsumerResponseMapper.java b/swagger/swagger-invocation/invocation-jaxrs/src/test/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/TestJaxrsConsumerResponseMapper.java new file mode 100644 index 00000000000..44f4f3cf7ed --- /dev/null +++ b/swagger/swagger-invocation/invocation-jaxrs/src/test/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/TestJaxrsConsumerResponseMapper.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.swagger.invocation.jaxrs.response; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +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 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.models.OpenAPI; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Response; + +public class TestJaxrsConsumerResponseMapper { + @Path("/") + interface ConsumerResponseForTest { + @ApiResponse(responseCode = "200", description = "", content = @Content(schema = + @Schema(implementation = String.class))) + @Path("/jaxrsResponse") + @GET + jakarta.ws.rs.core.Response jaxrsResponse(); + } + + SwaggerEnvironment environment = new SwaggerEnvironment(); + + SwaggerConsumer swaggerConsumer; + + String result = "abc"; + + org.apache.servicecomb.swagger.invocation.Response response = org.apache.servicecomb.swagger.invocation.Response + .ok(result); + + @BeforeEach + public void setup() { + OpenAPI swagger = SwaggerGenerator.generate(ConsumerResponseForTest.class); + swaggerConsumer = environment.createConsumer(ConsumerResponseForTest.class, swagger); + } + + @SuppressWarnings("unchecked") + @Test + public void jaxrsResponse() { + SwaggerConsumerOperation operation = swaggerConsumer.findOperation("jaxrsResponse"); + + Response jaxrsResponse = (Response) operation.getResponseMapper().mapResponse(response); + Assertions.assertEquals(result, jaxrsResponse.getEntity()); + Assertions.assertTrue(jaxrsResponse.getHeaders().isEmpty()); + } + + @Test + public void jaxrsResponseWithHeaders() { + SwaggerConsumerOperation operation = swaggerConsumer.findOperation("jaxrsResponse"); + response.addHeader("h", "v1").addHeader("h", "v2").addHeader("h", null); + response.addHeader("h1", null); + + Response jaxrsResponse = (Response) operation.getResponseMapper().mapResponse(response); + Assertions.assertEquals(result, jaxrsResponse.getEntity()); + Assertions.assertEquals(1, jaxrsResponse.getHeaders().size()); + MatcherAssert.assertThat(jaxrsResponse.getHeaders().get("h"), Matchers.contains("v1", "v2")); + } +} diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/test/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/TestJaxrsProducerResponseMapper.java b/swagger/swagger-invocation/invocation-jaxrs/src/test/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/TestJaxrsProducerResponseMapper.java new file mode 100644 index 00000000000..6b60db54852 --- /dev/null +++ b/swagger/swagger-invocation/invocation-jaxrs/src/test/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/TestJaxrsProducerResponseMapper.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.swagger.invocation.jaxrs.response; + +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response.Status; + +import org.apache.servicecomb.swagger.invocation.Response; +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 TestJaxrsProducerResponseMapper { + JaxrsProducerResponseMapper mapper = new JaxrsProducerResponseMapper(); + + @Test + public void mapResponse_withoutHeaders() { + jakarta.ws.rs.core.Response jaxrsResponse = Mockito.mock(jakarta.ws.rs.core.Response.class); + Mockito.when(jaxrsResponse.getStatusInfo()).thenReturn(Status.OK); + Mockito.when(jaxrsResponse.getEntity()).thenReturn("result"); + Mockito.when(jaxrsResponse.getHeaders()).thenReturn(new MultivaluedHashMap<>()); + + Response response = mapper.mapResponse(null, jaxrsResponse); + Assertions.assertEquals(Status.OK, response.getStatus()); + Assertions.assertEquals("result", response.getResult()); + Assertions.assertNull(response.getHeaders()); + } + + @Test + public void mapResponse_withHeaders() { + jakarta.ws.rs.core.Response jaxrsResponse = Mockito.mock(jakarta.ws.rs.core.Response.class); + Mockito.when(jaxrsResponse.getStatusInfo()).thenReturn(Status.OK); + Mockito.when(jaxrsResponse.getEntity()).thenReturn("result"); + + MultivaluedMap headers = new MultivaluedHashMap<>(); + headers.add("h", "v"); + Mockito.when(jaxrsResponse.getHeaders()).thenReturn(headers); + + Response response = mapper.mapResponse(null, jaxrsResponse); + Assertions.assertEquals(Status.OK, response.getStatus()); + Assertions.assertEquals("result", response.getResult()); + Assertions.assertEquals(1, response.getHeaders().size()); + MatcherAssert.assertThat(response.getHeaders("h"), Matchers.contains("v")); + } +} diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/test/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/TestJaxrsProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-jaxrs/src/test/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/TestJaxrsProducerResponseMapperFactory.java new file mode 100644 index 00000000000..0a72f648b27 --- /dev/null +++ b/swagger/swagger-invocation/invocation-jaxrs/src/test/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/TestJaxrsProducerResponseMapperFactory.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.swagger.invocation.jaxrs.response; + +import jakarta.ws.rs.core.Response; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestJaxrsProducerResponseMapperFactory { + JaxrsProducerResponseMapperFactory factory = new JaxrsProducerResponseMapperFactory(); + + @Test + public void isMatch_true() { + Assertions.assertTrue(factory.isMatch(Response.class)); + } + + @Test + public void isMatch_false() { + Assertions.assertFalse(factory.isMatch(String.class)); + } + + @Test + public void createResponseMapper() { + MatcherAssert.assertThat(factory.createResponseMapper(null, null), + Matchers.instanceOf(JaxrsProducerResponseMapper.class)); + } +} diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/test/resources/log4j2.xml b/swagger/swagger-invocation/invocation-jaxrs/src/test/resources/log4j2.xml new file mode 100644 index 00000000000..8cee07220ec --- /dev/null +++ b/swagger/swagger-invocation/invocation-jaxrs/src/test/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/swagger/swagger-invocation/invocation-springmvc/pom.xml b/swagger/swagger-invocation/invocation-springmvc/pom.xml index 6d5062707a4..5ae28883576 100644 --- a/swagger/swagger-invocation/invocation-springmvc/pom.xml +++ b/swagger/swagger-invocation/invocation-springmvc/pom.xml @@ -1,11 +1,12 @@ - + - 4.0.0 - - io.servicecomb - swagger-invocation - 0.3.0-SNAPSHOT - - swagger-invocation-springmvc + 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 + swagger-invocation + 3.4.0-SNAPSHOT + + swagger-invocation-springmvc + Java Chassis::Swagger::Invocation::Spring MVC + - - - io.servicecomb - swagger-invocation-core - - - org.springframework - spring-webmvc - - - \ No newline at end of file + + + org.apache.servicecomb + swagger-invocation-core + + + org.springframework + spring-webmvc + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.jmockit + jmockit + test + + + diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/io/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapper.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/io/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapper.java deleted file mode 100644 index e135abd35ab..00000000000 --- a/swagger/swagger-invocation/invocation-springmvc/src/main/java/io/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapper.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.springmvc.response; - -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; - -@Component -public class SpringmvcConsumerResponseMapper implements ConsumerResponseMapper { - @Override - public Class getResponseClass() { - return ResponseEntity.class; - } - - @Override - public Object mapResponse(Response response) { - HttpStatus status = HttpStatus.valueOf(response.getStatusCode()); - - HttpHeaders httpHeaders = null; - Map> headers = response.getHeaders().getHeaderMap(); - if (headers != null) { - httpHeaders = new HttpHeaders(); - for (Entry> entry : headers.entrySet()) { - for (Object value : entry.getValue()) { - httpHeaders.add(entry.getKey(), String.valueOf(value)); - } - } - } - - return new ResponseEntity(response.getResult(), httpHeaders, status); - } -} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/io/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapper.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/io/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapper.java deleted file mode 100644 index 2e08a000087..00000000000 --- a/swagger/swagger-invocation/invocation-springmvc/src/main/java/io/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapper.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.swagger.invocation.springmvc.response; - -import java.util.List; -import java.util.Map.Entry; - -import javax.ws.rs.core.Response.StatusType; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; - -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.context.HttpStatus; -import io.servicecomb.swagger.invocation.response.Headers; -import io.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; - -@Component -public class SpringmvcProducerResponseMapper implements ProducerResponseMapper { - @Override - public Class getResponseClass() { - return ResponseEntity.class; - } - - @SuppressWarnings("unchecked") - @Override - public Response mapResponse(StatusType status, Object response) { - ResponseEntity springmvcResponse = (ResponseEntity) response; - - StatusType responseStatus = new HttpStatus(springmvcResponse.getStatusCode().value(), - springmvcResponse.getStatusCode().getReasonPhrase()); - Response cseResponse = Response.status(responseStatus).entity(springmvcResponse.getBody()); - HttpHeaders headers = springmvcResponse.getHeaders(); - - Headers cseHeaders = cseResponse.getHeaders(); - for (Entry> entry : headers.entrySet()) { - if (entry.getValue() == null || entry.getValue().isEmpty()) { - continue; - } - - for (String value : entry.getValue()) { - cseHeaders.addHeader(entry.getKey(), value); - } - } - return cseResponse; - } -} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartArrayConverter.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartArrayConverter.java new file mode 100644 index 00000000000..d9b0901df6c --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartArrayConverter.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.swagger.invocation.converter; + +import java.lang.reflect.Type; +import java.util.List; + +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.http.Part; + +public class PartListToMultipartArrayConverter implements Converter { + @Override + public Type getSrcType() { + return ParameterizedTypeUtil.make(List.class, Part.class); + } + + @Override + public Type getTargetType() { + return MultipartFile[].class; + } + + @Override + public Object convert(Object value) { + if (value == null) { + return null; + } + @SuppressWarnings("unchecked") + List partList = (List) value; + PartToMultipartFile[] partArray = new PartToMultipartFile[partList.size()]; + for (int i = 0; i < partArray.length; i++) { + partArray[i] = new PartToMultipartFile(partList.get(i)); + } + return partArray; + } +} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartListConverter.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartListConverter.java new file mode 100644 index 00000000000..4d7d50725cd --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartListConverter.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.converter; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.http.Part; + +public class PartListToMultipartListConverter implements Converter { + @Override + public Type getSrcType() { + return ParameterizedTypeUtil.make(List.class, Part.class); + } + + @Override + public Type getTargetType() { + return ParameterizedTypeUtil.make(List.class, MultipartFile.class); + } + + @Override + public Object convert(Object value) { + if (value == null) { + return null; + } + @SuppressWarnings("unchecked") + List partList = (List) value; + List fileList = new ArrayList<>(); + partList.forEach(part -> fileList.add(new PartToMultipartFile(part))); + return fileList; + } +} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartToMultipartConverter.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartToMultipartConverter.java new file mode 100644 index 00000000000..ea65af5b121 --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartToMultipartConverter.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.swagger.invocation.converter; + +import java.lang.reflect.Type; + +import jakarta.servlet.http.Part; + +import org.springframework.web.multipart.MultipartFile; + +public class PartToMultipartConverter implements Converter { + @Override + public Type getSrcType() { + return Part.class; + } + + @Override + public Type getTargetType() { + return MultipartFile.class; + } + + @Override + public Object convert(Object value) { + if (value == null) { + return null; + } + + return new PartToMultipartFile((Part) value); + } +} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartToMultipartFile.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartToMultipartFile.java new file mode 100644 index 00000000000..8dd03dd3c86 --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartToMultipartFile.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.invocation.converter; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import jakarta.servlet.http.Part; + +import org.apache.commons.io.IOUtils; +import org.springframework.web.multipart.MultipartFile; + +public class PartToMultipartFile implements MultipartFile { + private final Part part; + + public PartToMultipartFile(Part part) { + this.part = part; + } + + @Override + public String getName() { + return part.getName(); + } + + @Override + public String getOriginalFilename() { + return part.getSubmittedFileName(); + } + + @Override + public String getContentType() { + return part.getContentType(); + } + + @Override + public boolean isEmpty() { + return part.getSize() == 0; + } + + @Override + public long getSize() { + return part.getSize(); + } + + @Override + public byte[] getBytes() throws IOException { + try (InputStream is = getInputStream()) { + return IOUtils.toByteArray(is); + } + } + + @Override + public InputStream getInputStream() throws IOException { + return part.getInputStream(); + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + part.write(dest.getPath()); + } +} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapper.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapper.java new file mode 100644 index 00000000000..cba05cbb0c4 --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapper.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.swagger.invocation.springmvc.response; + +import java.util.Map.Entry; + +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import io.vertx.core.MultiMap; + +public class SpringmvcConsumerResponseMapper implements ConsumerResponseMapper { + private final ConsumerResponseMapper realMapper; + + public SpringmvcConsumerResponseMapper(ConsumerResponseMapper realMapper) { + this.realMapper = realMapper; + } + + @Override + public Object mapResponse(Response response) { + HttpStatus status = HttpStatus.valueOf(response.getStatusCode()); + + MultiMap headers = response.getHeaders(); + if (headers == null) { + Object realResult = realMapper.mapResponse(response); + return new ResponseEntity<>(realResult, null, status); + } + + HttpHeaders httpHeaders = new HttpHeaders(); + for (Entry entry : headers.entries()) { + httpHeaders.add(entry.getKey(), entry.getValue()); + } + + Object realResult = realMapper.mapResponse(response); + return new ResponseEntity<>(realResult, httpHeaders, status); + } +} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapperFactory.java new file mode 100644 index 00000000000..0e22da39176 --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapperFactory.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.swagger.invocation.springmvc.response; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; +import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; +import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory; +import org.springframework.http.ResponseEntity; + +public class SpringmvcConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { + @Override + public boolean isMatch(Type consumerType) { + if (!ParameterizedType.class.isAssignableFrom(consumerType.getClass())) { + return false; + } + + return ((ParameterizedType) consumerType).getRawType().equals(ResponseEntity.class); + } + + @Override + public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, + Type consumerType) { + Type realConsumerType = ((ParameterizedType) consumerType).getActualTypeArguments()[0]; + ConsumerResponseMapper realMapper = factories.createResponseMapper(realConsumerType); + return new SpringmvcConsumerResponseMapper(realMapper); + } +} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapper.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapper.java new file mode 100644 index 00000000000..2838d4558a0 --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapper.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.swagger.invocation.springmvc.response; + +import java.util.List; +import java.util.Map.Entry; + +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.context.HttpStatus; +import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; + +import jakarta.ws.rs.core.Response.StatusType; + +public class SpringmvcProducerResponseMapper implements ProducerResponseMapper { + private final ProducerResponseMapper realMapper; + + public SpringmvcProducerResponseMapper(ProducerResponseMapper realMapper) { + this.realMapper = realMapper; + } + + @SuppressWarnings("unchecked") + @Override + public Response mapResponse(StatusType status, Object response) { + ResponseEntity springmvcResponse = (ResponseEntity) response; + + // TODO: maybe can use swagger HttpStatus instead of customized one + StatusType responseStatus = new HttpStatus(springmvcResponse.getStatusCode().value(), ""); + + Response cseResponse = null; + if (HttpStatus.isSuccess(responseStatus)) { + cseResponse = realMapper.mapResponse(responseStatus, springmvcResponse.getBody()); + } else { + // not support fail response mapper now + cseResponse = Response.status(responseStatus).entity(springmvcResponse.getBody()); + } + + HttpHeaders headers = springmvcResponse.getHeaders(); + for (Entry> entry : headers.entrySet()) { + if (entry.getValue() == null) { + continue; + } + + for (String value : entry.getValue()) { + cseResponse.addHeader(entry.getKey(), value); + } + } + return cseResponse; + } +} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapperFactory.java new file mode 100644 index 00000000000..cfb43f5c3b2 --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapperFactory.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.swagger.invocation.springmvc.response; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; +import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; +import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; +import org.springframework.http.ResponseEntity; + +public class SpringmvcProducerResponseMapperFactory implements ProducerResponseMapperFactory { + @Override + public boolean isMatch(Type producerType) { + if (!ParameterizedType.class.isAssignableFrom(producerType.getClass())) { + return false; + } + + return ((ParameterizedType) producerType).getRawType().equals(ResponseEntity.class); + } + + @Override + public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, + Type producerType) { + Type realProducerType = ((ParameterizedType) producerType).getActualTypeArguments()[0]; + ProducerResponseMapper realMapper = factories.createResponseMapper(realProducerType); + return new SpringmvcProducerResponseMapper(realMapper); + } +} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter b/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter new file mode 100644 index 00000000000..2222af91fe7 --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter @@ -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. +# + +org.apache.servicecomb.swagger.invocation.converter.PartListToMultipartArrayConverter +org.apache.servicecomb.swagger.invocation.converter.PartToMultipartConverter +org.apache.servicecomb.swagger.invocation.converter.PartListToMultipartListConverter +org.apache.servicecomb.swagger.invocation.converter.impl.part.ResourceToPartConverter diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory b/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory new file mode 100644 index 00000000000..26ddc8af9fa --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory @@ -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.swagger.invocation.springmvc.response.SpringmvcConsumerResponseMapperFactory diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory b/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory new file mode 100644 index 00000000000..5550f33368c --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory @@ -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.swagger.invocation.springmvc.response.SpringmvcProducerResponseMapperFactory diff --git a/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/converter/TestPartToMultipartFile.java b/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/converter/TestPartToMultipartFile.java new file mode 100644 index 00000000000..7bf2bda9836 --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/converter/TestPartToMultipartFile.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.invocation.converter; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; + +import jakarta.servlet.http.Part; + +import org.apache.servicecomb.foundation.common.Holder; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import mockit.Expectations; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; + +public class TestPartToMultipartFile { + @Mocked + Part part; + + PartToMultipartFile multipartFile; + + @Before + public void setup() { + multipartFile = new PartToMultipartFile(part); + } + + @Test + public void getName() { + String name = "paramName"; + new Expectations() { + { + part.getName(); + result = name; + } + }; + + Assertions.assertEquals(name, multipartFile.getName()); + } + + @Test + public void getOriginalFilename() { + String submittedFileName = "fileName"; + new Expectations() { + { + part.getSubmittedFileName(); + result = submittedFileName; + } + }; + + Assertions.assertEquals(submittedFileName, multipartFile.getOriginalFilename()); + } + + @Test + public void getContentType() { + String contentType = "json"; + new Expectations() { + { + part.getContentType(); + result = contentType; + } + }; + + Assertions.assertEquals(contentType, multipartFile.getContentType()); + } + + @Test + public void isEmptyTrue() { + new Expectations() { + { + part.getSize(); + result = 0; + } + }; + + Assertions.assertTrue(multipartFile.isEmpty()); + } + + @Test + public void isEmptyFalse() { + new Expectations() { + { + part.getSize(); + result = 1; + } + }; + + Assertions.assertFalse(multipartFile.isEmpty()); + } + + @Test + public void getSize() { + long size = 10; + new Expectations() { + { + part.getSize(); + result = size; + } + }; + + Assertions.assertEquals(size, multipartFile.getSize()); + } + + static class ByteArrayInputStreamForTest extends ByteArrayInputStream { + boolean closed; + + public ByteArrayInputStreamForTest(byte[] buf) { + super(buf); + } + + @Override + public void close() throws IOException { + closed = true; + } + } + + @Test + public void getBytes_normal() throws IOException { + byte[] bytes = new byte[] {1, 2, 3}; + ByteArrayInputStreamForTest is = new ByteArrayInputStreamForTest(bytes); + new Expectations() { + { + part.getInputStream(); + result = is; + } + }; + + Assertions.assertArrayEquals(bytes, multipartFile.getBytes()); + Assertions.assertTrue(is.closed); + } + + @Test + public void getBytes_exception() throws IOException { + new Expectations() { + { + part.getInputStream(); + result = new IOException("open stream failed"); + } + }; + + IOException exception = Assertions.assertThrows(IOException.class, + () -> multipartFile.getBytes()); + Assertions.assertEquals("open stream failed", exception.getMessage()); + } + + @Test + public void transferTo() throws IllegalStateException, IOException { + File dest = new File("/dest"); + Holder destName = new Holder<>(); + new MockUp(part) { + @Mock + void write(String fileName) throws IOException { + destName.value = fileName; + } + }; + + multipartFile.transferTo(dest); + Assertions.assertEquals(dest.getPath(), destName.value); + } +} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcConsumerResponseMapper.java b/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcConsumerResponseMapper.java new file mode 100644 index 00000000000..eb95d441576 --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcConsumerResponseMapper.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.springmvc.response; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.swagger.engine.SwaggerConsumer; +import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; +import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.apache.servicecomb.swagger.invocation.Response; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.springframework.http.ResponseEntity; + +import io.swagger.v3.oas.models.OpenAPI; + +public class TestSpringmvcConsumerResponseMapper { + interface ConsumerResponseForTest { + ResponseEntity responseEntity(); + + CompletableFuture> asyncResponseEntity(); + } + + SwaggerEnvironment environment = new SwaggerEnvironment(); + + SwaggerConsumer swaggerConsumer; + + String result = "abc"; + + Response response = Response.ok(result); + + @Before + public void setup() { + OpenAPI swagger = SwaggerGenerator.generate(ConsumerResponseForTest.class); + swaggerConsumer = environment.createConsumer(ConsumerResponseForTest.class, swagger); + } + + @Test + public void responseEntity() { + SwaggerConsumerOperation operation = swaggerConsumer.findOperation("responseEntity"); + + @SuppressWarnings("unchecked") + ResponseEntity responseEntity = (ResponseEntity) operation.getResponseMapper() + .mapResponse(response); + Assertions.assertEquals(result, responseEntity.getBody()); + Assertions.assertTrue(responseEntity.getHeaders().isEmpty()); + } + + @Test + public void responseEntityWithHeader() { + SwaggerConsumerOperation operation = swaggerConsumer.findOperation("responseEntity"); + response.addHeader("h", "v"); + + @SuppressWarnings("unchecked") + ResponseEntity responseEntity = (ResponseEntity) operation.getResponseMapper() + .mapResponse(response); + Assertions.assertEquals(result, responseEntity.getBody()); + Assertions.assertEquals(1, responseEntity.getHeaders().size()); + MatcherAssert.assertThat(responseEntity.getHeaders().get("h"), Matchers.contains("v")); + } + + @Test + public void asyncResponseEntity() { + SwaggerConsumerOperation operation = swaggerConsumer.findOperation("asyncResponseEntity"); + + @SuppressWarnings("unchecked") + ResponseEntity responseEntity = (ResponseEntity) operation.getResponseMapper() + .mapResponse(response); + Assertions.assertEquals(result, responseEntity.getBody()); + Assertions.assertTrue(responseEntity.getHeaders().isEmpty()); + } + + @Test + public void asyncResponseEntityWithHeader() { + SwaggerConsumerOperation operation = swaggerConsumer.findOperation("asyncResponseEntity"); + response.addHeader("h", "v1").addHeader("h", "v2"); + response.addHeader("h1", null); + + @SuppressWarnings("unchecked") + ResponseEntity responseEntity = (ResponseEntity) operation.getResponseMapper() + .mapResponse(response); + Assertions.assertEquals(result, responseEntity.getBody()); + Assertions.assertEquals(1, responseEntity.getHeaders().size()); + MatcherAssert.assertThat(responseEntity.getHeaders().get("h"), Matchers.contains("v1", "v2")); + } +} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapper.java b/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapper.java new file mode 100644 index 00000000000..c48fc62be8c --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapper.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.springmvc.response; + +import java.util.Arrays; +import java.util.List; + +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.StatusType; + +import org.apache.servicecomb.foundation.common.http.HttpStatus; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; + +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; + +public class TestSpringmvcProducerResponseMapper { + @Mocked + ProducerResponseMapper realMapper; + + SpringmvcProducerResponseMapper mapper; + + String[] arrResult = new String[] {"a", "b"}; + + @Before + public void setup() { + mapper = new SpringmvcProducerResponseMapper(realMapper); + + new MockUp(realMapper) { + @Mock + Response mapResponse(StatusType status, Object response) { + if (HttpStatus.isSuccess(status.getStatusCode())) { + return Response.ok(Arrays.asList(arrResult)); + } + + return null; + } + }; + } + + @SuppressWarnings("unchecked") + @Test + public void mapResponse_withoutHeader_success() { + ResponseEntity responseEntity = + new ResponseEntity<>(arrResult, org.springframework.http.HttpStatus.OK); + Response response = mapper.mapResponse(null, responseEntity); + MatcherAssert.assertThat((List) response.getResult(), Matchers.contains("a", "b")); + Assertions.assertEquals(Status.OK, response.getStatus()); + } + + @Test + public void mapResponse_withoutHeader_fail() { + ResponseEntity responseEntity = + new ResponseEntity<>(arrResult, org.springframework.http.HttpStatus.BAD_REQUEST); + Response response = mapper.mapResponse(null, responseEntity); + Assertions.assertSame(arrResult, response.getResult()); + Assertions.assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus().getStatusCode()); + } + + @Test + public void mapResponse_withHeader() { + HttpHeaders headers = new HttpHeaders(); + headers.add("h", "v"); + + ResponseEntity responseEntity = + new ResponseEntity<>(arrResult, headers, org.springframework.http.HttpStatus.OK); + Response response = mapper.mapResponse(null, responseEntity); + + List hv = response.getHeaders("h"); + MatcherAssert.assertThat(hv, Matchers.contains("v")); + } +} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapperFactory.java new file mode 100644 index 00000000000..35c84db148f --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapperFactory.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.springmvc.response; + +import java.lang.reflect.Method; +import java.util.List; + +import org.apache.servicecomb.foundation.common.utils.ReflectUtils; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; +import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; +import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +public class TestSpringmvcProducerResponseMapperFactory { + SpringmvcProducerResponseMapperFactory factory = new SpringmvcProducerResponseMapperFactory(); + + ResponseMapperFactories factories = new ResponseMapperFactories<>( + ProducerResponseMapperFactory.class); + + public ResponseEntity responseEntity() { + return null; + } + + public List list() { + return null; + } + + @Test + public void isMatch_true() { + Method method = ReflectUtils.findMethod(this.getClass(), "responseEntity"); + Assertions.assertTrue(factory.isMatch(method.getGenericReturnType())); + } + + @Test + public void isMatch_Parameterized_false() { + Method method = ReflectUtils.findMethod(this.getClass(), "list"); + Assertions.assertFalse(factory.isMatch(method.getGenericReturnType())); + } + + @Test + public void isMatch_false() { + Assertions.assertFalse(factory.isMatch(String.class)); + } + + @Test + public void createResponseMapper() { + Method responseEntityMethod = ReflectUtils.findMethod(this.getClass(), "responseEntity"); + + ProducerResponseMapper mapper = factory + .createResponseMapper(factories, responseEntityMethod.getGenericReturnType()); + MatcherAssert.assertThat(mapper, Matchers.instanceOf(SpringmvcProducerResponseMapper.class)); + + ResponseEntity responseEntity = new ResponseEntity<>(new String[] {"a", "b"}, HttpStatus.OK); + Response response = mapper.mapResponse(null, responseEntity); + MatcherAssert.assertThat(response.getResult(), Matchers.arrayContaining("a", "b")); + } +} diff --git a/swagger/swagger-invocation/invocation-springmvc/src/test/resources/log4j2.xml b/swagger/swagger-invocation/invocation-springmvc/src/test/resources/log4j2.xml new file mode 100644 index 00000000000..8cee07220ec --- /dev/null +++ b/swagger/swagger-invocation/invocation-springmvc/src/test/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/swagger/swagger-invocation/invocation-validator/pom.xml b/swagger/swagger-invocation/invocation-validator/pom.xml new file mode 100644 index 00000000000..cf0f46c7f4f --- /dev/null +++ b/swagger/swagger-invocation/invocation-validator/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + org.apache.servicecomb + swagger-invocation + 3.4.0-SNAPSHOT + + swagger-invocation-validator + Java Chassis::Swagger::Invocation::Validator + + + org.apache.servicecomb + swagger-invocation-core + + + org.hibernate.validator + hibernate-validator + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + diff --git a/swagger/swagger-invocation/invocation-validator/src/main/java/org/apache/servicecomb/swagger/invocation/validator/DefaultParameterNameProvider.java b/swagger/swagger-invocation/invocation-validator/src/main/java/org/apache/servicecomb/swagger/invocation/validator/DefaultParameterNameProvider.java new file mode 100644 index 00000000000..e1a30725b45 --- /dev/null +++ b/swagger/swagger-invocation/invocation-validator/src/main/java/org/apache/servicecomb/swagger/invocation/validator/DefaultParameterNameProvider.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.swagger.invocation.validator; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import jakarta.validation.ParameterNameProvider; + +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; + +public class DefaultParameterNameProvider implements ParameterNameProvider { + private final Map> methodCache = new ConcurrentHashMapEx<>(); + + @Override + public List getParameterNames(Constructor constructor) { + return methodCache.computeIfAbsent(constructor, k -> getParameterNamesEx(constructor)); + } + + @Override + public List getParameterNames(Method method) { + return methodCache.computeIfAbsent(method, k -> getParameterNamesEx(method)); + } + + private List getParameterNamesEx(Executable methodOrConstructor) { + Parameter[] parameters = methodOrConstructor.getParameters(); + List parameterNames = new ArrayList<>(parameters.length); + for (int idx = 0; idx < parameters.length; idx++) { + Parameter parameter = parameters[idx]; + String parameterName = parameter.isNamePresent() ? parameter.getName() : "arg" + idx; + parameterNames.add(parameterName); + } + return Collections.unmodifiableList(parameterNames); + } +} diff --git a/swagger/swagger-invocation/invocation-validator/src/test/java/org/apache/servicecomb/swagger/invocation/validator/TestDefaultParameterNameProvider.java b/swagger/swagger-invocation/invocation-validator/src/test/java/org/apache/servicecomb/swagger/invocation/validator/TestDefaultParameterNameProvider.java new file mode 100644 index 00000000000..b655231874d --- /dev/null +++ b/swagger/swagger-invocation/invocation-validator/src/test/java/org/apache/servicecomb/swagger/invocation/validator/TestDefaultParameterNameProvider.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.invocation.validator; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestDefaultParameterNameProvider { + static class ValidatorForTest { + static class Student { + private String name; + + int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + } + + private String grade; + + private int number; + + public ValidatorForTest() { + } + + public ValidatorForTest(String grade, int number) { + this.grade = grade; + this.number = number; + } + + public int add(int a, int b) { + return a + b; + } + + public String sayHi(String hi) { + return hi + " sayhi"; + } + + public Student sayHello(Student student) { + return student; + } + + public String setTest(String grade) { + this.grade = grade; + return this.grade; + } + + public int getNumber() { + return number; + } + + public void setNumber(int number) { + this.number = number; + } + } + + Class validatorForTest = ValidatorForTest.class; + + DefaultParameterNameProvider parameterNameProvider = new DefaultParameterNameProvider(); + + @Test + public void testMethod() throws NoSuchMethodException { + Method method = validatorForTest.getMethod("add", int.class, int.class); + MatcherAssert.assertThat(parameterNameProvider.getParameterNames(method), Matchers.contains("a", "b")); + method = validatorForTest.getMethod("sayHi", String.class); + MatcherAssert.assertThat(parameterNameProvider.getParameterNames(method), Matchers.contains("hi")); + method = validatorForTest.getMethod("sayHello", ValidatorForTest.Student.class); + MatcherAssert.assertThat(parameterNameProvider.getParameterNames(method), Matchers.contains("student")); + method = validatorForTest.getMethod("setTest", String.class); + MatcherAssert.assertThat(parameterNameProvider.getParameterNames(method), Matchers.contains("grade")); + method = validatorForTest.getMethod("getNumber"); + Assertions.assertTrue(parameterNameProvider.getParameterNames(method).isEmpty()); + method = validatorForTest.getMethod("setNumber", int.class); + MatcherAssert.assertThat(parameterNameProvider.getParameterNames(method), Matchers.contains("number")); + } + + @Test + public void testConstructor() throws NoSuchMethodException { + Constructor constructor = validatorForTest.getConstructor(String.class, int.class); + MatcherAssert.assertThat(parameterNameProvider.getParameterNames(constructor), Matchers.contains("grade", "number")); + constructor = validatorForTest.getConstructor(); + Assertions.assertTrue(parameterNameProvider.getParameterNames(constructor).isEmpty()); + + } +} diff --git a/swagger/swagger-invocation/pom.xml b/swagger/swagger-invocation/pom.xml index e4900587700..00f83a03fc4 100644 --- a/swagger/swagger-invocation/pom.xml +++ b/swagger/swagger-invocation/pom.xml @@ -1,11 +1,12 @@ - - + + 4.0.0 - io.servicecomb + org.apache.servicecomb swagger - 0.3.0-SNAPSHOT + 3.4.0-SNAPSHOT swagger-invocation + Java Chassis::Swagger::Invocation + pom - invocation-core - invocation-jaxrs - invocation-springmvc + invocation-core + invocation-jaxrs + invocation-springmvc + invocation-validator diff --git a/tracing/README.md b/tracing/README.md new file mode 100644 index 00000000000..7ffbb400bcc --- /dev/null +++ b/tracing/README.md @@ -0,0 +1,74 @@ +# Customized Tracing +Customized tracing with [Zipkin](http://zipkin.io/) is supported to allow users to add tracing spans at points of +interest. + +## Set Up Customized Tracing +1. Include the following dependency +``` + + org.apache.servicecomb + tracing-zipkin + +``` +2. Enable tracing with annotation `@EnableZipkinTracing` on your application entry or configuration + +Import `tracing.zipkin.EnableZipkinTracing` package + +``` +import org.apache.servicecomb.tracing.zipkin.EnableZipkinTracing; +``` +Add annotation `@EnableZipkinTracing` + +``` +@SpringBootApplication +@EnableZipkinTracing +public class ZipkinSpanTestApplication { + public static void main(String[] args) { + SpringApplication.run(ZipkinSpanTestApplication.class); + } +} +``` +3. Add new span to the point of interest with annotation `@Span` + +Import `tracing.Span` package + +``` +import org.apache.servicecomb.tracing.Span; +``` +Add annotation `@Span` + +``` +@Component +public class SlowRepoImpl implements SlowRepo { + private static final Logger logger = LoggerFactory.getLogger(SlowRepoImpl.class); + + private final Random random = new Random(); + + @Span + @Override + public String crawl() throws InterruptedException { + logger.info("in /crawl"); + Thread.sleep(random.nextInt(200)); + return "crawled"; + } +} +``` + +That's it! + +## Reported Span Data +Customized tracing span includes two pieces of data: +* span name - annotated method name +* call.path - annotated method signature + +e.g. the example `SlowRepoImpl` in the previous section reports the following span + +| key | value | +| --- | --- | +| span name | crawl | +| call.path | public abstract java.lang.String org.apache.servicecomb.tests.tracing.SlowRepo.crawl() throws java.lang.InterruptedException | + +## Constraints +* Customized tracing with annotation only supports method calls in the request thread. +* Classes with `@Span` must be a spring managed bean. If you want to do load-time weaving for non spring beans, +you have to do it manually according to this [answer](https://stackoverflow.com/questions/41383941/load-time-weaving-for-non-spring-beans-in-a-spring-application). diff --git a/tracing/pom.xml b/tracing/pom.xml new file mode 100644 index 00000000000..7ffde2fbc3a --- /dev/null +++ b/tracing/pom.xml @@ -0,0 +1,51 @@ + + + + + + org.apache.servicecomb + java-chassis-parent + 3.4.0-SNAPSHOT + ../parents/default + + 4.0.0 + + tracing + Java Chassis::Tracing + pom + + tracing-common + tracing-zipkin + + + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-logging + + + + + + diff --git a/tracing/tracing-common/pom.xml b/tracing/tracing-common/pom.xml new file mode 100644 index 00000000000..f03ecbb2b04 --- /dev/null +++ b/tracing/tracing-common/pom.xml @@ -0,0 +1,33 @@ + + + + + + tracing + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + tracing-common + Java Chassis::Tracing::Common + + + diff --git a/tracing/tracing-common/src/main/java/org/apache/servicecomb/tracing/Span.java b/tracing/tracing-common/src/main/java/org/apache/servicecomb/tracing/Span.java new file mode 100644 index 00000000000..e58396fd542 --- /dev/null +++ b/tracing/tracing-common/src/main/java/org/apache/servicecomb/tracing/Span.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.tracing; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Span { + String spanName() default ""; + + String callPath() default ""; +} diff --git a/tracing/tracing-zipkin/pom.xml b/tracing/tracing-zipkin/pom.xml new file mode 100644 index 00000000000..e5755729748 --- /dev/null +++ b/tracing/tracing-zipkin/pom.xml @@ -0,0 +1,67 @@ + + + + + + tracing + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + tracing-zipkin + Java Chassis::Tracing::Zipkin + + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + handler-tracing-zipkin + + + org.apache.servicecomb + tracing-common + + + io.zipkin.brave + brave-spring-beans + + + io.zipkin.brave + brave + + + io.zipkin.brave + brave-context-slf4j + + + io.zipkin.reporter2 + zipkin-sender-okhttp3 + + + io.zipkin.reporter2 + zipkin-reporter-brave + + + + diff --git a/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/EnableZipkinTracing.java b/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/EnableZipkinTracing.java new file mode 100644 index 00000000000..f5667e29001 --- /dev/null +++ b/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/EnableZipkinTracing.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.tracing.zipkin; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; + +/** + * This annotation enables auto-configuration of Zipkin for auto span generation using annotation {@link org.apache.servicecomb.tracing.Span}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Import(ZipkinSpanAspectConfig.class) +public @interface EnableZipkinTracing { + +} diff --git a/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspect.java b/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspect.java new file mode 100644 index 00000000000..39106ddd327 --- /dev/null +++ b/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspect.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.tracing.zipkin; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; + +import org.apache.servicecomb.tracing.Span; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brave.Tracing; + +@Aspect +class ZipkinSpanAspect { + private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private final ZipkinTracingAdviser adviser; + + ZipkinSpanAspect(Tracing tracing) { + this.adviser = new ZipkinTracingAdviser(tracing.tracer()); + } + + @Around("execution(@org.apache.servicecomb.tracing.Span * *(..)) && @annotation(spanAnnotation)") + public Object advise(ProceedingJoinPoint joinPoint, Span spanAnnotation) throws Throwable { + String spanName = spanAnnotation.spanName(); + String callPath = spanAnnotation.callPath(); + Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); + LOG.debug("Generating zipkin span for method {}", method.toString()); + if ("".equals(spanName)) { + spanName = method.getName(); + } + if ("".equals(callPath)) { + callPath = method.toString(); + } + + return adviser.invoke(spanName, callPath, joinPoint::proceed); + } +} diff --git a/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspectConfig.java b/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspectConfig.java new file mode 100644 index 00000000000..0237e584a90 --- /dev/null +++ b/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspectConfig.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.tracing.zipkin; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +import brave.Tracing; + +@Configuration +@EnableAspectJAutoProxy +class ZipkinSpanAspectConfig { + @Bean + ZipkinSpanAspect zipkinSpanAspect(Tracing tracing) { + return new ZipkinSpanAspect(tracing); + } +} diff --git a/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviser.java b/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviser.java new file mode 100644 index 00000000000..b2bcf1c2423 --- /dev/null +++ b/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviser.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.tracing.zipkin; + +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + +import brave.Span; +import brave.Tracer; +import brave.Tracer.SpanInScope; + +class ZipkinTracingAdviser { + + static final String CALL_PATH = "call.path"; + + private final Tracer tracer; + + ZipkinTracingAdviser(Tracer tracer) { + this.tracer = tracer; + } + + @SuppressWarnings({"unused", "try"}) + T invoke(String spanName, String path, ThrowableSupplier supplier) throws Throwable { + Span span = createSpan(spanName, path); + try (SpanInScope spanInScope = tracer.withSpanInScope(span)) { + return supplier.get(); + } catch (Throwable throwable) { + span.tag("error", throwable.getClass().getSimpleName() + ": " + throwable.getMessage()); + throw throwable; + } finally { + span.finish(); + } + } + + private Span createSpan(String spanName, String path) { + InvocationContext context = ContextUtils.getInvocationContext(); + Span currentSpan = null; + if (context != null) { + currentSpan = context.getLocalContext(ZipkinTracingFilter.CONTEXT_TRACE_SPAN); + } + if (currentSpan == null) { + currentSpan = tracer.currentSpan(); + } + if (currentSpan != null) { + return tracer.newChild(currentSpan.context()).name(spanName).tag(CALL_PATH, path).start(); + } + + return tracer.newTrace().name(spanName).tag(CALL_PATH, path).start(); + } + + interface ThrowableSupplier { + T get() throws Throwable; + } +} diff --git a/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspectTest.java b/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspectTest.java new file mode 100644 index 00000000000..4eee935137d --- /dev/null +++ b/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspectTest.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.tracing.zipkin; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.apache.servicecomb.tracing.zipkin.ZipkinTracingAdviser.CALL_PATH; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; + +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.stream.Collectors; + +import org.apache.servicecomb.tracing.zipkin.ZipkinSpanAspectTest.TracingConfig; +import org.apache.servicecomb.tracing.zipkin.app.ZipkinSpanTestApplication; +import org.apache.servicecomb.tracing.zipkin.app.ZipkinSpanTestApplication.CustomSpanTask; +import org.apache.servicecomb.tracing.zipkin.app.ZipkinSpanTestApplication.SomeSlowTask; +import org.hamcrest.MatcherAssert; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +import brave.Tracing; +import brave.propagation.StrictScopeDecorator; +import brave.propagation.ThreadLocalCurrentTraceContext; +import zipkin2.Span; +import zipkin2.reporter.brave.ZipkinSpanHandler; + +@SpringBootTest(classes = {ZipkinSpanTestApplication.class, TracingConfig.class}) +public class ZipkinSpanAspectTest { + private Queue spans; + + private SomeSlowTask someSlowTask; + + private CustomSpanTask customSpanTask; + + private Tracing tracing; + + @Autowired + public void setSpans(Queue spans) { + this.spans = spans; + } + + @Autowired + public void setSomeSlowTask(SomeSlowTask someSlowTask) { + this.someSlowTask = someSlowTask; + } + + @Autowired + public void setCustomSpanTask(CustomSpanTask customSpanTask) { + this.customSpanTask = customSpanTask; + } + + @Autowired + public void setTracing(Tracing tracing) { + this.tracing = tracing; + } + + public ZipkinSpanAspectTest() { + } + + @AfterEach + public void tearDown() throws Exception { + tracing.close(); + } + + @Test + public void reportedSpanContainsAnnotatedMethodInfo() throws Exception { + someSlowTask.crawl(); + + await().atMost(2, SECONDS).until(() -> !spans.isEmpty()); + + zipkin2.Span span = spans.poll(); + MatcherAssert.assertThat(span.name(), is("crawl")); + MatcherAssert.assertThat(tracedValues(span), contains(SomeSlowTask.class.getMethod("crawl").toString())); + } + + @Test + public void reportCustomSpanInformation() { + customSpanTask.invoke(); + await().atMost(2, SECONDS).until(() -> !spans.isEmpty()); + + zipkin2.Span span = spans.poll(); + MatcherAssert.assertThat(span.name(), is("transaction1")); + MatcherAssert.assertThat(tracedValues(span), contains("startA")); + } + + private List tracedValues(zipkin2.Span spans) { + return spans.tags().entrySet().stream() + .filter(span -> CALL_PATH.equals(span.getKey()) || "error".equals(span.getKey())) + .filter(span -> span.getValue() != null) + .map(Map.Entry::getValue) + .distinct() + .collect(Collectors.toList()); + } + + @Configuration + static class TracingConfig { + @Bean + Queue spans() { + return new ConcurrentLinkedDeque<>(); + } + + @Bean + @Primary + Tracing zipkinTracing(Queue spans) { + return Tracing.newBuilder() + .currentTraceContext( + ThreadLocalCurrentTraceContext.newBuilder().addScopeDecorator(StrictScopeDecorator.create()).build()) + .addSpanHandler(ZipkinSpanHandler.create(spans::add)) + .build(); + } + } +} diff --git a/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviserTest.java b/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviserTest.java new file mode 100644 index 00000000000..60df34100ed --- /dev/null +++ b/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviserTest.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.tracing.zipkin; + +import static com.seanyinx.github.unit.scaffolding.AssertUtils.expectFailing; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.apache.servicecomb.tracing.zipkin.ZipkinTracingAdviser.CALL_PATH; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; + +import java.lang.reflect.Array; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; + +import org.apache.servicecomb.tracing.zipkin.ZipkinTracingAdviser.ThrowableSupplier; +import org.hamcrest.MatcherAssert; + +import brave.Span; +import brave.Tracer.SpanInScope; +import brave.Tracing; +import brave.propagation.StrictScopeDecorator; +import brave.propagation.ThreadLocalCurrentTraceContext; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import zipkin2.reporter.brave.ZipkinSpanHandler; + +public class ZipkinTracingAdviserTest { + private static final int nThreads = 10; + + private final String spanName = "some span"; + + private final String path = this.getClass().getCanonicalName(); + + private final String expected = "supplied"; + + private final RuntimeException error = new RuntimeException("oops"); + + private final ThrowableSupplier supplier = () -> expected; + + private final Map> traces = new ConcurrentHashMap<>(); + + private final Tracing tracing = Tracing.newBuilder() + .currentTraceContext( + ThreadLocalCurrentTraceContext.newBuilder().addScopeDecorator(StrictScopeDecorator.create()).build()) + .addSpanHandler(ZipkinSpanHandler.create( + e -> traces.computeIfAbsent(e.traceId(), id -> new ConcurrentLinkedDeque<>()).add(e))) + .build(); + + private final ZipkinTracingAdviser tracingAdviser = new ZipkinTracingAdviser(tracing.tracer()); + + @AfterEach + public void tearDown() throws Exception { + tracing.close(); + } + + @Test + public void startsNewRootSpan() throws Throwable { + String result = tracingAdviser.invoke(spanName, path, supplier); + + MatcherAssert.assertThat(result, is(expected)); + await().atMost(2, SECONDS).until(() -> !traces.isEmpty()); + + zipkin2.Span span = traces.values().iterator().next().poll(); + MatcherAssert.assertThat(span.name(), is(spanName)); + MatcherAssert.assertThat(tracedValues(span), contains(this.getClass().getCanonicalName())); + } + + @Test + public void includesExceptionInTags() throws Throwable { + try { + tracingAdviser.invoke(spanName, path, () -> { + throw error; + }); + + expectFailing(RuntimeException.class); + } catch (RuntimeException ignored) { + } + + await().atMost(2, SECONDS).until(() -> !traces.isEmpty()); + + zipkin2.Span span = traces.values().iterator().next().poll(); + Assertions.assertEquals(spanName, span.name()); + MatcherAssert.assertThat(tracedValues(span), + containsInAnyOrder(this.getClass().getCanonicalName(), "RuntimeException: oops")); + } + + @SuppressWarnings({"unused", "try"}) + @Test + public void startsNewChildSpan() { + CyclicBarrier cyclicBarrier = new CyclicBarrier(nThreads); + + CompletableFuture[] futures = (CompletableFuture[]) Array.newInstance(CompletableFuture.class, nThreads); + for (int i = 0; i < nThreads; i++) { + futures[i] = CompletableFuture.runAsync(() -> { + Span currentSpan = tracing.tracer().newTrace().start(); + + waitTillAllAreReady(cyclicBarrier); + + try (SpanInScope spanInScope = tracing.tracer().withSpanInScope(currentSpan)) { + MatcherAssert.assertThat(tracingAdviser.invoke(spanName, path, supplier), is(expected)); + } catch (Throwable throwable) { + Assertions.fail(throwable.getMessage()); + } finally { + currentSpan.finish(); + } + }, Executors.newFixedThreadPool(nThreads)); + } + + CompletableFuture.allOf(futures).join(); + + MatcherAssert.assertThat(traces.size(), is(nThreads)); + + for (Queue queue : traces.values()) { + zipkin2.Span child = queue.poll(); + MatcherAssert.assertThat(child.name(), is(spanName)); + + zipkin2.Span parent = queue.poll(); + MatcherAssert.assertThat(child.parentId(), is(parent.id())); + MatcherAssert.assertThat(child.traceId(), is(parent.traceId())); + MatcherAssert.assertThat(tracedValues(child), contains(this.getClass().getCanonicalName())); + } + } + + private void waitTillAllAreReady(CyclicBarrier cyclicBarrier) { + try { + cyclicBarrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + throw new RuntimeException(e); + } + } + + private List tracedValues(zipkin2.Span spans) { + return spans.tags().entrySet().stream() + .filter(span -> CALL_PATH.equals(span.getKey()) || "error".equals(span.getKey())) + .filter(span -> span.getValue() != null) + .map(Map.Entry::getValue) + .distinct() + .collect(Collectors.toList()); + } +} diff --git a/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/app/ZipkinSpanTestApplication.java b/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/app/ZipkinSpanTestApplication.java new file mode 100644 index 00000000000..d2ee1608c81 --- /dev/null +++ b/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/app/ZipkinSpanTestApplication.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.tracing.zipkin.app; + +import org.apache.servicecomb.tracing.Span; +import org.apache.servicecomb.tracing.zipkin.EnableZipkinTracing; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +@EnableZipkinTracing +public class ZipkinSpanTestApplication { + public static void main(String[] args) { + SpringApplication.run(ZipkinSpanTestApplication.class); + } + + @Bean + SomeSlowTask someSlowTask() { + return new SomeSlowTask(); + } + + @Bean + CustomSpanTask customSpanTask() { + return new CustomSpanTask(); + } + + public static class SomeSlowTask { + @Span + public String crawl() { + return "crawling..."; + } + } + + public static class CustomSpanTask { + @Span(spanName = "transaction1", callPath = "startA") + public String invoke() { + return "invoke the method"; + } + } +} diff --git a/transports/pom.xml b/transports/pom.xml index 90db1f3f25c..13439c0ffdc 100644 --- a/transports/pom.xml +++ b/transports/pom.xml @@ -1,11 +1,12 @@ - + 4.0.0 - io.servicecomb + org.apache.servicecomb java-chassis-parent - 0.3.0-SNAPSHOT - ../parent + 3.4.0-SNAPSHOT + ../parents/default transports + Java Chassis::Transports pom - transport-highway - transport-rest + transport-highway + transport-rest + transport-common - \ No newline at end of file + diff --git a/transports/transport-common/pom.xml b/transports/transport-common/pom.xml new file mode 100644 index 00000000000..00c1ae33c73 --- /dev/null +++ b/transports/transport-common/pom.xml @@ -0,0 +1,57 @@ + + + + + transports + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + + transport-common + Java Chassis::Transports::common + + + + org.apache.servicecomb + foundation-config + + + + org.apache.servicecomb + foundation-test-scaffolding + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.jmockit + jmockit + test + + + diff --git a/transports/transport-common/src/main/java/org/apache/servicecomb/transport/common/TransportConfigUtils.java b/transports/transport-common/src/main/java/org/apache/servicecomb/transport/common/TransportConfigUtils.java new file mode 100644 index 00000000000..672475e592c --- /dev/null +++ b/transports/transport-common/src/main/java/org/apache/servicecomb/transport/common/TransportConfigUtils.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.transport.common; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class TransportConfigUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(TransportConfigUtils.class); + + private TransportConfigUtils() { + } + + // old verticle count key is ambiguous + // suggest to use new name + public static int readVerticleCount(String key, String deprecatedKey) { + int count = LegacyPropertyFactory.getIntProperty(key, -1); + if (count > 0) { + return count; + } + + count = LegacyPropertyFactory.getIntProperty(deprecatedKey, -1); + if (count > 0) { + LOGGER.warn("{} is ambiguous, and deprecated, recommended to use {}.", deprecatedKey, key); + return count; + } + + // default value + count = Math.min(Runtime.getRuntime().availableProcessors(), 8); + LOGGER.info("{} not defined, set to {}.", key, count); + return count; + } +} diff --git a/transports/transport-common/src/test/java/org/apache/servicecomb/transport/common/TestTransportConfigUtils.java b/transports/transport-common/src/test/java/org/apache/servicecomb/transport/common/TestTransportConfigUtils.java new file mode 100644 index 00000000000..05351e62351 --- /dev/null +++ b/transports/transport-common/src/test/java/org/apache/servicecomb/transport/common/TestTransportConfigUtils.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.transport.common; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; +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.Mock; +import mockit.MockUp; + +public class TestTransportConfigUtils { + Environment environment = Mockito.mock(Environment.class); + + @BeforeEach + public void setup() { + LegacyPropertyFactory.setEnvironment(environment); + } + + @AfterAll + public static void teardown() { + } + + static String key = "verticle-count"; + + static String deprecatedKey = "thread-count"; + + @Test + public void readVerticleCount_new_exist() { + Mockito.when(environment.getProperty("verticle-count", int.class, -1)).thenReturn(10); + Assertions.assertEquals(10, TransportConfigUtils.readVerticleCount(key, deprecatedKey)); + } + + @Test + public void readVerticleCount_old_exist() { + Mockito.when(environment.getProperty("verticle-count", int.class, -1)).thenReturn(-1); + Mockito.when(environment.getProperty("thread-count", int.class, -1)).thenReturn(10); + LogCollector collector = new LogCollector(); + Assertions.assertEquals(10, TransportConfigUtils.readVerticleCount(key, deprecatedKey)); + Assertions.assertEquals("thread-count is ambiguous, and deprecated, recommended to use verticle-count.", + collector.getEvent(0).getMessage().getFormattedMessage()); + collector.tearDown(); + } + + @Test + public void readVerticleCount_default_smallCpu() { + Mockito.when(environment.getProperty("verticle-count", int.class, -1)).thenReturn(-1); + Mockito.when(environment.getProperty("thread-count", int.class, -1)).thenReturn(-1); + + new MockUp() { + @Mock + int availableProcessors() { + return 7; + } + }; + + LogCollector collector = new LogCollector(); + Assertions.assertEquals(7, TransportConfigUtils.readVerticleCount(key, deprecatedKey)); + Assertions.assertEquals("verticle-count not defined, set to 7.", + collector.getLastEvents().getMessage().getFormattedMessage()); + collector.tearDown(); + } + + @Test + public void readVerticleCount_default_bigCpu() { + Mockito.when(environment.getProperty("verticle-count", int.class, -1)).thenReturn(-1); + Mockito.when(environment.getProperty("thread-count", int.class, -1)).thenReturn(-1); + + AtomicInteger count = new AtomicInteger(8); + new MockUp() { + @Mock + int availableProcessors() { + return count.get(); + } + }; + + LogCollector collector = new LogCollector(); + Assertions.assertEquals(8, TransportConfigUtils.readVerticleCount(key, deprecatedKey)); + Assertions.assertEquals("verticle-count not defined, set to 8.", + collector.getLastEvents().getMessage().getFormattedMessage()); + + count.set(9); + collector.clear(); + Assertions.assertEquals(8, TransportConfigUtils.readVerticleCount(key, deprecatedKey)); + Assertions.assertEquals("verticle-count not defined, set to 8.", + collector.getLastEvents().getMessage().getFormattedMessage()); + + collector.tearDown(); + } +} diff --git a/transports/transport-common/src/test/resources/log4j2.xml b/transports/transport-common/src/test/resources/log4j2.xml new file mode 100644 index 00000000000..8cee07220ec --- /dev/null +++ b/transports/transport-common/src/test/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/transports/transport-highway/pom.xml b/transports/transport-highway/pom.xml index ab801064e9a..d722eb7aa23 100644 --- a/transports/transport-highway/pom.xml +++ b/transports/transport-highway/pom.xml @@ -1,11 +1,12 @@ - 4.0.0 - - io.servicecomb - transports - 0.3.0-SNAPSHOT - - transport-highway - - - io.servicecomb - java-chassis-core - - - io.servicecomb - foundation-vertx - - - io.servicecomb - common-protobuf - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + 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 + transports + 3.4.0-SNAPSHOT + + transport-highway + Java Chassis::Transports::Hightway + + + org.apache.servicecomb + java-chassis-core + + + org.apache.servicecomb + common-protobuf + + + org.apache.servicecomb + foundation-vertx + + + org.apache.servicecomb + foundation-metrics + + + org.apache.servicecomb + foundation-test-scaffolding + + + org.apache.servicecomb + transport-common + + + + io.vertx + vertx-codegen + provided + + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.jmockit + jmockit + test + + diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClient.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClient.java deleted file mode 100644 index cd3534e831d..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClient.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.codec.protobuf.definition.OperationProtobuf; -import io.servicecomb.codec.protobuf.definition.ProtobufManager; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.transport.AbstractTransport; -import io.servicecomb.foundation.ssl.SSLCustom; -import io.servicecomb.foundation.ssl.SSLOption; -import io.servicecomb.foundation.ssl.SSLOptionFactory; -import io.servicecomb.foundation.vertx.VertxTLSBuilder; -import io.servicecomb.foundation.vertx.VertxUtils; -import io.servicecomb.foundation.vertx.client.ClientPoolManager; -import io.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.Response; -import io.vertx.core.DeploymentOptions; -import io.vertx.core.Vertx; - -public class HighwayClient { - private static final Logger log = LoggerFactory.getLogger(HighwayClient.class); - - private static final String SSL_KEY = "highway.consumer"; - - private ClientPoolManager clientMgr = new ClientPoolManager<>(); - - private final boolean sslEnabled; - - public HighwayClient(boolean sslEnabled) { - this.sslEnabled = sslEnabled; - } - - public void init(Vertx vertx) throws Exception { - TcpClientConfig config = createTcpClientConfig(); - - DeploymentOptions deployOptions = VertxUtils.createClientDeployOptions(clientMgr, - HighwayConfig.getClientThreadCount(), - HighwayConfig.getClientConnectionPoolPerThread(), - config); - - VertxUtils.blockDeploy(vertx, HighwayClientVerticle.class, deployOptions); - } - - private TcpClientConfig createTcpClientConfig() { - TcpClientConfig tcpClientConfig = new TcpClientConfig(); - tcpClientConfig.setRequestTimeoutMillis(AbstractTransport.getRequestTimeout()); - - if (this.sslEnabled) { - SSLOptionFactory factory = - SSLOptionFactory.createSSLOptionFactory(SSL_KEY, null); - SSLOption sslOption; - if (factory == null) { - sslOption = SSLOption.buildFromYaml(SSL_KEY); - } else { - sslOption = factory.createSSLOption(); - } - SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); - VertxTLSBuilder.buildClientOptionsBase(sslOption, sslCustom, tcpClientConfig); - } - return tcpClientConfig; - } - - public void send(Invocation invocation, AsyncResponse asyncResp) throws Exception { - HighwayClientConnectionPool tcpClientPool = clientMgr.findThreadBindClientPool(); - - OperationMeta operationMeta = invocation.getOperationMeta(); - OperationProtobuf operationProtobuf = ProtobufManager.getOrCreateOperation(operationMeta); - - HighwayClientConnection tcpClient = tcpClientPool.findOrCreateClient(invocation.getEndpoint().getEndpoint()); - HighwayClientPackage clientPackage = new HighwayClientPackage(invocation, operationProtobuf, tcpClient); - log.debug("Calling method {} of {} by highway", operationMeta.getMethod(), invocation.getMicroserviceName()); - tcpClientPool.send(tcpClient, clientPackage, ar -> { - // 此时是在网络线程中,转换线程 - invocation.getResponseExecutor().execute(() -> { - if (ar.failed()) { - // 只会是本地异常 - asyncResp.consumerFail(ar.cause()); - return; - } - - // 处理应答 - try { - Response response = - HighwayCodec.decodeResponse(invocation, - operationProtobuf, - ar.result(), - tcpClient.getProtobufFeature()); - asyncResp.complete(response); - } catch (Throwable e) { - asyncResp.consumerFail(e); - } - }); - }); - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientConnection.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientConnection.java deleted file mode 100644 index 9a241661315..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientConnection.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.transport.highway; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.protostuff.runtime.ProtobufFeature; -import io.servicecomb.foundation.vertx.client.tcp.AbstractTcpClientPackage; -import io.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; -import io.servicecomb.foundation.vertx.client.tcp.TcpClientConnection; -import io.servicecomb.foundation.vertx.tcp.TcpOutputStream; -import io.servicecomb.transport.highway.message.LoginRequest; -import io.servicecomb.transport.highway.message.LoginResponse; -import io.servicecomb.transport.highway.message.RequestHeader; -import io.vertx.core.Context; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.net.NetClient; - -public class HighwayClientConnection extends TcpClientConnection { - private static final Logger LOGGER = LoggerFactory.getLogger(HighwayClientConnection.class); - - private ProtobufFeature protobufFeature = new ProtobufFeature(); - - public HighwayClientConnection(Context context, NetClient netClient, String endpoint, - TcpClientConfig clientConfig) { - super(context, netClient, endpoint, clientConfig); - setLocalSupportLogin(true); - } - - public ProtobufFeature getProtobufFeature() { - return protobufFeature; - } - - @Override - protected TcpOutputStream createLogin() { - try { - RequestHeader header = new RequestHeader(); - header.setMsgType(MsgType.LOGIN); - - LoginRequest login = new LoginRequest(); - login.setProtocol(HighwayTransport.NAME); - login.setUseProtobufMapCodec(true); - - HighwayOutputStream os = new HighwayOutputStream(AbstractTcpClientPackage.getAndIncRequestId(), null); - os.write(header, LoginRequest.getLoginRequestSchema(), login); - return os; - } catch (Throwable e) { - throw new Error("impossible.", e); - } - } - - @Override - protected boolean onLoginResponse(Buffer bodyBuffer) { - try { - LoginResponse response = LoginResponse.readObject(bodyBuffer); - protobufFeature.setUseProtobufMapCodec(response.isUseProtobufMapCodec()); - return true; - } catch (Throwable e) { - LOGGER.error("decode login response failed.", e); - return false; - } - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientConnectionPool.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientConnectionPool.java deleted file mode 100644 index bfc86a22005..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientConnectionPool.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.transport.highway; - -import io.servicecomb.foundation.vertx.client.tcp.AbstractTcpClientConnectionPool; -import io.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; -import io.vertx.core.Context; -import io.vertx.core.net.NetClient; - -public class HighwayClientConnectionPool extends AbstractTcpClientConnectionPool { - public HighwayClientConnectionPool(TcpClientConfig clientConfig, Context context, NetClient netClient) { - super(clientConfig, context, netClient); - } - - @Override - protected HighwayClientConnection create(String endpoint) { - return new HighwayClientConnection(context, netClient, endpoint, clientConfig); - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientManager.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientManager.java deleted file mode 100644 index 05e45d56a94..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientManager.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.foundation.vertx.VertxUtils; -import io.vertx.core.Vertx; - -public final class HighwayClientManager { - private static final Logger LOGGER = LoggerFactory.getLogger(HighwayClientManager.class); - - public static final HighwayClientManager INSTANCE = new HighwayClientManager(); - - // same instance in AbstractTranport. need refactor in future. - private final Vertx transportVertx = VertxUtils.getOrCreateVertxByName("transport", null); - - private static final Object LOCK = new Object(); - - private volatile HighwayClient sslClient = null; - - private volatile HighwayClient nonSslCient = null; - - private HighwayClientManager() { - } - - public HighwayClient getHighwayClient(boolean sslEnabled) { - try { - if (sslEnabled) { - if (sslClient == null) { - synchronized (LOCK) { - if (sslClient == null) { - HighwayClient client = new HighwayClient(true); - client.init(transportVertx); - sslClient = client; - } - } - } - return sslClient; - } else { - if (nonSslCient == null) { - synchronized (LOCK) { - if (nonSslCient == null) { - HighwayClient client = new HighwayClient(false); - client.init(transportVertx); - nonSslCient = client; - } - } - } - return nonSslCient; - } - } catch (Exception e) { - LOGGER.error(""); - throw new IllegalStateException("init rest client transport failed."); - } - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientPackage.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientPackage.java deleted file mode 100644 index d189c157ed1..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientPackage.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.transport.highway; - -import io.servicecomb.codec.protobuf.definition.OperationProtobuf; -import io.servicecomb.core.Invocation; -import io.servicecomb.foundation.vertx.client.tcp.AbstractTcpClientPackage; -import io.servicecomb.foundation.vertx.tcp.TcpOutputStream; - -public class HighwayClientPackage extends AbstractTcpClientPackage { - private Invocation invocation; - - private OperationProtobuf operationProtobuf; - - private HighwayClientConnection tcpClient; - - public HighwayClientPackage(Invocation invocation, OperationProtobuf operationProtobuf, - HighwayClientConnection tcpClient) { - this.invocation = invocation; - this.operationProtobuf = operationProtobuf; - this.tcpClient = tcpClient; - } - - @Override - public TcpOutputStream createStream() { - try { - return HighwayCodec.encodeRequest(msgId, invocation, operationProtobuf, tcpClient.getProtobufFeature()); - } catch (Exception e) { - String msg = String.format("encode request failed. appid=%s, qualifiedName=%s", - invocation.getAppId(), - invocation.getOperationMeta().getMicroserviceQualifiedName()); - throw new Error(msg, e); - } - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientVerticle.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientVerticle.java deleted file mode 100644 index 92ad84300f9..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayClientVerticle.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.transport.highway; - -import io.servicecomb.foundation.vertx.client.tcp.AbstractTcpClientVerticle; - -public class HighwayClientVerticle - extends AbstractTcpClientVerticle { - @Override - public HighwayClientConnectionPool createClientPool() { - return new HighwayClientConnectionPool(clientConfig, context, netClient); - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayCodec.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayCodec.java deleted file mode 100644 index 3ac267f5142..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayCodec.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import io.protostuff.runtime.ProtobufFeature; -import io.servicecomb.codec.protobuf.definition.OperationProtobuf; -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.invocation.InvocationFactory; -import io.servicecomb.foundation.vertx.client.tcp.TcpData; -import io.servicecomb.foundation.vertx.tcp.TcpOutputStream; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.transport.highway.message.RequestHeader; -import io.servicecomb.transport.highway.message.ResponseHeader; -import io.vertx.core.buffer.Buffer; - -public final class HighwayCodec { - private static HighwayTransport highwayTransport; - - private HighwayCodec() { - } - - public static void setHighwayTransport(HighwayTransport highwayTransport) { - HighwayCodec.highwayTransport = highwayTransport; - } - - public static TcpOutputStream encodeRequest(long msgId, Invocation invocation, - OperationProtobuf operationProtobuf, ProtobufFeature protobufFeature) throws Exception { - // 写header - RequestHeader header = new RequestHeader(); - header.setMsgType(MsgType.REQUEST); - header.setFlags(0); - header.setDestMicroservice(invocation.getMicroserviceName()); - header.setSchemaId(invocation.getSchemaId()); - header.setOperationName(invocation.getOperationName()); - header.setContext(invocation.getContext()); - - HighwayOutputStream os = new HighwayOutputStream(msgId, protobufFeature); - os.write(header, operationProtobuf.getRequestSchema(), invocation.getArgs()); - return os; - } - - public static Invocation decodeRequest(RequestHeader header, OperationProtobuf operationProtobuf, - Buffer bodyBuffer, ProtobufFeature protobufFeature) throws Exception { - WrapSchema schema = operationProtobuf.getRequestSchema(); - Object[] args = schema.readObject(bodyBuffer, protobufFeature); - - Invocation invocation = - InvocationFactory.forProvider(highwayTransport.getEndpoint(), - operationProtobuf.getOperationMeta(), - args); - invocation.setContext(header.getContext()); - return invocation; - } - - public static RequestHeader readRequestHeader(Buffer headerBuffer, - ProtobufFeature protobufFeature) throws Exception { - return RequestHeader.readObject(headerBuffer, protobufFeature); - } - - public static Buffer encodeResponse(long msgId, ResponseHeader header, WrapSchema bodySchema, - Object body, ProtobufFeature protobufFeature) throws Exception { - try (HighwayOutputStream os = new HighwayOutputStream(msgId, protobufFeature)) { - os.write(header, bodySchema, body); - return os.getBuffer(); - } - } - - public static Response decodeResponse(Invocation invocation, OperationProtobuf operationProtobuf, - TcpData tcpData, ProtobufFeature protobufFeature) throws Exception { - ResponseHeader header = ResponseHeader.readObject(tcpData.getHeaderBuffer(), protobufFeature); - if (header.getContext() != null) { - invocation.getContext().putAll(header.getContext()); - } - - WrapSchema bodySchema = operationProtobuf.findResponseSchema(header.getStatusCode()); - Object body = bodySchema.readObject(tcpData.getBodyBuffer(), protobufFeature); - - Response response = Response.create(header.getStatusCode(), header.getReasonPhrase(), body); - response.setHeaders(header.getHeaders()); - - return response; - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayConfig.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayConfig.java deleted file mode 100644 index 3224513225d..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayConfig.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import com.netflix.config.DynamicIntProperty; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicStringProperty; - -public final class HighwayConfig { - private HighwayConfig() { - } - - public static String getAddress() { - DynamicStringProperty address = - DynamicPropertyFactory.getInstance().getStringProperty("cse.highway.address", null); - return address.get(); - } - - public static int getServerThreadCount() { - DynamicIntProperty address = - DynamicPropertyFactory.getInstance().getIntProperty("cse.highway.server.thread-count", 1); - return address.get(); - } - - public static int getClientThreadCount() { - DynamicIntProperty address = - DynamicPropertyFactory.getInstance().getIntProperty("cse.highway.client.thread-count", 1); - return address.get(); - } - - public static int getClientConnectionPoolPerThread() { - DynamicIntProperty address = - DynamicPropertyFactory.getInstance().getIntProperty("cse.highway.client.connection-pool-per-thread", 1); - return address.get(); - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayOutputStream.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayOutputStream.java deleted file mode 100644 index 9898d8aa334..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayOutputStream.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.transport.highway; - -import io.protostuff.LinkedBuffer; -import io.protostuff.ProtobufOutput; -import io.protostuff.runtime.ProtobufFeature; -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.servicecomb.foundation.vertx.tcp.TcpOutputStream; -import io.servicecomb.transport.highway.message.RequestHeader; -import io.servicecomb.transport.highway.message.ResponseHeader; - -public class HighwayOutputStream extends TcpOutputStream { - private ProtobufFeature protobufFeature; - - public HighwayOutputStream(long msgId, ProtobufFeature protobufFeature) { - super(msgId); - this.protobufFeature = protobufFeature; - } - - public void write(RequestHeader header, WrapSchema bodySchema, Object body) throws Exception { - write(RequestHeader.getRequestHeaderSchema(), header, bodySchema, body); - } - - public void write(ResponseHeader header, WrapSchema bodySchema, Object body) throws Exception { - write(ResponseHeader.getResponseHeaderSchema(), header, bodySchema, body); - } - - public void write(WrapSchema headerSchema, Object header, WrapSchema bodySchema, Object body) throws Exception { - // 写protobuf数据 - LinkedBuffer linkedBuffer = LinkedBuffer.allocate(); - ProtobufOutput output = new ProtobufOutput(linkedBuffer); - - // 写header - if (headerSchema != null) { - headerSchema.writeObject(output, header, protobufFeature); - } - int headerSize = output.getSize(); - - // 写body - // void时bodySchema为null - if (bodySchema != null) { - bodySchema.writeObject(output, body, protobufFeature); - } - - writeLength(output.getSize(), headerSize); - LinkedBuffer.writeTo(this, linkedBuffer); - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayServer.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayServer.java deleted file mode 100644 index be6beb7eeb6..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayServer.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.foundation.vertx.server.TcpServer; -import io.servicecomb.foundation.vertx.server.TcpServerConnection; - -public class HighwayServer extends TcpServer { - - public HighwayServer(URIEndpointObject endpointObject) { - super(endpointObject); - } - - @Override - protected TcpServerConnection createTcpServerConnection() { - return new HighwayServerConnection(); - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayServerConnection.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayServerConnection.java deleted file mode 100644 index d8c79ead687..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayServerConnection.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.transport.highway; - -import javax.ws.rs.core.Response.Status; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.protostuff.runtime.ProtobufFeature; -import io.servicecomb.foundation.vertx.server.TcpBufferHandler; -import io.servicecomb.foundation.vertx.server.TcpParser; -import io.servicecomb.foundation.vertx.server.TcpServerConnection; -import io.servicecomb.transport.highway.message.LoginRequest; -import io.servicecomb.transport.highway.message.LoginResponse; -import io.servicecomb.transport.highway.message.RequestHeader; -import io.servicecomb.transport.highway.message.ResponseHeader; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.net.NetSocket; - -public class HighwayServerConnection extends TcpServerConnection implements TcpBufferHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(HighwayServerConnection.class); - - private ProtobufFeature protobufFeature = new ProtobufFeature(); - - @Override - public void init(NetSocket netSocket) { - splitter = new TcpParser(this); - super.init(netSocket); - } - - @Override - public void handle(long msgId, Buffer headerBuffer, Buffer bodyBuffer) { - RequestHeader requestHeader = decodeRequestHeader(msgId, headerBuffer); - if (requestHeader == null) { - return; - } - - switch (requestHeader.getMsgType()) { - case MsgType.REQUEST: - onRequest(msgId, requestHeader, bodyBuffer); - break; - case MsgType.LOGIN: - onLogin(msgId, requestHeader, bodyBuffer); - break; - - default: - throw new Error("Unknown tcp msgType " + requestHeader.getMsgType()); - } - } - - protected RequestHeader decodeRequestHeader(long msgId, Buffer headerBuffer) { - RequestHeader requestHeader = null; - try { - requestHeader = HighwayCodec.readRequestHeader(headerBuffer, protobufFeature); - } catch (Exception e) { - String msg = String.format("decode request header error, msgId=%d", - msgId); - LOGGER.error(msg, e); - - netSocket.close(); - return null; - } - - return requestHeader; - } - - protected void onLogin(long msgId, RequestHeader header, Buffer bodyBuffer) { - LoginRequest request = null; - try { - request = LoginRequest.readObject(bodyBuffer); - } catch (Exception e) { - String msg = String.format("decode setParameter error, msgId=%d", - msgId); - LOGGER.error(msg, e); - netSocket.close(); - return; - } - - if (request != null) { - this.setProtocol(request.getProtocol()); - this.setZipName(request.getZipName()); - this.protobufFeature.setUseProtobufMapCodec(request.isUseProtobufMapCodec()); - } - - try (HighwayOutputStream os = new HighwayOutputStream(msgId, protobufFeature)) { - ResponseHeader responseHeader = new ResponseHeader(); - responseHeader.setStatusCode(Status.OK.getStatusCode()); - - LoginResponse response = new LoginResponse(); - response.setUseProtobufMapCodec(protobufFeature.isUseProtobufMapCodec()); - - os.write(ResponseHeader.getResponseHeaderSchema(), - responseHeader, - LoginResponse.getLoginResponseSchema(), - response); - netSocket.write(os.getBuffer()); - } catch (Exception e) { - throw new Error("impossible.", e); - } - } - - protected void onRequest(long msgId, RequestHeader header, Buffer bodyBuffer) { - HighwayServerInvoke invoke = new HighwayServerInvoke(protobufFeature); - if (invoke.init(this, msgId, header, bodyBuffer)) { - invoke.execute(); - } - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayServerInvoke.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayServerInvoke.java deleted file mode 100644 index 010c0ef6251..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayServerInvoke.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.protostuff.runtime.ProtobufFeature; -import io.servicecomb.codec.protobuf.definition.OperationProtobuf; -import io.servicecomb.codec.protobuf.definition.ProtobufManager; -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.servicecomb.core.CseContext; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.foundation.vertx.tcp.TcpConnection; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.InvocationException; -import io.servicecomb.transport.highway.message.RequestHeader; -import io.servicecomb.transport.highway.message.ResponseHeader; -import io.vertx.core.buffer.Buffer; - -public class HighwayServerInvoke { - private static final Logger LOGGER = LoggerFactory.getLogger(HighwayServerInvoke.class); - - private MicroserviceMetaManager microserviceMetaManager = CseContext.getInstance().getMicroserviceMetaManager(); - - private ProtobufFeature protobufFeature; - - private RequestHeader header; - - private OperationMeta operationMeta; - - private OperationProtobuf operationProtobuf; - - private TcpConnection connection; - - private long msgId; - - private Buffer bodyBuffer; - - public HighwayServerInvoke() { - this(null); - } - - public HighwayServerInvoke(ProtobufFeature protobufFeature) { - this.protobufFeature = protobufFeature; - } - - public void setMicroserviceMetaManager(MicroserviceMetaManager microserviceMetaManager) { - this.microserviceMetaManager = microserviceMetaManager; - } - - public boolean init(TcpConnection connection, long msgId, - RequestHeader header, Buffer bodyBuffer) { - try { - doInit(connection, msgId, header, bodyBuffer); - return true; - } catch (Throwable e) { - String microserviceQualifidName = "unknown"; - if (operationMeta != null) { - microserviceQualifidName = operationMeta.getMicroserviceQualifiedName(); - } - String msg = String.format("decode request error, microserviceQualifidName=%s, msgId=%d", - microserviceQualifidName, - msgId); - LOGGER.error(msg, e); - - return false; - } - } - - private void doInit(TcpConnection connection, long msgId, RequestHeader header, - Buffer bodyBuffer) throws Exception { - this.connection = connection; - this.msgId = msgId; - this.header = header; - - MicroserviceMeta microserviceMeta = microserviceMetaManager.ensureFindValue(header.getDestMicroservice()); - SchemaMeta schemaMeta = microserviceMeta.ensureFindSchemaMeta(header.getSchemaId()); - this.operationMeta = schemaMeta.ensureFindOperation(header.getOperationName()); - this.operationProtobuf = ProtobufManager.getOrCreateOperation(operationMeta); - - this.bodyBuffer = bodyBuffer; - } - - private void runInExecutor() { - try { - doRunInExecutor(); - } catch (Throwable e) { - String msg = String.format("handle request error, %s, msgId=%d", - operationMeta.getMicroserviceQualifiedName(), - msgId); - LOGGER.error(msg, e); - - sendResponse(header.getContext(), Response.providerFailResp(e)); - } - } - - private void doRunInExecutor() throws Exception { - Invocation invocation = HighwayCodec.decodeRequest(header, operationProtobuf, bodyBuffer, protobufFeature); - - invocation.next(response -> { - sendResponse(invocation.getContext(), response); - }); - } - - private void sendResponse(Map context, Response response) { - ResponseHeader header = new ResponseHeader(); - header.setStatusCode(response.getStatusCode()); - header.setReasonPhrase(response.getReasonPhrase()); - header.setContext(context); - header.setHeaders(response.getHeaders()); - - WrapSchema bodySchema = operationProtobuf.findResponseSchema(response.getStatusCode()); - Object body = response.getResult(); - if (response.isFailed()) { - body = ((InvocationException) body).getErrorData(); - } - - try { - Buffer respBuffer = HighwayCodec.encodeResponse(msgId, header, bodySchema, body, protobufFeature); - connection.write(respBuffer.getByteBuf()); - } catch (Exception e) { - // 没招了,直接打日志 - String msg = String.format("encode response failed, %s, msgId=%d", - operationProtobuf.getOperationMeta().getMicroserviceQualifiedName(), - msgId); - LOGGER.error(msg, e); - } - } - - public void execute() { - operationMeta.getExecutor().execute(this::runInExecutor); - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayServerVerticle.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayServerVerticle.java deleted file mode 100644 index 7c02d4bfd90..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayServerVerticle.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import java.net.InetSocketAddress; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.transport.AbstractTransport; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.vertx.core.AbstractVerticle; -import io.vertx.core.Context; -import io.vertx.core.Future; -import io.vertx.core.Vertx; - -public class HighwayServerVerticle extends AbstractVerticle { - private static final Logger LOGGER = LoggerFactory.getLogger(HighwayServerVerticle.class); - - public static final String SSL_KEY = "highway.provider"; - - private Endpoint endpoint; - - private URIEndpointObject endpointObject; - - @Override - public void init(Vertx vertx, Context context) { - super.init(vertx, context); - this.endpoint = (Endpoint) context.config().getValue(AbstractTransport.ENDPOINT_KEY); - this.endpointObject = (URIEndpointObject) this.endpoint.getAddress(); - } - - @Override - public void start(Future startFuture) throws Exception { - super.start(); - - startListen(startFuture); - } - - protected void startListen(Future startFuture) { - // 如果本地未配置地址,则表示不必监听,只需要作为客户端使用即可 - if (endpointObject == null) { - LOGGER.warn("highway listen address is not configured, will not listen."); - startFuture.complete(); - return; - } - - HighwayServer server = new HighwayServer(endpointObject); - server.init(vertx, SSL_KEY, ar -> { - if (ar.succeeded()) { - InetSocketAddress socketAddress = ar.result(); - LOGGER.info("highway listen success. address={}:{}", - socketAddress.getHostString(), - socketAddress.getPort()); - startFuture.complete(); - return; - } - - LOGGER.error(HighwayTransport.NAME, ar.cause()); - startFuture.fail(ar.cause()); - }); - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayTransport.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayTransport.java deleted file mode 100644 index a8e98b5182f..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/HighwayTransport.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import java.util.Collections; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.transport.AbstractTransport; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.foundation.vertx.SimpleJsonObject; -import io.servicecomb.foundation.vertx.VertxUtils; -import io.servicecomb.foundation.vertx.tcp.TcpConst; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.vertx.core.DeploymentOptions; - -@Component -public class HighwayTransport extends AbstractTransport { - - private static final Logger log = LoggerFactory.getLogger(HighwayTransport.class); - - public static final String NAME = "highway"; - - @Override - public String getName() { - return NAME; - } - - public boolean init() throws Exception { - HighwayCodec.setHighwayTransport(this); - - DeploymentOptions deployOptions = new DeploymentOptions().setInstances(HighwayConfig.getServerThreadCount()); - setListenAddressWithoutSchema(HighwayConfig.getAddress(), Collections.singletonMap(TcpConst.LOGIN, "true")); - SimpleJsonObject json = new SimpleJsonObject(); - json.put(ENDPOINT_KEY, getEndpoint()); - deployOptions.setConfig(json); - return VertxUtils.blockDeploy(transportVertx, HighwayServerVerticle.class, deployOptions) && deployClient(); - } - - private boolean deployClient() { - return HighwayClientManager.INSTANCE.getHighwayClient(true) != null && - HighwayClientManager.INSTANCE.getHighwayClient(false) != null; - } - - @Override - public void send(Invocation invocation, AsyncResponse asyncResp) throws Exception { - URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); - HighwayClient client = - HighwayClientManager.INSTANCE.getHighwayClient(endpoint.isSslEnabled()); - log.debug("Sending request by highway to endpoint {}:{}", endpoint.getHostOrIp(), endpoint.getPort()); - client.send(invocation, asyncResp); - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/MsgType.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/MsgType.java deleted file mode 100644 index 53aa7377eeb..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/MsgType.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.transport.highway; - -public final class MsgType { - public static final byte REQUEST = 0; - - public static final byte LOGIN = 1; - - private MsgType() { - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/message/LoginRequest.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/message/LoginRequest.java deleted file mode 100644 index d3fb588016e..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/message/LoginRequest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.transport.highway.message; - -import io.protostuff.ProtobufOutput; -import io.protostuff.Tag; -import io.servicecomb.codec.protobuf.utils.ProtobufSchemaUtils; -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.vertx.core.buffer.Buffer; - -public class LoginRequest { - private static WrapSchema loginRequestSchema = ProtobufSchemaUtils.getOrCreateSchema(LoginRequest.class); - - public static WrapSchema getLoginRequestSchema() { - return loginRequestSchema; - } - - public static LoginRequest readObject(Buffer bodyBuffer) throws Exception { - return loginRequestSchema.readObject(bodyBuffer, null); - } - - @Tag(1) - private String protocol; - - // 压缩算法名字 - @Tag(2) - private String zipName; - - // 历史版本中的protoStuff实现的protobuf的map编码与标准的protobuf不兼容 - // 为保持highway的兼容,旧的不兼容编码也要保留 - // 只有LoginRequest/LoginResponse同时为true时,才使用标准protobuf编码 - @Tag(3) - private boolean useProtobufMapCodec; - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - public String getZipName() { - return zipName; - } - - public void setZipName(String zipName) { - this.zipName = zipName; - } - - public boolean isUseProtobufMapCodec() { - return useProtobufMapCodec; - } - - public void setUseProtobufMapCodec(boolean useProtobufMapCodec) { - this.useProtobufMapCodec = useProtobufMapCodec; - } - - public void writeObject(ProtobufOutput output) throws Exception { - loginRequestSchema.writeObject(output, this); - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/message/LoginResponse.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/message/LoginResponse.java deleted file mode 100644 index 07ec73467a2..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/message/LoginResponse.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.transport.highway.message; - -import io.protostuff.ProtobufOutput; -import io.protostuff.Tag; -import io.servicecomb.codec.protobuf.utils.ProtobufSchemaUtils; -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.vertx.core.buffer.Buffer; - -public class LoginResponse { - private static WrapSchema loginResponseSchema = ProtobufSchemaUtils.getOrCreateSchema(LoginResponse.class); - - public static WrapSchema getLoginResponseSchema() { - return loginResponseSchema; - } - - public static LoginResponse readObject(Buffer bodyBuffer) throws Exception { - return loginResponseSchema.readObject(bodyBuffer, null); - } - - @Tag(1) - private String protocol; - - // 压缩算法名字 - @Tag(2) - private String zipName; - - // 历史版本中的protoStuff实现的protobuf的map编码与标准的protobuf不兼容 - // 为保持highway的兼容,旧的不兼容编码也要保留 - // 只有LoginRequest/LoginResponse同时为true时,才使用标准protobuf编码 - @Tag(3) - private boolean useProtobufMapCodec; - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - public String getZipName() { - return zipName; - } - - public void setZipName(String zipName) { - this.zipName = zipName; - } - - public boolean isUseProtobufMapCodec() { - return useProtobufMapCodec; - } - - public void setUseProtobufMapCodec(boolean useProtobufMapCodec) { - this.useProtobufMapCodec = useProtobufMapCodec; - } - - public void writeObject(ProtobufOutput output) throws Exception { - loginResponseSchema.writeObject(output, this); - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/message/RequestHeader.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/message/RequestHeader.java deleted file mode 100644 index 5487682b9ea..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/message/RequestHeader.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway.message; - -import java.util.Map; - -import io.protostuff.ProtobufOutput; -import io.protostuff.Tag; -import io.protostuff.runtime.ProtobufFeature; -import io.servicecomb.codec.protobuf.utils.ProtobufSchemaUtils; -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.vertx.core.buffer.Buffer; - -/** - * tag的缺失、乱序,是因为要兼容历史版本 - * 1.tag(4),是历史版本中的压缩算法名,转移到login消息中传递 - */ -public class RequestHeader { - private static WrapSchema requestHeaderSchema = ProtobufSchemaUtils.getOrCreateSchema(RequestHeader.class); - - public static WrapSchema getRequestHeaderSchema() { - return requestHeaderSchema; - } - - public static RequestHeader readObject(Buffer bodyBuffer, ProtobufFeature protobufFeature) throws Exception { - return requestHeaderSchema.readObject(bodyBuffer, protobufFeature); - } - - //CHECKSTYLE:OFF: magicnumber - @Tag(2) - private byte msgType; - - // 运行时必须的数据,比如body是否压缩 - // 预留特性选项 - @Tag(3) - private int flags; - - @Tag(1) - private String destMicroservice; - - @Tag(5) - private String schemaId; - - @Tag(6) - private String operationName; - - @Tag(7) - private Map context; - - //CHECKSTYLE:ON - public byte getMsgType() { - return msgType; - } - - public void setMsgType(byte msgType) { - this.msgType = msgType; - } - - public String getDestMicroservice() { - return destMicroservice; - } - - public void setDestMicroservice(String destMicroservice) { - this.destMicroservice = destMicroservice; - } - - public int getFlags() { - return flags; - } - - public void setFlags(int flags) { - this.flags = flags; - } - - public String getSchemaId() { - return schemaId; - } - - public void setSchemaId(String schemaId) { - this.schemaId = schemaId; - } - - public String getOperationName() { - return operationName; - } - - public void setOperationName(String operationName) { - this.operationName = operationName; - } - - public Map getContext() { - return context; - } - - public void setContext(Map context) { - this.context = context; - } - - public void writeObject(ProtobufOutput output) throws Exception { - requestHeaderSchema.writeObject(output, this); - } -} diff --git a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/message/ResponseHeader.java b/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/message/ResponseHeader.java deleted file mode 100644 index 84177e22c36..00000000000 --- a/transports/transport-highway/src/main/java/io/servicecomb/transport/highway/message/ResponseHeader.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway.message; - -import java.util.Map; - -import io.protostuff.ProtobufOutput; -import io.protostuff.Tag; -import io.protostuff.runtime.ProtobufFeature; -import io.servicecomb.codec.protobuf.utils.ProtobufSchemaUtils; -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.servicecomb.swagger.invocation.response.Headers; -import io.vertx.core.buffer.Buffer; - -public class ResponseHeader { - private static WrapSchema responseHeaderSchema = ProtobufSchemaUtils.getOrCreateSchema(ResponseHeader.class); - - public static WrapSchema getResponseHeaderSchema() { - return responseHeaderSchema; - } - - public static ResponseHeader readObject(Buffer bodyBuffer, ProtobufFeature protobufFeature) throws Exception { - return responseHeaderSchema.readObject(bodyBuffer, protobufFeature); - } - - // 运行时必须的数据,比如body是否压缩 - // 预留特性选项 - //CHECKSTYLE:OFF: magicnumber - @Tag(5) - private int flags; - - @Tag(1) - private int statusCode; - - @Tag(2) - private String reason; - - @Tag(3) - private Map context; - - @Tag(4) - private Headers headers = new Headers(); - - //CHECKSTYLE:ON: magicnumber - public int getStatusCode() { - return statusCode; - } - - public void setStatusCode(int statusCode) { - this.statusCode = statusCode; - } - - public String getReasonPhrase() { - return reason; - } - - public void setReasonPhrase(String reason) { - this.reason = reason; - } - - public Map getContext() { - return context; - } - - public void setContext(Map context) { - this.context = context; - } - - public Headers getHeaders() { - return headers; - } - - public void setHeaders(Headers headers) { - this.headers = headers; - } - - public void writeObject(ProtobufOutput output) throws Exception { - responseHeaderSchema.writeObject(output, this); - } -} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClient.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClient.java new file mode 100644 index 00000000000..681bfbd3874 --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClient.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.transport.highway; + +import java.util.Map; + +import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; +import org.apache.servicecomb.core.Invocation; +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.vertx.VertxTLSBuilder; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager; +import org.apache.servicecomb.foundation.vertx.client.ClientVerticle; +import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; + +import com.google.common.annotations.VisibleForTesting; + +import io.vertx.core.DeploymentOptions; +import io.vertx.core.Vertx; + +public class HighwayClient { + private static final String SSL_KEY = "highway.consumer"; + + private ClientPoolManager clientMgr; + + public void init(Vertx vertx) throws Exception { + TcpClientConfig normalConfig = createTcpClientConfig(); + normalConfig.setSsl(false); + + TcpClientConfig sslConfig = createTcpClientConfig(); + sslConfig.setSsl(true); + + clientMgr = new ClientPoolManager<>(vertx, new HighwayClientPoolFactory(normalConfig, sslConfig)); + + DeploymentOptions deployOptions = VertxUtils.createClientDeployOptions(clientMgr, + HighwayConfig.getClientThreadCount()); + Map result = VertxUtils.blockDeploy(vertx, ClientVerticle.class, deployOptions); + if (!(boolean) result.get("code")) { + throw new IllegalStateException((String) result.get("message")); + } + } + + @VisibleForTesting + TcpClientConfig createTcpClientConfig() { + TcpClientConfig tcpClientConfig = new TcpClientConfig(); + // global request timeout to be login timeout + tcpClientConfig.setMsLoginTimeout( + LegacyPropertyFactory.getLongProperty("servicecomb.request.timeout", TcpClientConfig.DEFAULT_LOGIN_TIMEOUT)); + + SSLOptionFactory factory = + SSLOptionFactory.createSSLOptionFactory(SSL_KEY, LegacyPropertyFactory.getEnvironment()); + SSLOption sslOption; + if (factory == null) { + sslOption = SSLOption.build(SSL_KEY, LegacyPropertyFactory.getEnvironment()); + } else { + sslOption = factory.createSSLOption(); + } + SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); + VertxTLSBuilder.buildClientOptionsBase(sslOption, sslCustom, tcpClientConfig); + if (!sslOption.isCheckCNHost()) { + tcpClientConfig.setHostnameVerificationAlgorithm(""); + } else { + tcpClientConfig.setHostnameVerificationAlgorithm("HTTPS"); + } + + return tcpClientConfig; + } + + public HighwayClientPackage createClientPackage(Invocation invocation, OperationProtobuf operationProtobuf) { + long msRequestTimeout = invocation.getOperationMeta().getConfig().getMsRequestTimeout(); + return new HighwayClientPackage(invocation, operationProtobuf, msRequestTimeout); + } + + public HighwayClientConnection findClientPool(Invocation invocation) { + return clientMgr.findClientPool(invocation.isSync()) + .findOrCreateClient(invocation.getEndpoint().getEndpoint()); + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientConnection.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientConnection.java new file mode 100644 index 00000000000..77d6fcfcc22 --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientConnection.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.transport.highway; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.foundation.vertx.client.tcp.AbstractTcpClientPackage; +import org.apache.servicecomb.foundation.vertx.client.tcp.NetClientWrapper; +import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConnection; +import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; +import org.apache.servicecomb.transport.highway.message.LoginRequest; +import org.apache.servicecomb.transport.highway.message.LoginResponse; +import org.apache.servicecomb.transport.highway.message.RequestHeader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.Context; +import io.vertx.core.buffer.Buffer; + +public class HighwayClientConnection extends TcpClientConnection { + private static final Logger LOGGER = LoggerFactory.getLogger(HighwayClientConnection.class); + + public HighwayClientConnection(Context context, NetClientWrapper netClientWrapper, String endpoint) { + super(context, netClientWrapper, endpoint); + setLocalSupportLogin(true); + } + + @Override + protected TcpOutputStream createLogin() { + try { + RequestHeader header = new RequestHeader(); + header.setMsgType(MsgType.LOGIN); + + LoginRequest login = new LoginRequest(); + login.setProtocol(CoreConst.HIGHWAY); + + HighwayOutputStream os = new HighwayOutputStream(AbstractTcpClientPackage.getAndIncRequestId()); + os.write(header, LoginRequest.getRootSerializer(), login); + return os; + } catch (Throwable e) { + throw new Error("impossible.", e); + } + } + + @Override + protected boolean onLoginResponse(Buffer bodyBuffer) { + try { + LoginResponse.readObject(bodyBuffer); + return true; + } catch (Throwable e) { + LOGGER.error("decode login response failed.", e); + return false; + } + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientConnectionPool.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientConnectionPool.java new file mode 100644 index 00000000000..0e04e46d8c2 --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientConnectionPool.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.transport.highway; + +import org.apache.servicecomb.foundation.vertx.client.tcp.AbstractTcpClientConnectionPool; +import org.apache.servicecomb.foundation.vertx.client.tcp.NetClientWrapper; + +import io.vertx.core.Context; + +public class HighwayClientConnectionPool extends AbstractTcpClientConnectionPool { + public HighwayClientConnectionPool(Context context, NetClientWrapper netClientWrapper) { + super(context, netClientWrapper); + } + + @Override + protected HighwayClientConnection create(String endpoint) { + return new HighwayClientConnection(context, netClientWrapper, endpoint); + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientFilter.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientFilter.java new file mode 100644 index 00000000000..3d657bb9d53 --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientFilter.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.transport.highway; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; +import org.apache.servicecomb.codec.protobuf.definition.ProtobufManager; +import org.apache.servicecomb.core.CoreConst; +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.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.foundation.common.utils.AsyncUtils; +import org.apache.servicecomb.foundation.vertx.client.tcp.TcpData; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HighwayClientFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(HighwayClientFilter.class); + + public static final String NAME = "highway-client"; + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean enabledForTransport(String transport) { + return CoreConst.HIGHWAY.equals(transport); + } + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 2000; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + LOGGER.debug("Sending request by highway, operation={}, endpoint={}.", + invocation.getMicroserviceQualifiedName(), + invocation.getEndpoint().getEndpoint()); + + OperationProtobuf operationProtobuf = ProtobufManager.getOrCreateOperation(invocation); + return send(invocation, operationProtobuf) + .thenApply(tcpData -> convertToResponse(invocation, operationProtobuf, tcpData)) + .thenApply(this::convertFailedResponseToException); + } + + protected CompletableFuture send(Invocation invocation, OperationProtobuf operationProtobuf) { + invocation.getInvocationStageTrace().startConsumerConnection(); + HighwayClient highwayClient = ((HighwayTransport) invocation.getTransport()).getHighwayClient(); + HighwayClientPackage clientPackage = highwayClient.createClientPackage(invocation, operationProtobuf); + HighwayClientConnection clientConnection = highwayClient.findClientPool(invocation); + invocation.getInvocationStageTrace().finishConsumerConnection(); + invocation.onStartSendRequest(); + invocation.getInvocationStageTrace().startConsumerSendRequest(); + CompletableFuture sendFuture = clientConnection + .send(clientPackage).whenComplete((r, e) -> invocation.getInvocationStageTrace().finishWaitResponse()); + invocation.getInvocationStageTrace().finishConsumerSendRequest(); + invocation.getInvocationStageTrace().startWaitResponse(); + return invocation.optimizeSyncConsumerThread(sendFuture); + } + + protected Response convertToResponse(Invocation invocation, OperationProtobuf operationProtobuf, TcpData tcpData) { + try { + invocation.getInvocationStageTrace().startConsumerDecodeResponse(); + Response result = HighwayCodec.decodeResponse(invocation, operationProtobuf, tcpData); + invocation.getInvocationStageTrace().finishConsumerDecodeResponse(); + return result; + } catch (Exception e) { + throw AsyncUtils.rethrow(e); + } + } + + protected Response convertFailedResponseToException(Response response) { + if (response.isFailed()) { + Object errorData = response.getResult(); + if (errorData instanceof InvocationException) { + errorData = ((InvocationException) errorData).getErrorData(); + } + throw Exceptions.create(response.getStatus(), errorData); + } + + return response; + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientPackage.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientPackage.java new file mode 100644 index 00000000000..db1c58dd14f --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientPackage.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.transport.highway; + +import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.vertx.client.tcp.AbstractTcpClientPackage; +import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HighwayClientPackage extends AbstractTcpClientPackage { + private static final Logger LOGGER = LoggerFactory.getLogger(HighwayClientPackage.class); + + private final Invocation invocation; + + private final OperationProtobuf operationProtobuf; + + public HighwayClientPackage(Invocation invocation, OperationProtobuf operationProtobuf, long msRequestTimeout) { + this.invocation = invocation; + this.operationProtobuf = operationProtobuf; + this.setMsRequestTimeout(msRequestTimeout); + } + + @Override + public TcpOutputStream createStream() { + try { + return HighwayCodec.encodeRequest(msgId, invocation, operationProtobuf); + } catch (Exception e) { + String msg = String.format("encode request failed. appid=%s, qualifiedName=%s", + invocation.getAppId(), + invocation.getOperationMeta().getMicroserviceQualifiedName()); + LOGGER.error(msg, e); + throw new Error(msg, e); + } + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientPoolFactory.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientPoolFactory.java new file mode 100644 index 00000000000..2f1c70f111b --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientPoolFactory.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.transport.highway; + +import org.apache.servicecomb.foundation.vertx.client.tcp.AbstractTcpClientPoolFactory; +import org.apache.servicecomb.foundation.vertx.client.tcp.NetClientWrapper; +import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; + +import io.vertx.core.Context; + +public class HighwayClientPoolFactory extends AbstractTcpClientPoolFactory { + public HighwayClientPoolFactory(TcpClientConfig normalClientConfig, TcpClientConfig sslClientConfig) { + super(normalClientConfig, sslClientConfig); + } + + @Override + protected HighwayClientConnectionPool doCreateClientPool(Context context, NetClientWrapper netClientWrapper) { + return new HighwayClientConnectionPool(context, netClientWrapper); + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayCodec.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayCodec.java new file mode 100644 index 00000000000..ca2f24e0a8e --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayCodec.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.transport.highway; + +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; +import org.apache.servicecomb.codec.protobuf.definition.RequestRootDeserializer; +import org.apache.servicecomb.codec.protobuf.definition.ResponseRootDeserializer; +import org.apache.servicecomb.codec.protobuf.definition.ResponseRootSerializer; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.vertx.client.tcp.TcpData; +import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; +import org.apache.servicecomb.swagger.generator.SwaggerConst; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.transport.highway.message.RequestHeader; +import org.apache.servicecomb.transport.highway.message.ResponseHeader; + +import com.fasterxml.jackson.databind.JavaType; +import com.google.common.base.Defaults; + +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; +import io.vertx.core.buffer.Buffer; + +@SuppressWarnings("rawtypes") +public final class HighwayCodec { + private HighwayCodec() { + } + + public static TcpOutputStream encodeRequest(long msgId, Invocation invocation, + OperationProtobuf operationProtobuf) throws Exception { + // 写header + RequestHeader header = new RequestHeader(); + header.setMsgType(MsgType.REQUEST); + header.setFlags(0); + header.setDestMicroservice(invocation.getMicroserviceName()); + header.setSchemaId(invocation.getSchemaId()); + header.setOperationName(invocation.getOperationName()); + header.setContext(invocation.getContext()); + + HighwayOutputStream os = new HighwayOutputStream(msgId); + os.write(header, operationProtobuf.getRequestRootSerializer(), invocation.getSwaggerArguments()); + return os; + } + + // Proto buffer never serialize default values, put it back in provider. + // Or will get IllegalArgumentsException for primitive types. + private static Map addPrimitiveTypeDefaultValues(Invocation invocation, + Map swaggerArguments) { + if (invocation.getOperationMeta().getSwaggerProducerOperation() != null && !invocation.isEdge()) { + List swaggerParameters = invocation.getOperationMeta().getSwaggerOperation() + .getParameters(); + if (swaggerParameters != null) { + for (Parameter parameter : swaggerParameters) { + if (swaggerArguments.get(parameter.getName()) == null) { + Type type = invocation.getOperationMeta().getSwaggerProducerOperation() + .getSwaggerParameterType(parameter.getName()); + swaggerArguments.put(parameter.getName(), defaultPrimitiveValue(null, type)); + } + } + } + + RequestBody requestBody = invocation.getOperationMeta().getSwaggerOperation().getRequestBody(); + if (requestBody != null && requestBody.getContent() != null + && requestBody.getContent().get(SwaggerConst.FORM_MEDIA_TYPE) != null + && requestBody.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema() != null + && requestBody.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties() != null) { + for (Object entry : + requestBody.getContent().get(SwaggerConst.FORM_MEDIA_TYPE).getSchema().getProperties().entrySet()) { + Entry types = (Entry) entry; + if (swaggerArguments.get(types.getKey()) == null) { + Type type = invocation.getOperationMeta().getSwaggerProducerOperation() + .getSwaggerParameterType(types.getKey()); + swaggerArguments.put(types.getKey(), defaultPrimitiveValue(null, type)); + } + } + } + } + return swaggerArguments; + } + + public static void decodeRequest(Invocation invocation, RequestHeader header, OperationProtobuf operationProtobuf, + Buffer bodyBuffer) throws Exception { + RequestRootDeserializer requestDeserializer = operationProtobuf.getRequestRootDeserializer(); + Map swaggerArguments = requestDeserializer.deserialize(bodyBuffer.getBytes()); + addPrimitiveTypeDefaultValues(invocation, swaggerArguments); + invocation.setSwaggerArguments(swaggerArguments); + } + + public static RequestHeader readRequestHeader(Buffer headerBuffer) throws Exception { + return RequestHeader.readObject(headerBuffer); + } + + public static Buffer encodeResponse(long msgId, ResponseHeader header, ResponseRootSerializer bodySchema, + Object body) throws Exception { + try (HighwayOutputStream os = new HighwayOutputStream(msgId)) { + os.write(header, bodySchema, body); + return os.getBuffer(); + } + } + + public static Response decodeResponse(Invocation invocation, OperationProtobuf operationProtobuf, TcpData tcpData) + throws Exception { + ResponseHeader header = ResponseHeader.readObject(tcpData.getHeaderBuffer()); + if (header.getContext() != null) { + invocation.getContext().putAll(header.getContext()); + } + + ResponseRootDeserializer bodySchema = operationProtobuf + .findResponseRootDeserializer(header.getStatusCode()); + JavaType type = invocation.findResponseType(header.getStatusCode()); + Object body = bodySchema + .deserialize(tcpData.getBodyBuffer().getBytes(), type); + + Response response = Response.create(header.getStatusCode(), header.getReasonPhrase() + , defaultPrimitiveValue(body, type)); + response.setHeaders(header.toMultiMap()); + + return response; + } + + private static Object defaultPrimitiveValue(Object body, Type type) { + if (body == null) { + if (type instanceof Class && ((Class) type).isPrimitive()) { + return Defaults.defaultValue((Class) type); + } + if (type instanceof JavaType && ((JavaType) type).isPrimitive()) { + return Defaults.defaultValue(((JavaType) type).getRawClass()); + } + } + return body; + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayConfig.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayConfig.java new file mode 100644 index 00000000000..4ddf9c47f11 --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayConfig.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.transport.highway; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.transport.common.TransportConfigUtils; + +public final class HighwayConfig { + private HighwayConfig() { + } + + public static String getAddress() { + return LegacyPropertyFactory.getStringProperty("servicecomb.highway.address", null); + } + + public static int getServerThreadCount() { + return TransportConfigUtils.readVerticleCount( + "servicecomb.highway.server.verticle-count", + "servicecomb.highway.server.thread-count"); + } + + public static int getClientThreadCount() { + return TransportConfigUtils.readVerticleCount( + "servicecomb.highway.client.verticle-count", + "servicecomb.highway.client.thread-count"); + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayOutputStream.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayOutputStream.java new file mode 100644 index 00000000000..b0eb3e8158e --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayOutputStream.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.transport.highway; + +import org.apache.servicecomb.codec.protobuf.definition.RequestRootSerializer; +import org.apache.servicecomb.codec.protobuf.definition.ResponseRootSerializer; +import org.apache.servicecomb.foundation.protobuf.RootSerializer; +import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; +import org.apache.servicecomb.transport.highway.message.RequestHeader; +import org.apache.servicecomb.transport.highway.message.ResponseHeader; + +public class HighwayOutputStream extends TcpOutputStream { + public HighwayOutputStream(long msgId) { + super(msgId); + } + + public void write(RequestHeader header, RequestRootSerializer requestRootSerializer, Object body) throws Exception { + write(RequestHeader.getRootSerializer().serialize(header), requestRootSerializer.serialize(body)); + } + + public void write(ResponseHeader header, ResponseRootSerializer responseRootSerializer, Object body) + throws Exception { + write(ResponseHeader.getRootSerializer().serialize(header), responseRootSerializer.serialize(body)); + } + + public void write(RequestHeader header, RootSerializer bodySerializer, Object body) throws Exception { + write(RequestHeader.getRootSerializer(), header, bodySerializer, body); + } + + public void write(ResponseHeader header, RootSerializer bodySerializer, Object body) throws Exception { + write(ResponseHeader.getRootSerializer(), header, bodySerializer, body); + } + + public void write(RootSerializer headerSerializer, Object header, RootSerializer bodySerializer, Object body) + throws Exception { + byte[] headerBytes = new byte[0]; + byte[] bodyBytes = new byte[0]; + + if (headerSerializer != null) { + headerBytes = headerSerializer.serialize(header); + } + + if (bodySerializer != null) { + bodyBytes = bodySerializer.serialize(body); + } + + write(headerBytes, bodyBytes); + } + + private void write(byte[] headerBytes, byte[] bodyBytes) + throws Exception { + int headerLength = 0; + int totalLength = 0; + + if (headerBytes != null) { + headerLength = headerBytes.length; + totalLength = totalLength + headerLength; + } + + if (bodyBytes != null) { + totalLength = totalLength + bodyBytes.length; + } + + this.writeLength(totalLength, headerLength); + this.write(headerBytes); + this.write(bodyBytes); + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayProducerInvocationFlow.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayProducerInvocationFlow.java new file mode 100644 index 00000000000..3f38fd760d3 --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayProducerInvocationFlow.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.transport.highway; + +import org.apache.servicecomb.core.Invocation; +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.ExceptionUtils; +import org.apache.servicecomb.foundation.vertx.tcp.TcpConnection; +import org.apache.servicecomb.swagger.invocation.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HighwayProducerInvocationFlow extends ProducerInvocationFlow { + private static final Logger LOGGER = LoggerFactory.getLogger(HighwayProducerInvocationFlow.class); + + private final TcpConnection connection; + + private final long msgId; + + public HighwayProducerInvocationFlow(InvocationCreator invocationCreator, TcpConnection connection, long msgId) { + super(invocationCreator); + this.connection = connection; + this.msgId = msgId; + } + + @Override + protected Invocation sendCreateInvocationException(Throwable throwable) { + logException(throwable); + return null; + } + + private void logException(Throwable throwable) { + if (Exceptions.isPrintInvocationStackTrace()) { + LOGGER.error("Failed to prepare invocation, msgId={}.", msgId, throwable); + return; + } + + LOGGER.error("Failed to prepare invocation, msgId={}, message={}.", msgId, + ExceptionUtils.getExceptionMessageWithoutTrace(throwable)); + } + + @Override + protected void endResponse(Invocation invocation, Response response) { + HighwayTransportContext transportContext = invocation.getTransportContext(); + connection.write(transportContext.getResponseBuffer()); + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServer.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServer.java new file mode 100644 index 00000000000..2a76d0b8919 --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServer.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.transport.highway; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.foundation.vertx.server.TcpServer; +import org.apache.servicecomb.foundation.vertx.server.TcpServerConnection; + +public class HighwayServer extends TcpServer { + private final Endpoint endpoint; + + public HighwayServer(Endpoint endpoint) { + super((URIEndpointObject) endpoint.getAddress()); + this.endpoint = endpoint; + } + + @Override + protected TcpServerConnection createTcpServerConnection() { + return new HighwayServerConnection(endpoint); + } + + @Override + protected int getConnectionLimit() { + return LegacyPropertyFactory.getIntProperty("servicecomb.highway.server.connection-limit", Integer.MAX_VALUE); + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilter.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilter.java new file mode 100644 index 00000000000..1f165f9227c --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilter.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.transport.highway; + +import static org.apache.servicecomb.core.exception.Exceptions.toProducerResponse; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; +import org.apache.servicecomb.codec.protobuf.definition.ResponseRootSerializer; +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.ProviderFilter; +import org.apache.servicecomb.foundation.common.utils.AsyncUtils; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.apache.servicecomb.transport.highway.message.ResponseHeader; + +import io.vertx.core.buffer.Buffer; + +public class HighwayServerCodecFilter extends AbstractFilter implements ProviderFilter { + public static final String NAME = "highway-server-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.HIGHWAY.equals(transport); + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + return CompletableFuture.completedFuture(invocation) + .thenCompose(this::decodeRequest) + .thenCompose(nextNode::onFilter) + .exceptionally(exception -> toProducerResponse(invocation, exception)) + .thenCompose(response -> encodeResponse(invocation, response)); + } + + protected CompletableFuture decodeRequest(Invocation invocation) { + HighwayTransportContext transportContext = invocation.getTransportContext(); + try { + invocation.getInvocationStageTrace().startProviderDecodeRequest(); + HighwayCodec.decodeRequest(invocation, + transportContext.getHeader(), + transportContext.getOperationProtobuf(), + transportContext.getBodyBuffer()); + invocation.getInvocationStageTrace().finishProviderDecodeRequest(); + return CompletableFuture.completedFuture(invocation); + } catch (Exception e) { + return AsyncUtils.completeExceptionally(e); + } + } + + protected CompletableFuture encodeResponse(Invocation invocation, Response response) { + invocation.onEncodeResponseStart(response); + + ResponseHeader header = new ResponseHeader(); + header.setStatusCode(response.getStatusCode()); + header.setReasonPhrase(response.getReasonPhrase()); + header.setContext(invocation.getContext()); + header.fromMultiMap(response.getHeaders()); + + HighwayTransportContext transportContext = invocation.getTransportContext(); + long msgId = transportContext.getMsgId(); + OperationProtobuf operationProtobuf = transportContext.getOperationProtobuf(); + ResponseRootSerializer bodySchema = operationProtobuf.findResponseRootSerializer(response.getStatusCode()); + + try { + boolean failed = response.getResult() instanceof InvocationException; + Buffer respBuffer; + if (failed) { + respBuffer = HighwayCodec.encodeResponse( + msgId, header, bodySchema, ((InvocationException) response.getResult()).getErrorData()); + } else { + respBuffer = HighwayCodec.encodeResponse( + msgId, header, bodySchema, response.getResult()); + } + transportContext.setResponseBuffer(respBuffer); + + invocation.onEncodeResponseFinish(); + return CompletableFuture.completedFuture(response); + } catch (Exception e) { + // keep highway performance and simple, this encoding/decoding error not need handle by client + String msg = String.format("encode response failed, msgId=%d", msgId); + return AsyncUtils.completeExceptionally(new IllegalStateException(msg, e)); + } + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerConnection.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerConnection.java new file mode 100644 index 00000000000..b1c97704c5e --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerConnection.java @@ -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. + */ +package org.apache.servicecomb.transport.highway; + +import java.util.concurrent.CompletableFuture; + +import jakarta.ws.rs.core.Response.Status; + +import org.apache.servicecomb.codec.protobuf.definition.ProtobufManager; +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.MicroserviceMeta; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.definition.SchemaMeta; +import org.apache.servicecomb.core.invocation.InvocationCreator; +import org.apache.servicecomb.core.invocation.InvocationFactory; +import org.apache.servicecomb.foundation.vertx.server.TcpBufferHandler; +import org.apache.servicecomb.foundation.vertx.server.TcpParser; +import org.apache.servicecomb.foundation.vertx.server.TcpServerConnection; +import org.apache.servicecomb.transport.highway.message.LoginRequest; +import org.apache.servicecomb.transport.highway.message.LoginResponse; +import org.apache.servicecomb.transport.highway.message.RequestHeader; +import org.apache.servicecomb.transport.highway.message.ResponseHeader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.buffer.Buffer; +import io.vertx.core.net.NetSocket; + +public class HighwayServerConnection extends TcpServerConnection implements TcpBufferHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(HighwayServerConnection.class); + + private final Endpoint endpoint; + + public HighwayServerConnection(Endpoint endpoint) { + this.endpoint = endpoint; + } + + @Override + public void init(NetSocket netSocket) { + splitter = new TcpParser(this); + super.init(netSocket); + } + + @Override + public void handle(long msgId, Buffer headerBuffer, Buffer bodyBuffer) { + RequestHeader requestHeader = decodeRequestHeader(msgId, headerBuffer); + if (requestHeader == null) { + return; + } + + switch (requestHeader.getMsgType()) { + case MsgType.REQUEST: + onRequest(msgId, requestHeader, bodyBuffer); + break; + case MsgType.LOGIN: + onLogin(msgId, requestHeader, bodyBuffer); + break; + + default: + throw new Error("Unknown tcp msgType " + requestHeader.getMsgType()); + } + } + + protected RequestHeader decodeRequestHeader(long msgId, Buffer headerBuffer) { + try { + return HighwayCodec.readRequestHeader(headerBuffer); + } catch (Exception e) { + String msg = String.format("decode request header error, msgId=%d", + msgId); + LOGGER.error(msg, e); + + netSocket.close(); + return null; + } + } + + protected void onLogin(long msgId, RequestHeader header, Buffer bodyBuffer) { + LoginRequest request; + try { + request = LoginRequest.readObject(bodyBuffer); + } catch (Exception e) { + String msg = String.format("decode setParameter error, msgId=%d", + msgId); + LOGGER.error(msg, e); + netSocket.close(); + return; + } + + if (request != null) { + this.setProtocol(request.getProtocol()); + this.setZipName(request.getZipName()); + } + + try (HighwayOutputStream os = new HighwayOutputStream(msgId)) { + ResponseHeader responseHeader = new ResponseHeader(); + responseHeader.setStatusCode(Status.OK.getStatusCode()); + + LoginResponse response = new LoginResponse(); + + os.write(ResponseHeader.getRootSerializer(), + responseHeader, + LoginResponse.getRootSerializer(), + response); + netSocket.write(os.getBuffer()); + } catch (Exception e) { + throw new Error("impossible.", e); + } + } + + protected void onRequest(long msgId, RequestHeader header, Buffer bodyBuffer) { + InvocationCreator creator = () -> createInvocation(msgId, header, bodyBuffer); + new HighwayProducerInvocationFlow(creator, this, msgId) + .run(); + } + + public CompletableFuture createInvocation(long msgId, RequestHeader header, Buffer bodyBuffer) { + MicroserviceMeta microserviceMeta = SCBEngine.getInstance().getProducerMicroserviceMeta(); + SchemaMeta schemaMeta = microserviceMeta.ensureFindSchemaMeta(header.getSchemaId()); + OperationMeta operationMeta = schemaMeta.ensureFindOperation(header.getOperationName()); + + Invocation invocation = InvocationFactory.forProvider(endpoint, + operationMeta, + null); + invocation.getHandlerContext().put(CoreConst.REMOTE_ADDRESS, netSocket.remoteAddress()); + + HighwayTransportContext transportContext = new HighwayTransportContext() + .setConnection(this) + .setMsgId(msgId) + .setHeader(header) + .setBodyBuffer(bodyBuffer) + .setOperationProtobuf(ProtobufManager.getOrCreateOperation(invocation)); + invocation.setTransportContext(transportContext); + + invocation.mergeContext(header.getContext()); + + return CompletableFuture.completedFuture(invocation); + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerVerticle.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerVerticle.java new file mode 100644 index 00000000000..9b89ae32f2d --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerVerticle.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.highway; + +import java.net.InetSocketAddress; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.transport.AbstractTransport; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Context; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; + +public class HighwayServerVerticle extends AbstractVerticle { + private static final Logger LOGGER = LoggerFactory.getLogger(HighwayServerVerticle.class); + + public static final String SSL_KEY = "highway.provider"; + + private Endpoint endpoint; + + private URIEndpointObject endpointObject; + + @Override + public void init(Vertx vertx, Context context) { + super.init(vertx, context); + this.endpoint = (Endpoint) context.config().getValue(AbstractTransport.ENDPOINT_KEY); + this.endpointObject = (URIEndpointObject) this.endpoint.getAddress(); + } + + @Override + public void start(Promise startPromise) throws Exception { + try { + super.start(); + startListen(startPromise); + } catch (Throwable e) { + // vert.x got some states that not print error and execute call back in VertexUtils.blockDeploy, we add a log our self. + LOGGER.error("", e); + throw e; + } + } + + protected void startListen(Promise startPromise) { + // if listen address is not provided, do not fail and maybe a consumer service. + if (endpointObject == null) { + LOGGER.warn("highway listen address is not configured, will not listen."); + startPromise.complete(); + return; + } + + HighwayServer server = new HighwayServer(endpoint); + server.init(vertx, SSL_KEY, ar -> { + if (ar.succeeded()) { + InetSocketAddress socketAddress = ar.result(); + LOGGER.info("highway listen success. address={}:{}", + socketAddress.getHostString(), + socketAddress.getPort()); + startPromise.complete(); + return; + } + + LOGGER.error(CoreConst.HIGHWAY, ar.cause()); + startPromise.fail(ar.cause()); + }); + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayTransport.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayTransport.java new file mode 100644 index 00000000000..2d5d4fa5683 --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayTransport.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.transport.highway; + +import java.util.Collections; +import java.util.Map; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.transport.AbstractTransport; +import org.apache.servicecomb.foundation.vertx.SimpleJsonObject; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.apache.servicecomb.foundation.vertx.tcp.TcpConst; + +import io.vertx.core.DeploymentOptions; + +public class HighwayTransport extends AbstractTransport { + private final HighwayClient highwayClient = new HighwayClient(); + + @Override + public String getName() { + return CoreConst.HIGHWAY; + } + + @Override + public boolean init() throws Exception { + highwayClient.init(transportVertx); + + DeploymentOptions deployOptions = new DeploymentOptions().setInstances(HighwayConfig.getServerThreadCount()); + setListenAddressWithoutSchema(HighwayConfig.getAddress(), Collections.singletonMap(TcpConst.LOGIN, "true")); + SimpleJsonObject json = new SimpleJsonObject(); + json.put(ENDPOINT_KEY, getEndpoint()); + deployOptions.setConfig(json); + deployOptions.setWorkerPoolName("pool-worker-transport-highway"); + Map result = VertxUtils.blockDeploy(transportVertx, HighwayServerVerticle.class, deployOptions); + if ((boolean) result.get("code")) { + return true; + } else { + throw new IllegalStateException((String) result.get("message")); + } + } + + public HighwayClient getHighwayClient() { + return highwayClient; + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayTransportContext.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayTransportContext.java new file mode 100644 index 00000000000..bf4b699d35d --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayTransportContext.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.transport.highway; + +import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; +import org.apache.servicecomb.foundation.vertx.tcp.TcpConnection; +import org.apache.servicecomb.swagger.invocation.context.VertxTransportContext; +import org.apache.servicecomb.transport.highway.message.RequestHeader; + +import io.vertx.core.Context; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; + +public class HighwayTransportContext implements VertxTransportContext { + private final Context vertxContext; + + private TcpConnection connection; + + private long msgId; + + private RequestHeader header; + + private Buffer bodyBuffer; + + private OperationProtobuf operationProtobuf; + + private Buffer responseBuffer; + + public HighwayTransportContext() { + this.vertxContext = Vertx.currentContext(); + } + + @Override + public Context getVertxContext() { + return vertxContext; + } + + public TcpConnection getConnection() { + return connection; + } + + public HighwayTransportContext setConnection(TcpConnection connection) { + this.connection = connection; + return this; + } + + public long getMsgId() { + return msgId; + } + + public HighwayTransportContext setMsgId(long msgId) { + this.msgId = msgId; + return this; + } + + public RequestHeader getHeader() { + return header; + } + + public HighwayTransportContext setHeader(RequestHeader header) { + this.header = header; + return this; + } + + public Buffer getBodyBuffer() { + return bodyBuffer; + } + + public HighwayTransportContext setBodyBuffer(Buffer bodyBuffer) { + this.bodyBuffer = bodyBuffer; + return this; + } + + public OperationProtobuf getOperationProtobuf() { + return operationProtobuf; + } + + public HighwayTransportContext setOperationProtobuf( + OperationProtobuf operationProtobuf) { + this.operationProtobuf = operationProtobuf; + return this; + } + + public Buffer getResponseBuffer() { + return responseBuffer; + } + + public HighwayTransportContext setResponseBuffer(Buffer responseBuffer) { + this.responseBuffer = responseBuffer; + return this; + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/MsgType.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/MsgType.java new file mode 100644 index 00000000000..cd2f1a99ed3 --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/MsgType.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.transport.highway; + +public final class MsgType { + public static final byte REQUEST = 0; + + public static final byte LOGIN = 1; + + private MsgType() { + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/TransportHighwayConfiguration.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/TransportHighwayConfiguration.java new file mode 100644 index 00000000000..b029d643814 --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/TransportHighwayConfiguration.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.transport.highway; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TransportHighwayConfiguration { + @Bean + public HighwayServerCodecFilter highwayServerCodecFilter() { + return new HighwayServerCodecFilter(); + } + + @Bean + public HighwayClientFilter highwayClientFilter() { + return new HighwayClientFilter(); + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/Headers.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/Headers.java new file mode 100644 index 00000000000..6dfe8c146de --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/Headers.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.transport.highway.message; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Headers { + private Map> headerMap; + + public Map> getHeaderMap() { + return headerMap; + } + + public void setHeaderMap(Map> headerMap) { + this.headerMap = headerMap; + } + + public Object getFirst(String name) { + if (headerMap == null) { + return null; + } + + List values = headerMap.get(name); + if (values == null || values.isEmpty()) { + return null; + } + + return values.get(0); + } + + public List getHeader(String name) { + if (headerMap == null) { + return null; + } + + return headerMap.get(name); + } + + public Headers addHeader(String name, Object value) { + if (headerMap == null) { + headerMap = new HashMap<>(); + } + + List values = headerMap.computeIfAbsent(name, k -> new ArrayList<>()); + values.add(value); + + return this; + } + + public Headers addHeader(String name, List value) { + if (headerMap == null) { + headerMap = new HashMap<>(); + } + + List values = headerMap.computeIfAbsent(name, k -> new ArrayList<>()); + values.addAll(value); + + return this; + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/LoginRequest.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/LoginRequest.java new file mode 100644 index 00000000000..6c678e52cf6 --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/LoginRequest.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.transport.highway.message; + +import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.RootSerializer; + +import io.vertx.core.buffer.Buffer; + +public class LoginRequest { + private static final ProtoMapperFactory protoMapperFactory = new ProtoMapperFactory(); + + private static final RootDeserializer rootDeserializer = protoMapperFactory + .createFromName("LoginRequest.proto") + .createRootDeserializer("LoginRequest", LoginRequest.class); + + private static final RootSerializer rootSerializer = protoMapperFactory.createFromName("LoginRequest.proto") + .createRootSerializer("LoginRequest", LoginRequest.class); + + public static RootSerializer getRootSerializer() { + return rootSerializer; + } + + public static LoginRequest readObject(Buffer bodyBuffer) throws Exception { + return rootDeserializer.deserialize(bodyBuffer.getBytes()); + } + + private String protocol; + + // 压缩算法名字 + private String zipName; + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getZipName() { + return zipName; + } + + public void setZipName(String zipName) { + this.zipName = zipName; + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/LoginResponse.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/LoginResponse.java new file mode 100644 index 00000000000..d8cc0414463 --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/LoginResponse.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.transport.highway.message; + +import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.RootSerializer; + +import io.vertx.core.buffer.Buffer; + +public class LoginResponse { + private static final ProtoMapperFactory protoMapperFactory = new ProtoMapperFactory(); + + private static final RootDeserializer rootDeserializer = protoMapperFactory + .createFromName("LoginResponse.proto") + .createRootDeserializer("LoginResponse", LoginResponse.class); + + private static final RootSerializer rootSerializer = protoMapperFactory.createFromName("LoginResponse.proto") + .createRootSerializer("LoginResponse", LoginResponse.class); + + public static RootSerializer getRootSerializer() { + return rootSerializer; + } + + public static LoginResponse readObject(Buffer bodyBuffer) throws Exception { + return rootDeserializer.deserialize(bodyBuffer.getBytes()); + } + + private String protocol; + + // 压缩算法名字 + private String zipName; + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getZipName() { + return zipName; + } + + public void setZipName(String zipName) { + this.zipName = zipName; + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/RequestHeader.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/RequestHeader.java new file mode 100644 index 00000000000..4009a7fb89c --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/RequestHeader.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.transport.highway.message; + +import java.util.Map; + +import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.RootSerializer; + +import io.vertx.core.buffer.Buffer; + +public class RequestHeader { + private static final ProtoMapperFactory protoMapperFactory = new ProtoMapperFactory(); + + private static final RootDeserializer rootDeserializer = protoMapperFactory + .createFromName("RequestHeader.proto") + .createRootDeserializer("RequestHeader", RequestHeader.class); + + private static final RootSerializer rootSerializer = protoMapperFactory.createFromName("RequestHeader.proto") + .createRootSerializer("RequestHeader", RequestHeader.class); + + public static RootSerializer getRootSerializer() { + return rootSerializer; + } + + public static RequestHeader readObject(Buffer bodyBuffer) throws Exception { + return rootDeserializer.deserialize(bodyBuffer.getBytes()); + } + + private int msgType; + + // 运行时必须的数据,比如body是否压缩 + // 预留特性选项 + private int flags; + + private String destMicroservice; + + private String schemaId; + + private String operationName; + + private Map context; + + public int getMsgType() { + return msgType; + } + + public void setMsgType(int msgType) { + this.msgType = msgType; + } + + public String getDestMicroservice() { + return destMicroservice; + } + + public void setDestMicroservice(String destMicroservice) { + this.destMicroservice = destMicroservice; + } + + public int getFlags() { + return flags; + } + + public void setFlags(int flags) { + this.flags = flags; + } + + public String getSchemaId() { + return schemaId; + } + + public void setSchemaId(String schemaId) { + this.schemaId = schemaId; + } + + public String getOperationName() { + return operationName; + } + + public void setOperationName(String operationName) { + this.operationName = operationName; + } + + public Map getContext() { + return context; + } + + public void setContext(Map context) { + this.context = context; + } +} diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/ResponseHeader.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/ResponseHeader.java new file mode 100644 index 00000000000..2a50efd0365 --- /dev/null +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/message/ResponseHeader.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.highway.message; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.servicecomb.foundation.protobuf.ProtoMapperFactory; +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.RootSerializer; + +import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; + +public class ResponseHeader { + private static final ProtoMapperFactory protoMapperFactory = new ProtoMapperFactory(); + + private static final RootDeserializer rootDeserializer = protoMapperFactory + .createFromName("ResponseHeader.proto") + .createRootDeserializer("ResponseHeader", ResponseHeader.class); + + private static final RootSerializer rootSerializer = protoMapperFactory.createFromName("ResponseHeader.proto") + .createRootSerializer("ResponseHeader", ResponseHeader.class); + + public static RootSerializer getRootSerializer() { + return rootSerializer; + } + + public static ResponseHeader readObject(Buffer bodyBuffer) throws Exception { + return rootDeserializer.deserialize(bodyBuffer.getBytes()); + } + + // 运行时必须的数据,比如body是否压缩 + // 预留特性选项 + private int flags; + + private int statusCode; + + private String reasonPhrase; + + private Map context; + + private Headers headers = new Headers(); + + public int getFlags() { + return flags; + } + + public void setFlags(int flags) { + this.flags = flags; + } + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + + public String getReasonPhrase() { + return reasonPhrase; + } + + public void setReasonPhrase(String reason) { + this.reasonPhrase = reason; + } + + public Map getContext() { + return context; + } + + public void setContext(Map context) { + this.context = context; + } + + public Headers getHeaders() { + return headers; + } + + public void setHeaders(Headers headers) { + this.headers = headers; + } + + public void fromMultiMap(MultiMap multiMap) { + if (multiMap == null) { + return; + } + + for (Entry entry : multiMap.entries()) { + headers.addHeader(entry.getKey(), entry.getValue()); + } + } + + public MultiMap toMultiMap() { + MultiMap multiMap = MultiMap.caseInsensitiveMultiMap(); + Map> headerMap = headers.getHeaderMap(); + if (headerMap == null) { + return multiMap; + } + + for (Entry> entry : headerMap.entrySet()) { + String key = entry.getKey(); + for (Object value : entry.getValue()) { + multiMap.add(key, value.toString()); + } + } + return multiMap; + } +} diff --git a/transports/transport-highway/src/main/resources/LoginRequest.proto b/transports/transport-highway/src/main/resources/LoginRequest.proto new file mode 100644 index 00000000000..cc402fb06bb --- /dev/null +++ b/transports/transport-highway/src/main/resources/LoginRequest.proto @@ -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. + */ + +syntax = "proto3"; +package org.apache.servicecomb.transport.highway.message; + +message LoginRequest { + string protocol = 1; + string zipName = 2; +} diff --git a/transports/transport-highway/src/main/resources/LoginResponse.proto b/transports/transport-highway/src/main/resources/LoginResponse.proto new file mode 100644 index 00000000000..2931e792ceb --- /dev/null +++ b/transports/transport-highway/src/main/resources/LoginResponse.proto @@ -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. + */ + +syntax = "proto3"; +package org.apache.servicecomb.transport.highway.message; + +message LoginResponse { + string protocol = 1; + string zipName = 2; +} diff --git a/transports/transport-highway/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport b/transports/transport-highway/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport new file mode 100644 index 00000000000..25f1bb25976 --- /dev/null +++ b/transports/transport-highway/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport @@ -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.transport.highway.HighwayTransport diff --git a/transports/transport-highway/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/transports/transport-highway/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..46a50fbf311 --- /dev/null +++ b/transports/transport-highway/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.transport.highway.TransportHighwayConfiguration diff --git a/transports/transport-highway/src/main/resources/RequestHeader.proto b/transports/transport-highway/src/main/resources/RequestHeader.proto new file mode 100644 index 00000000000..1ccf235f842 --- /dev/null +++ b/transports/transport-highway/src/main/resources/RequestHeader.proto @@ -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. + */ + +syntax = "proto3"; +package org.apache.servicecomb.transport.highway.message; + +message RequestHeader { + string destMicroservice = 1; + int32 msgType = 2; + int32 flags = 3; + string schemaId = 4; + string operationName = 5; + map context = 6; +} diff --git a/transports/transport-highway/src/main/resources/ResponseHeader.proto b/transports/transport-highway/src/main/resources/ResponseHeader.proto new file mode 100644 index 00000000000..c92bf71e94a --- /dev/null +++ b/transports/transport-highway/src/main/resources/ResponseHeader.proto @@ -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. + */ + +syntax = "proto3"; +import "google/protobuf/any.proto"; +package org.apache.servicecomb.transport.highway.message; + +message ResponseHeader { + int32 flags = 1; + int32 statusCode = 2; + string reasonPhrase = 3; + map context = 4; + Headers headers = 5; +} + +message Headers { + map headerMap = 1; +} + +//@WrapProperty +message ListGoogle_protobuf_Any { + repeated google.protobuf.Any value = 1; +} diff --git a/transports/transport-highway/src/main/resources/microservice.yaml b/transports/transport-highway/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..4b37228f3f6 --- /dev/null +++ b/transports/transport-highway/src/main/resources/microservice.yaml @@ -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-config-order: -500 +servicecomb: + filter-chains: + transport: + scb-consumer-transport: + highway: highway-client + scb-producer-transport: + highway: highway-server-codec diff --git a/transports/transport-highway/src/test/java/io/servicecomb/transport/common/MockUtil.java b/transports/transport-highway/src/test/java/io/servicecomb/transport/common/MockUtil.java deleted file mode 100644 index 0797c4dc25a..00000000000 --- a/transports/transport-highway/src/test/java/io/servicecomb/transport/common/MockUtil.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.common; - -import org.mockito.Mockito; - -import io.protostuff.runtime.ProtobufFeature; -import io.servicecomb.codec.protobuf.definition.OperationProtobuf; -import io.servicecomb.codec.protobuf.definition.ProtobufManager; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.transport.highway.HighwayCodec; -import io.servicecomb.transport.highway.HighwayConfig; -import io.servicecomb.transport.highway.message.RequestHeader; -import io.vertx.core.buffer.Buffer; -import mockit.Mock; -import mockit.MockUp; - -public class MockUtil { - - private static MockUtil instance = new MockUtil(); - - private MockUtil() { - - } - - public static MockUtil getInstance() { - return instance; - } - - public void mockHighwayConfig() { - - new MockUp() { - @Mock - String getAddress() { - return "127.0.0.1"; - } - }; - } - - public RequestHeader requestHeader = new RequestHeader(); - - public boolean decodeRequestSucc = true; - - public void mockHighwayCodec() { - - new MockUp() { - @Mock - RequestHeader readRequestHeader(Buffer headerBuffer) throws Exception { - return requestHeader; - } - - @Mock - public Invocation decodeRequest(RequestHeader header, OperationProtobuf operationProtobuf, - Buffer bodyBuffer, ProtobufFeature protobufFeature) throws Exception { - if (decodeRequestSucc) { - return Mockito.mock(Invocation.class); - } - - throw new Error("decode failed"); - } - }; - } - - public void mockProtobufManager() { - - new MockUp() { - @Mock - public OperationProtobuf getOrCreateOperation(OperationMeta operationMeta) throws Exception { - return Mockito.mock(OperationProtobuf.class); - } - }; - } -} diff --git a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayClient.java b/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayClient.java deleted file mode 100644 index 5e364605a15..00000000000 --- a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayClient.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import java.util.concurrent.Executor; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.netty.buffer.ByteBuf; -import io.protostuff.runtime.ProtobufCompatibleUtils; -import io.servicecomb.codec.protobuf.definition.OperationProtobuf; -import io.servicecomb.codec.protobuf.definition.ProtobufManager; -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.foundation.vertx.VertxUtils; -import io.servicecomb.foundation.vertx.client.ClientPoolManager; -import io.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; -import io.servicecomb.foundation.vertx.server.TcpParser; -import io.servicecomb.foundation.vertx.tcp.TcpOutputStream; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.transport.highway.message.LoginRequest; -import io.servicecomb.transport.highway.message.RequestHeader; -import io.vertx.core.AbstractVerticle; -import io.vertx.core.DeploymentOptions; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestHighwayClient { - private TcpClientConfig options; - - HighwayClient client = new HighwayClient(true); - - Invocation invocation = Mockito.mock(Invocation.class); - - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - - OperationProtobuf operationProtobuf = Mockito.mock(OperationProtobuf.class); - - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - - Endpoint endpoint = Mockito.mock(Endpoint.class); - - Executor excutor = Mockito.mock(Executor.class); - - @Test - public void testHighwayClientSSL(@Mocked Vertx vertx) throws Exception { - new MockUp() { - @Mock - DeploymentOptions createClientDeployOptions( - ClientPoolManager clientMgr, - int instanceCount, - int poolCountPerVerticle, CLIENT_OPTIONS clientOptions) { - options = (TcpClientConfig) clientOptions; - return null; - } - - @Mock - boolean blockDeploy(Vertx vertx, - Class cls, - DeploymentOptions options) throws InterruptedException { - return true; - } - }; - - client.init(vertx); - Assert.assertEquals(options.isSsl(), true); - } - - @Test - public void testSend() { - boolean status = true; - mockUps(); - - Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - Mockito.when(invocation.getEndpoint()).thenReturn(endpoint); - Mockito.when(invocation.getEndpoint().getEndpoint()).thenReturn("endpoint"); - Mockito.when(invocation.getResponseExecutor()).thenReturn(excutor); - - try { - client.send(invocation, asyncResp); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testCreateLogin() throws Exception { - ProtobufCompatibleUtils.init(); - - HighwayClientConnection connection = - new HighwayClientConnection(null, null, "highway://127.0.0.1:7890", null); - TcpOutputStream os = connection.createLogin(); - ByteBuf buf = os.getBuffer().getByteBuf(); - - byte[] magic = new byte[TcpParser.TCP_MAGIC.length]; - buf.readBytes(magic); - Assert.assertArrayEquals(TcpParser.TCP_MAGIC, magic); - Assert.assertEquals(os.getMsgId(), buf.readLong()); - - int start = TcpParser.TCP_HEADER_LENGTH; - int totalLen = buf.readInt(); - int headerLen = buf.readInt(); - Buffer headerBuffer = - os.getBuffer().slice(start, start + headerLen); - int end = start + totalLen; - start += headerLen; - Buffer bodyBuffer = os.getBuffer().slice(start, end); - - RequestHeader header = RequestHeader.readObject(headerBuffer, connection.getProtobufFeature()); - Assert.assertEquals(MsgType.LOGIN, header.getMsgType()); - - LoginRequest login = LoginRequest.readObject(bodyBuffer); - Assert.assertEquals(HighwayTransport.NAME, login.getProtocol()); - } - - private void mockUps() { - - new MockUp>() { - - @Mock - public HighwayClientConnectionPool findThreadBindClientPool() { - return Mockito.mock(HighwayClientConnectionPool.class); - } - }; - - new MockUp() { - @Mock - public OperationProtobuf getOrCreateOperation(OperationMeta operationMeta) throws Exception { - return operationProtobuf; - } - }; - - new MockUp() { - @Mock - public Buffer encodeRequest(Invocation invocation, OperationProtobuf operationProtobuf, - long msgId) throws Exception { - return null; - } - }; - } -} diff --git a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayClientManager.java b/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayClientManager.java deleted file mode 100644 index dbd64143ae8..00000000000 --- a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayClientManager.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import org.junit.Assert; -import org.junit.Test; - -public class TestHighwayClientManager { - @Test - public void testRestTransportClientManager() { - HighwayClient client1 = HighwayClientManager.INSTANCE.getHighwayClient(false); - HighwayClient client2 = HighwayClientManager.INSTANCE.getHighwayClient(false); - Assert.assertEquals(client1, client2); - - HighwayClient client3 = HighwayClientManager.INSTANCE.getHighwayClient(true); - HighwayClient client4 = HighwayClientManager.INSTANCE.getHighwayClient(true); - Assert.assertEquals(client3, client4); - } -} diff --git a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayCodec.java b/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayCodec.java deleted file mode 100644 index 691abd0a477..00000000000 --- a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayCodec.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.Mockito; - -import io.netty.buffer.ByteBuf; -import io.protostuff.Input; -import io.protostuff.runtime.ProtobufCompatibleUtils; -import io.protostuff.runtime.ProtobufFeature; -import io.servicecomb.codec.protobuf.definition.OperationProtobuf; -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.servicecomb.codec.protobuf.utils.schema.NotWrapSchema; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.foundation.vertx.client.tcp.TcpData; -import io.servicecomb.foundation.vertx.server.TcpParser; -import io.servicecomb.foundation.vertx.tcp.TcpOutputStream; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.registry.ServiceRegistryFactory; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.transport.highway.message.RequestHeader; -import io.servicecomb.transport.highway.message.ResponseHeader; -import io.vertx.core.buffer.Buffer; -import mockit.Mock; -import mockit.MockUp; - -public class TestHighwayCodec { - - private RequestHeader header = null; - - private OperationProtobuf operationProtobuf = null; - - private Buffer bodyBuffer = null; - - private WrapSchema schema = null; - - private SchemaMeta schemaMeta = null; - - private OperationMeta operationMeta = null; - - private ByteBuf lByteBuf = null; - - private ByteBuffer nioBuffer = null; - - private Invocation invocation = null; - - @BeforeClass - public static void setupClass() { - ProtobufCompatibleUtils.init(); - HighwayCodec.setHighwayTransport(new HighwayTransport()); - } - - @Before - public void setUp() throws Exception { - ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal(); - serviceRegistry.init(); - RegistryUtils.setServiceRegistry(serviceRegistry); - - header = Mockito.mock(RequestHeader.class); - - operationProtobuf = Mockito.mock(OperationProtobuf.class); - - bodyBuffer = Mockito.mock(Buffer.class); - - schema = Mockito.mock(WrapSchema.class); - - schemaMeta = Mockito.mock(SchemaMeta.class); - - operationMeta = Mockito.mock(OperationMeta.class); - - lByteBuf = Mockito.mock(ByteBuf.class); - - nioBuffer = Mockito.mock(ByteBuffer.class); - - invocation = Mockito.mock(Invocation.class); - } - - @After - public void tearDown() throws Exception { - - header = null; - - operationProtobuf = null; - - bodyBuffer = null; - - schema = null; - - schemaMeta = null; - - operationMeta = null; - - lByteBuf = null; - - nioBuffer = null; - - invocation = null; - } - - @Test - public void testDecodeRequest() { - boolean status = true; - - try { - commonMock(); - Invocation inv = HighwayCodec.decodeRequest(header, operationProtobuf, bodyBuffer, null); - Assert.assertNotNull(inv); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testDecodeResponse() throws Exception { - Invocation invocation = Mockito.mock(Invocation.class); - Mockito.when(operationProtobuf.findResponseSchema(200)).thenReturn(Mockito.mock(WrapSchema.class)); - - Map context = new HashMap<>(); - Mockito.when(invocation.getContext()).thenReturn(context); - - TcpData tcpData = Mockito.mock(TcpData.class); - - Mockito.when(tcpData.getHeaderBuffer()).thenReturn(bodyBuffer); - commonMock(); - - ResponseHeader header = new ResponseHeader(); - header.setStatusCode(200); - header.setContext(new HashMap()); - header.getContext().put("a", "10"); - Buffer responseBuf = HighwayCodec.encodeResponse(0, header, null, null, new ProtobufFeature()); - - TcpData tcp = new TcpData(responseBuf.slice(23, responseBuf.length()), null); - Response response = HighwayCodec.decodeResponse(invocation, operationProtobuf, tcp, null); - Assert.assertEquals("10", invocation.getContext().get("a")); - Assert.assertEquals(200, response.getStatusCode()); - } - - @Test - public void testEncodeResponse() { - boolean status = true; - WrapSchema bodySchema = Mockito.mock(WrapSchema.class); - try { - commonMock(); - HighwayCodec.encodeResponse(23432142, null, bodySchema, new Object(), new ProtobufFeature()); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testEncodeRequest() { - boolean status = true; - try { - commonMock(); - TcpOutputStream os = HighwayCodec.encodeRequest(0, invocation, operationProtobuf, null); - Assert.assertNotNull(os); - Assert.assertArrayEquals(TcpParser.TCP_MAGIC, os.getBuffer().getBytes(0, 7)); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testReadRequestHeader() { - boolean status = true; - try { - new MockUp() { - @Mock - public Object readObject(Input input) throws IOException { - return new RequestHeader(); - } - }; - bodyBuffer = Buffer.buffer("\"abc\""); - RequestHeader requestHeader = HighwayCodec.readRequestHeader(bodyBuffer, null); - Assert.assertNotNull(requestHeader); - Assert.assertEquals(0, requestHeader.getFlags()); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - private void commonMock() { - Mockito.when(operationProtobuf.getRequestSchema()).thenReturn(schema); - Mockito.when(bodyBuffer.getByteBuf()).thenReturn(lByteBuf); - Mockito.when(lByteBuf.nioBuffer()).thenReturn(nioBuffer); - Mockito.when(operationProtobuf.getOperationMeta()).thenReturn(operationMeta); - Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); - } -} diff --git a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayConfig.java b/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayConfig.java deleted file mode 100644 index dc556056227..00000000000 --- a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayConfig.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import org.junit.Assert; -import org.junit.Test; - -public class TestHighwayConfig { - @Test - public void testGetThreadCount() { - Assert.assertEquals(HighwayConfig.getServerThreadCount(), 1); - Assert.assertEquals(HighwayConfig.getClientThreadCount(), 1); - } - - @Test - public void testGetAddress() { - Assert.assertEquals(HighwayConfig.getAddress(), null); - } - - @Test - public void testGetConnectionPoolPerThread() { - Assert.assertEquals(HighwayConfig.getClientConnectionPoolPerThread(), 1); - } -} diff --git a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayServerConnection.java b/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayServerConnection.java deleted file mode 100644 index f5a8135f0f1..00000000000 --- a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayServerConnection.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.transport.highway; - -import java.net.InetSocketAddress; - -import javax.xml.ws.Holder; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.protostuff.LinkedBuffer; -import io.protostuff.ProtobufOutput; -import io.servicecomb.codec.protobuf.definition.ProtobufManager; -import io.servicecomb.codec.protobuf.utils.ProtobufSchemaUtils; -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.servicecomb.core.CseContext; -import io.servicecomb.core.definition.MicroserviceMeta; -import io.servicecomb.core.definition.MicroserviceMetaManager; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.foundation.vertx.stream.BufferOutputStream; -import io.servicecomb.transport.highway.message.LoginRequest; -import io.servicecomb.transport.highway.message.RequestHeader; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.net.NetSocket; -import io.vertx.core.net.impl.NetSocketImpl; -import io.vertx.core.net.impl.SocketAddressImpl; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestHighwayServerConnection { - private static WrapSchema requestHeaderSchema = - ProtobufSchemaUtils.getOrCreateSchema(RequestHeader.class); - - private static WrapSchema setParameterRequestSchema = - ProtobufSchemaUtils.getOrCreateSchema(LoginRequest.class); - - HighwayServerConnection connection; - - @Mocked - MicroserviceMetaManager microserviceMetaManager; - - @Mocked - NetSocketImpl netSocket; - - RequestHeader header = new RequestHeader(); - - @Before - public void init() { - new Expectations(CseContext.getInstance()) { - { - netSocket.remoteAddress(); - result = new SocketAddressImpl(new InetSocketAddress("127.0.0.1", 80)); - } - }; - connection = new HighwayServerConnection(); - connection.init(netSocket); - - header = new RequestHeader(); - } - - @Test - public void testInvalidMsgType() throws Exception { - header.setMsgType((byte) 100); - Buffer headerBuffer = createBuffer(requestHeaderSchema, header); - - try { - connection.handle(0, headerBuffer, null); - throw new Error("must error"); - } catch (Throwable e) { - Assert.assertEquals("Unknown tcp msgType 100", e.getMessage()); - } - } - - @Test - public void testReqeustHeaderError() throws Exception { - header.setMsgType(MsgType.LOGIN); - Buffer headerBuffer = createBuffer(requestHeaderSchema, header); - - headerBuffer.setByte(0, (byte) 100); - - connection.handle(0, headerBuffer, null); - - Assert.assertEquals(null, connection.getProtocol()); - Assert.assertEquals(null, connection.getZipName()); - } - - @Test - public void testSetParameterNormal() throws Exception { - header.setMsgType(MsgType.LOGIN); - Buffer headerBuffer = createBuffer(requestHeaderSchema, header); - - LoginRequest body = new LoginRequest(); - body.setProtocol("p"); - body.setZipName("z"); - Buffer bodyBuffer = createBuffer(setParameterRequestSchema, body); - - connection.handle(0, headerBuffer, bodyBuffer); - - Assert.assertEquals("p", connection.getProtocol()); - Assert.assertEquals("z", connection.getZipName()); - } - - @Test - public void testSetParameterError() throws Exception { - header.setMsgType(MsgType.LOGIN); - Buffer headerBuffer = createBuffer(requestHeaderSchema, header); - - LoginRequest body = new LoginRequest(); - body.setProtocol("p"); - body.setZipName("z"); - Buffer bodyBuffer = createBuffer(setParameterRequestSchema, body); - bodyBuffer.setByte(0, (byte) 100); - - connection.handle(0, headerBuffer, bodyBuffer); - - Assert.assertEquals(null, connection.getProtocol()); - Assert.assertEquals(null, connection.getZipName()); - } - - @Test - public void testRequestNormal(@Mocked MicroserviceMeta microserviceMeta, @Mocked OperationMeta operationMeta, - @Mocked SchemaMeta schemaMeta) throws Exception { - header.setMsgType(MsgType.REQUEST); - Buffer headerBuffer = createBuffer(requestHeaderSchema, header); - - Buffer bodyBuffer = Buffer.buffer(); - - new Expectations(CseContext.getInstance()) { - { - CseContext.getInstance().getMicroserviceMetaManager(); - result = microserviceMetaManager; - - microserviceMetaManager.ensureFindValue(null); - result = microserviceMeta; - - microserviceMeta.ensureFindSchemaMeta(header.getSchemaId()); - result = schemaMeta; - } - }; - - new Expectations(ProtobufManager.class) { - { - ProtobufManager.getOrCreateOperation(operationMeta); - result = null; - } - }; - - Holder holder = new Holder<>(); - new MockUp() { - @Mock - public boolean init(NetSocket netSocket, long msgId, - RequestHeader header, Buffer bodyBuffer) { - return true; - } - - @Mock - public void execute() { - holder.value = true; - } - }; - - connection.handle(0, headerBuffer, bodyBuffer); - - Assert.assertEquals(null, connection.getProtocol()); - Assert.assertEquals(null, connection.getZipName()); - Assert.assertEquals(true, holder.value); - } - - @Test - public void testRequestError() throws Exception { - header.setMsgType(MsgType.REQUEST); - Buffer headerBuffer = createBuffer(requestHeaderSchema, header); - - Buffer bodyBuffer = Buffer.buffer(); - - Holder holder = new Holder<>(false); - new MockUp() { - @Mock - public boolean init(NetSocket netSocket, long msgId, - RequestHeader header, Buffer bodyBuffer) { - return false; - } - }; - - connection.handle(0, headerBuffer, bodyBuffer); - - Assert.assertEquals(null, connection.getProtocol()); - Assert.assertEquals(null, connection.getZipName()); - Assert.assertEquals(false, holder.value); - } - - protected Buffer createBuffer(WrapSchema schema, Object value) throws Exception { - Buffer headerBuffer; - LinkedBuffer linkedBuffer = LinkedBuffer.allocate(); - ProtobufOutput output = new ProtobufOutput(linkedBuffer); - schema.writeObject(output, value); - try (BufferOutputStream os = new BufferOutputStream()) { - LinkedBuffer.writeTo(os, linkedBuffer); - - headerBuffer = os.getBuffer(); - } - return headerBuffer; - } -} diff --git a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayServerInvoke.java b/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayServerInvoke.java deleted file mode 100644 index 3114322ac86..00000000000 --- a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayServerInvoke.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.netty.buffer.ByteBuf; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.definition.SchemaMeta; -import io.servicecomb.core.executor.ReactiveExecutor; -import io.servicecomb.core.unittest.UnitTestMeta; -import io.servicecomb.foundation.vertx.tcp.TcpConnection; -import io.servicecomb.transport.common.MockUtil; -import io.servicecomb.transport.highway.message.RequestHeader; -import io.vertx.core.buffer.Buffer; -import mockit.Mock; -import mockit.MockUp; - -public class TestHighwayServerInvoke { - class Impl { - public int add(int x, int y) { - return x + y; - } - } - - private UnitTestMeta unitTestMeta; - - private ByteBuf netSocketBuffer; - - private TcpConnection connection; - - @Before - public void setup() { - unitTestMeta = new UnitTestMeta(); - connection = new MockUp() { - @Mock - public void write(ByteBuf data) { - netSocketBuffer = data; - } - }.getMockInstance(); - } - - @Test - public void test() { - MockUtil.getInstance().mockHighwayCodec(); - - SchemaMeta schemaMeta = unitTestMeta.getOrCreateSchemaMeta(Impl.class); - OperationMeta operationMeta = schemaMeta.ensureFindOperation("add"); - operationMeta.setExecutor(new ReactiveExecutor()); - - HighwayServerInvoke highwayServerInvoke = new HighwayServerInvoke(); - highwayServerInvoke.setMicroserviceMetaManager(unitTestMeta.getMicroserviceMetaManager()); - - RequestHeader requestHeader = MockUtil.getInstance().requestHeader; - - // 初始化失败 - requestHeader.setDestMicroservice(null); - Assert.assertFalse(highwayServerInvoke.init(connection, 0, null, null)); - - // 初始化成功 - requestHeader.setDestMicroservice(schemaMeta.getMicroserviceName()); - requestHeader.setSchemaId(schemaMeta.getSchemaId()); - requestHeader.setOperationName(operationMeta.getOperationId()); - Assert.assertTrue(highwayServerInvoke.init(connection, 0, requestHeader, null)); - - // exe成功 - netSocketBuffer = null; - highwayServerInvoke.execute(); - Assert.assertEquals(null, netSocketBuffer); - - // exe失败 - MockUtil.getInstance().decodeRequestSucc = false; - highwayServerInvoke.execute(); - Assert.assertEquals(true, Buffer.buffer(netSocketBuffer).toString().startsWith("CSE.TCP")); - } -} diff --git a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayTransport.java b/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayTransport.java deleted file mode 100644 index 3ac69d9bdf2..00000000000 --- a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayTransport.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import javax.xml.ws.Holder; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.codec.protobuf.definition.OperationProtobuf; -import io.servicecomb.codec.protobuf.utils.WrapSchema; -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.swagger.invocation.AsyncResponse; -import mockit.Mock; -import mockit.MockUp; - -public class TestHighwayTransport { - - private HighwayTransport transport = new HighwayTransport(); - - @Test - public void testGetInstance() { - Assert.assertNotNull(transport); - } - - @Test - public void testInit() { - boolean status = true; - try { - transport.init(); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testSendException() throws Exception { - Invocation invocation = Mockito.mock(Invocation.class); - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - commonHighwayMock(invocation); - - Holder sended = new Holder(false); - new MockUp() { - @Mock - public void send(Invocation invocation, AsyncResponse asyncResp) throws Exception { - sended.value = true; - } - }; - transport.send(invocation, asyncResp); - Assert.assertTrue(sended.value); - } - - @Test - public void testHighway() { - Invocation invocation = Mockito.mock(Invocation.class); - commonHighwayMock(invocation); - Assert.assertEquals("highway", transport.getName()); - } - - private void commonHighwayMock(Invocation invocation) { - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - OperationProtobuf operationProtobuf = Mockito.mock(OperationProtobuf.class); - Mockito.when(operationMeta.getExtData("protobuf")).thenReturn(operationProtobuf); - Endpoint lEndpoint = Mockito.mock(Endpoint.class); - Mockito.when(invocation.getEndpoint()).thenReturn(lEndpoint); - WrapSchema lWrapSchema = Mockito.mock(WrapSchema.class); - Mockito.when(operationProtobuf.getRequestSchema()).thenReturn(lWrapSchema); - URIEndpointObject ep = Mockito.mock(URIEndpointObject.class); - Mockito.when(lEndpoint.getAddress()).thenReturn(ep); - Mockito.when(ep.getHostOrIp()).thenReturn("127.0.0.1"); - Mockito.when(ep.getPort()).thenReturn(80); - } -} diff --git a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayVerticle.java b/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayVerticle.java deleted file mode 100644 index c7b80b2c39e..00000000000 --- a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/TestHighwayVerticle.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Transport; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.transport.common.MockUtil; -import io.vertx.core.Context; -import io.vertx.core.Future; -import io.vertx.core.Vertx; -import io.vertx.core.json.JsonObject; -import mockit.Expectations; -import mockit.Mocked; - -public class TestHighwayVerticle { - - private HighwayServerVerticle highwayVerticle = null; - - @Before - public void setUp() throws Exception { - highwayVerticle = new HighwayServerVerticle(); - } - - @After - public void tearDown() throws Exception { - highwayVerticle = null; - } - - @Test - public void testHighwayVerticle(@Mocked Transport transport, @Mocked Vertx vertx, @Mocked Context context, - @Mocked JsonObject json) { - URIEndpointObject endpiontObject = new URIEndpointObject("highway://127.0.0.1:9090"); - new Expectations() { - { - transport.parseAddress(anyString); - result = endpiontObject; - } - }; - - Endpoint endpoint = new Endpoint(transport, "highway://127.0.0.1:9090"); - - new Expectations() { - { - context.config(); - result = json; - json.getValue(anyString); - result = endpoint; - } - }; - - highwayVerticle.init(vertx, context); - @SuppressWarnings("unchecked") - Future startFuture = Mockito.mock(Future.class); - highwayVerticle.startListen(startFuture); - MockUtil.getInstance().mockHighwayConfig(); - try { - highwayVerticle.startListen(startFuture); - Assert.assertTrue(true); - } catch (Exception e) { - Assert.assertTrue(false); - } - } -} diff --git a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/message/TestLoginRequest.java b/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/message/TestLoginRequest.java deleted file mode 100644 index a9f5ba26f94..00000000000 --- a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/message/TestLoginRequest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.transport.highway.message; - -import org.junit.Assert; -import org.junit.Test; - -public class TestLoginRequest { - @Test - public void test() { - LoginRequest req = new LoginRequest(); - req.setProtocol("p"); - req.setZipName("z"); - - Assert.assertEquals("p", req.getProtocol()); - Assert.assertEquals("z", req.getZipName()); - } -} diff --git a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/message/TestLoginResponse.java b/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/message/TestLoginResponse.java deleted file mode 100644 index 953e83f3ae8..00000000000 --- a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/message/TestLoginResponse.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.transport.highway.message; - -import org.junit.Assert; -import org.junit.Test; - -public class TestLoginResponse { - @Test - public void test() { - LoginResponse resp = new LoginResponse(); - resp.setProtocol("p"); - resp.setZipName("z"); - - Assert.assertEquals("p", resp.getProtocol()); - Assert.assertEquals("z", resp.getZipName()); - } -} diff --git a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/message/TestRequestHeader.java b/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/message/TestRequestHeader.java deleted file mode 100644 index 0fdc0a9bf17..00000000000 --- a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/message/TestRequestHeader.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway.message; - -import java.util.Map; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestRequestHeader { - - private RequestHeader requestHeader = null; - - @Before - public void setUp() throws Exception { - requestHeader = new RequestHeader(); - } - - @After - public void tearDown() throws Exception { - requestHeader = null; - } - - @Test - public void testContext() { - - Map context = null; - requestHeader.setContext(context); - Assert.assertNull(requestHeader.getContext()); - } - - @Test - public void testDestMicroservice() { - requestHeader.setDestMicroservice("test"); - Assert.assertEquals("test", requestHeader.getDestMicroservice()); - } - - @Test - public void testFlags() { - - requestHeader.setFlags(1); - Assert.assertEquals(1, requestHeader.getFlags()); - } - - @Test - public void testOperationName() { - - requestHeader.setOperationName("cse"); - Assert.assertEquals("cse", requestHeader.getOperationName()); - } - - @Test - public void testSchemaId() { - - requestHeader.setSchemaId("id"); - Assert.assertEquals("id", requestHeader.getSchemaId()); - } -} diff --git a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/message/TestResponseHeader.java b/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/message/TestResponseHeader.java deleted file mode 100644 index 91da5a6618a..00000000000 --- a/transports/transport-highway/src/test/java/io/servicecomb/transport/highway/message/TestResponseHeader.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.highway.message; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestResponseHeader { - - private ResponseHeader responseHeader = null; - - Map context = new HashMap<>(); - - @Before - public void setUp() throws Exception { - responseHeader = new ResponseHeader(); - } - - @After - public void tearDown() throws Exception { - responseHeader = null; - } - - @Test - public void testSetContext() { - context.put("key1", "v1"); - responseHeader.setContext(context); - Assert.assertNotNull(responseHeader.getContext()); - Assert.assertEquals("v1", responseHeader.getContext().get("key1")); - } -} diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/common/MockUtil.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/common/MockUtil.java new file mode 100644 index 00000000000..385ba6d4b8a --- /dev/null +++ b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/common/MockUtil.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.common; + +import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.transport.highway.HighwayCodec; +import org.apache.servicecomb.transport.highway.HighwayConfig; +import org.apache.servicecomb.transport.highway.message.RequestHeader; +import org.mockito.Mockito; + +import io.vertx.core.buffer.Buffer; +import mockit.Mock; +import mockit.MockUp; + +public class MockUtil { + + private static final MockUtil instance = new MockUtil(); + + private MockUtil() { + + } + + public static MockUtil getInstance() { + return instance; + } + + public void mockHighwayConfig() { + + new MockUp() { + @Mock + String getAddress() { + return "127.0.0.1"; + } + }; + } + + public RequestHeader requestHeader = new RequestHeader(); + + public boolean decodeRequestSucc = true; + + public void mockHighwayCodec() { + + new MockUp() { + @Mock + RequestHeader readRequestHeader(Buffer headerBuffer) throws Exception { + return requestHeader; + } + + @Mock + public Invocation decodeRequest(RequestHeader header, OperationProtobuf operationProtobuf, Buffer bodyBuffer) + throws Exception { + if (decodeRequestSucc) { + return Mockito.mock(Invocation.class); + } + + throw new Error("decode failed"); + } + }; + } +} diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilterTest.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilterTest.java new file mode 100644 index 00000000000..46352690c95 --- /dev/null +++ b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilterTest.java @@ -0,0 +1,193 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.highway; + +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 static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.SCBStatus; +import org.apache.servicecomb.core.bootstrap.SCBBootstrap; +import org.apache.servicecomb.core.definition.OperationMeta; +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.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.apache.servicecomb.transport.highway.message.RequestHeader; +import org.apache.servicecomb.transport.highway.message.ResponseHeader; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.core.env.Environment; + +import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.impl.SysProps; +import io.vertx.core.json.Json; +import mockit.Expectations; +import mockit.Mocked; +import mockit.Verifications; + +public class HighwayServerCodecFilterTest { + HighwayServerCodecFilter codecFilter = new HighwayServerCodecFilter(); + + Invocation invocation; + + @Mocked + Endpoint endpoint; + + @Mocked + OperationMeta operationMeta; + + @Mocked + HighwayTransportContext transportContext; + + MultiMap headers = MultiMap.caseInsensitiveMultiMap(); + + FilterNode nextNode = new FilterNode(new AbstractFilter() { + @Override + public String getName() { + return "test"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + Response response = Response.ok("ok"); + response.setHeaders(headers); + return CompletableFuture.completedFuture(response); + } + }); + + static SCBEngine engine; + + Environment environment = Mockito.mock(Environment.class); + + @Before + public void setUp() { + 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.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); + + engine = SCBBootstrap.createSCBEngineForTest(environment); + engine.setStatus(SCBStatus.UP); + invocation = InvocationFactory.forProvider(endpoint, operationMeta, null); + } + + @After + public void tearDown() { + engine.destroy(); + } + + private void mockDecodeRequestFail() throws Exception { + new Expectations(invocation) { + { + invocation.getTransportContext(); + result = transportContext; + } + }; + new Expectations(HighwayCodec.class) { + { + HighwayCodec.decodeRequest(invocation, (RequestHeader) any, (OperationProtobuf) any, (Buffer) any); + result = new RuntimeExceptionWithoutStackTrace("encode request failed"); + } + }; + } + + @Test + public void should_not_invoke_filter_when_decode_request_failed(@Mocked FilterNode nextNode) throws Exception { + mockDecodeRequestFail(); + + codecFilter.onFilter(invocation, nextNode); + + new Verifications() { + { + nextNode.onFilter(invocation); + times = 0; + } + }; + } + + @Test + public void should_convert_exception_to_response_when_decode_request_failed() + throws Exception { + mockDecodeRequestFail(); + + Response response = codecFilter.onFilter(invocation, nextNode).get(); + + assertThat(response.getStatus()).isEqualTo(INTERNAL_SERVER_ERROR); + + JSONAssert.assertEquals(Json.encode(((InvocationException) response.getResult()).getErrorData()), + "{\"code\":\"SCB.50000000\",\"message\":\"Unexpected " + + "exception when processing null. encode request failed\"}", false); + } + + private void success_invocation() throws InterruptedException, ExecutionException { + new Expectations(invocation) { + { + invocation.getTransportContext(); + result = transportContext; + } + }; + + codecFilter.onFilter(invocation, nextNode).get(); + } + + @Test + public void should_encode_response_header(@Mocked ResponseHeader responseHeader) + throws ExecutionException, InterruptedException { + success_invocation(); + + new Verifications() { + { + MultiMap captureHeaders; + responseHeader.fromMultiMap(captureHeaders = withCapture()); + assertThat(captureHeaders).isSameAs(headers); + } + }; + } + + @Test + public void should_encode_response() throws ExecutionException, InterruptedException { + success_invocation(); + + new Verifications() { + { + Buffer captureBuffer; + transportContext.setResponseBuffer(captureBuffer = withCapture()); + assertThat(captureBuffer).isNotNull(); + } + }; + } +} diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayClient.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayClient.java new file mode 100644 index 00000000000..d7a29f0f86e --- /dev/null +++ b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayClient.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.highway; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager; +import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import io.vertx.core.AbstractVerticle; +import io.vertx.core.DeploymentOptions; +import io.vertx.core.Vertx; +import mockit.Deencapsulation; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; + +public class TestHighwayClient { + private static final String REQUEST_TIMEOUT_KEY = "servicecomb.request.timeout"; + + HighwayClient client = new HighwayClient(); + + Environment environment = Mockito.mock(Environment.class); + + static long nanoTime = 123; + + @BeforeClass + public static void setup() { + new MockUp() { + @Mock + long nanoTime() { + return nanoTime; + } + }; + } + + @AfterClass + public static void teardown() { + + } + + @Before + public void setUp() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty(REQUEST_TIMEOUT_KEY, long.class, (long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT)) + .thenReturn((long) 2000); + Mockito.when(environment.getProperty("servicecomb.highway.client.verticle-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.highway.client.thread-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.highway.server.verticle-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.highway.server.thread-count", int.class, -1)) + .thenReturn(-1); + } + + @Test + public void testLoginTimeout(@Mocked Vertx vertx) { + TcpClientConfig tcpClientConfig = client.createTcpClientConfig(); + Assertions.assertEquals(2000, tcpClientConfig.getMsLoginTimeout()); + } + + @Test + public void testHighwayClientSSL(@Mocked Vertx vertx) throws Exception { + new MockUp() { + @Mock + Map blockDeploy(Vertx vertx, + Class cls, + DeploymentOptions options) { + Map result = new HashMap<>(); + result.put("code", true); + return result; + } + }; + + client.init(vertx); + + ClientPoolManager clientMgr = Deencapsulation.getField(client, "clientMgr"); + Assertions.assertSame(vertx, Deencapsulation.getField(clientMgr, "vertx")); + } +} diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayTransport.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayTransport.java new file mode 100644 index 00000000000..18d674f00f0 --- /dev/null +++ b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayTransport.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.highway; + +import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; +import org.apache.servicecomb.codec.protobuf.definition.RequestRootSerializer; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +import io.vertx.core.impl.SysProps; + +public class TestHighwayTransport { + private static final Logger LOGGER = LoggerFactory.getLogger(TestHighwayTransport.class); + + Environment environment = Mockito.mock(Environment.class); + + @BeforeClass + public static void setup() { + VertxUtils.blockCloseVertxByName("transport"); + Thread.getAllStackTraces().keySet().forEach(t -> LOGGER.info("before: {}", t.getName())); + } + + @AfterClass + public static void teardown() { + VertxUtils.blockCloseVertxByName("transport"); + Thread.getAllStackTraces().keySet().forEach(t -> LOGGER.info("after: {}", t.getName())); + } + + @Before + public void setUp() { + Mockito.when(environment.getProperty( + "servicecomb.request.timeout", long.class, (long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT)) + .thenReturn((long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT); + Mockito.when(environment.getProperty("servicecomb.highway.client.verticle-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.highway.client.thread-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.highway.server.verticle-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.highway.server.thread-count", int.class, -1)) + .thenReturn(-1); + 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 testGetInstance() { + HighwayTransport transport = new HighwayTransport(); + Assertions.assertNotNull(transport); + } + + @Test + public void testInit() { + HighwayTransport transport = new HighwayTransport(); + transport.setEnvironment(environment); + boolean status = true; + try { + transport.init(); + } catch (Exception e) { + e.printStackTrace(); + status = false; + } + + Assertions.assertTrue(status); + } + + @Test + public void testHighway() { + HighwayTransport transport = new HighwayTransport(); + Invocation invocation = Mockito.mock(Invocation.class); + commonHighwayMock(invocation); + Assertions.assertEquals("highway", transport.getName()); + } + + private void commonHighwayMock(Invocation invocation) { + OperationMeta operationMeta = Mockito.mock(OperationMeta.class); + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + OperationProtobuf operationProtobuf = Mockito.mock(OperationProtobuf.class); + Mockito.when(operationMeta.getExtData("protobuf")).thenReturn(operationProtobuf); + Endpoint lEndpoint = Mockito.mock(Endpoint.class); + Mockito.when(invocation.getEndpoint()).thenReturn(lEndpoint); + RequestRootSerializer lWrapSchema = Mockito.mock(RequestRootSerializer.class); + Mockito.when(operationProtobuf.getRequestRootSerializer()).thenReturn(lWrapSchema); + URIEndpointObject ep = Mockito.mock(URIEndpointObject.class); + Mockito.when(lEndpoint.getAddress()).thenReturn(ep); + Mockito.when(ep.getHostOrIp()).thenReturn("127.0.0.1"); + Mockito.when(ep.getPort()).thenReturn(80); + } +} diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayVerticle.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayVerticle.java new file mode 100644 index 00000000000..e17ec8348f0 --- /dev/null +++ b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayVerticle.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.transport.highway; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.transport.common.MockUtil; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; + +import io.vertx.core.Context; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import io.vertx.core.json.JsonObject; +import mockit.Expectations; +import mockit.Mocked; + +public class TestHighwayVerticle { + @Test + public void testHighwayVehicle(@Mocked Transport transport, @Mocked Vertx vertx, @Mocked Context context, + @Mocked JsonObject json) { + HighwayServerVerticle highwayVehicle = new HighwayServerVerticle(); + URIEndpointObject endpointObject = new URIEndpointObject("highway://127.0.0.1:9090"); + new Expectations() { + { + transport.parseAddress(anyString); + result = endpointObject; + } + }; + + Endpoint endpoint = new Endpoint(transport, "highway://127.0.0.1:9090"); + + new Expectations() { + { + context.config(); + result = json; + json.getValue(anyString); + result = endpoint; + } + }; + + highwayVehicle.init(vertx, context); + @SuppressWarnings("unchecked") + Promise startPromise = Mockito.mock(Promise.class); + highwayVehicle.startListen(startPromise); + MockUtil.getInstance().mockHighwayConfig(); + try { + highwayVehicle.startListen(startPromise); + Assertions.assertTrue(true); + } catch (Exception e) { + Assertions.fail(); + } + } +} diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestHeaders.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestHeaders.java new file mode 100644 index 00000000000..d51100bae04 --- /dev/null +++ b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestHeaders.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.transport.highway.message; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestHeaders { + @Test + public void test1() { + Headers headers = new Headers(); + Assertions.assertNull(headers.getFirst("h1")); + Assertions.assertNull(headers.getHeader("h1")); + + Map> headerMap = new HashMap<>(); + List h1Value = Arrays.asList("h1v1", "h1v2"); + headerMap.put("h1", h1Value); + headerMap.put("h2", null); + headerMap.put("h3", Arrays.asList()); + headers.setHeaderMap(headerMap); + + Assertions.assertEquals(headerMap, headers.getHeaderMap()); + Assertions.assertEquals("h1v1", headers.getFirst("h1")); + Assertions.assertNull(headers.getFirst("h2")); + Assertions.assertNull(headers.getFirst("h3")); + Assertions.assertEquals(h1Value, headers.getHeader("h1")); + } + + @Test + public void test2() { + Headers headers = new Headers(); + headers.addHeader("h1", "h1v1"); + headers.addHeader("h1", "h1v2"); + + Assertions.assertEquals("h1v1", headers.getFirst("h1")); + } + + @Test + public void addHeader_list() { + Headers headers = new Headers(); + headers.addHeader("h", Arrays.asList("v1", "v2")); + headers.addHeader("h", Arrays.asList("v3")); + + MatcherAssert.assertThat(headers.getHeader("h"), Matchers.contains("v1", "v2", "v3")); + } +} diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestLoginRequest.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestLoginRequest.java new file mode 100644 index 00000000000..13b12900005 --- /dev/null +++ b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestLoginRequest.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.transport.highway.message; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestLoginRequest { + @Test + public void test() { + LoginRequest req = new LoginRequest(); + req.setProtocol("p"); + req.setZipName("z"); + + Assertions.assertEquals("p", req.getProtocol()); + Assertions.assertEquals("z", req.getZipName()); + } +} diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestLoginResponse.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestLoginResponse.java new file mode 100644 index 00000000000..45132c4dc8c --- /dev/null +++ b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestLoginResponse.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.transport.highway.message; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestLoginResponse { + @Test + public void test() { + LoginResponse resp = new LoginResponse(); + resp.setProtocol("p"); + resp.setZipName("z"); + + Assertions.assertEquals("p", resp.getProtocol()); + Assertions.assertEquals("z", resp.getZipName()); + } +} diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestRequestHeader.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestRequestHeader.java new file mode 100644 index 00000000000..01be0df7943 --- /dev/null +++ b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestRequestHeader.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.transport.highway.message; + +import java.util.Map; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class TestRequestHeader { + + private RequestHeader requestHeader = null; + + @Before + public void setUp() throws Exception { + requestHeader = new RequestHeader(); + } + + @After + public void tearDown() throws Exception { + requestHeader = null; + } + + @Test + public void testContext() { + + Map context = null; + requestHeader.setContext(context); + Assertions.assertNull(requestHeader.getContext()); + } + + @Test + public void testDestMicroservice() { + requestHeader.setDestMicroservice("test"); + Assertions.assertEquals("test", requestHeader.getDestMicroservice()); + } + + @Test + public void testFlags() { + + requestHeader.setFlags(1); + Assertions.assertEquals(1, requestHeader.getFlags()); + } + + @Test + public void testOperationName() { + + requestHeader.setOperationName("cse"); + Assertions.assertEquals("cse", requestHeader.getOperationName()); + } + + @Test + public void testSchemaId() { + + requestHeader.setSchemaId("id"); + Assertions.assertEquals("id", requestHeader.getSchemaId()); + } +} diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestResponseHeader.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestResponseHeader.java new file mode 100644 index 00000000000..f5a6ffef9c9 --- /dev/null +++ b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/message/TestResponseHeader.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.transport.highway.message; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class TestResponseHeader { + + private ResponseHeader responseHeader = null; + + Map context = new HashMap<>(); + + @Before + public void setUp() throws Exception { + responseHeader = new ResponseHeader(); + } + + @After + public void tearDown() throws Exception { + responseHeader = null; + } + + @Test + public void testSetContext() { + context.put("key1", "v1"); + responseHeader.setContext(context); + Assertions.assertNotNull(responseHeader.getContext()); + Assertions.assertEquals("v1", responseHeader.getContext().get("key1")); + } +} diff --git a/transports/transport-highway/src/test/resources/log4j.properties b/transports/transport-highway/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/transports/transport-highway/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/transports/transport-rest/pom.xml b/transports/transport-rest/pom.xml index d1bb7098ee4..933e7c8858c 100644 --- a/transports/transport-rest/pom.xml +++ b/transports/transport-rest/pom.xml @@ -1,12 +1,13 @@ - 4.0.0 - - io.servicecomb - transports - 0.3.0-SNAPSHOT - - transport-rest - pom + 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 + transports + 3.4.0-SNAPSHOT + + transport-rest + Java Chassis::Transports::Rest + pom - - transport-rest-vertx - transport-rest-servlet - transport-rest-client - + + transport-rest-vertx + transport-rest-servlet + transport-rest-client + diff --git a/transports/transport-rest/transport-rest-client/pom.xml b/transports/transport-rest/transport-rest-client/pom.xml index 60c14ae316d..49175b2140a 100644 --- a/transports/transport-rest/transport-rest-client/pom.xml +++ b/transports/transport-rest/transport-rest-client/pom.xml @@ -1,12 +1,13 @@ - - 4.0.0 - - io.servicecomb - transport-rest - 0.3.0-SNAPSHOT - - transport-rest-client + + 4.0.0 + + org.apache.servicecomb + transport-rest + 3.4.0-SNAPSHOT + + transport-rest-client + Java Chassis::Transports::Rest::Client + + + + io.vertx + vertx-rx-java3 + + + org.apache.servicecomb + foundation-vertx + + + org.apache.servicecomb + transport-common + + + org.apache.servicecomb + common-rest + + + + io.vertx + vertx-codegen + provided + - - - io.servicecomb - foundation-vertx - - - io.servicecomb - common-rest - - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.servicecomb + foundation-test-scaffolding + test + + + org.apache.servicecomb + registry-local + test + + + org.apache.servicecomb + swagger-generator-jaxrs + test + + + org.apache.commons + commons-text + test + + + org.jmockit + jmockit + test + + diff --git a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/RestTransportClient.java b/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/RestTransportClient.java deleted file mode 100644 index 48b78ed32fb..00000000000 --- a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/RestTransportClient.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.foundation.ssl.SSLCustom; -import io.servicecomb.foundation.ssl.SSLOption; -import io.servicecomb.foundation.ssl.SSLOptionFactory; -import io.servicecomb.foundation.vertx.VertxTLSBuilder; -import io.servicecomb.foundation.vertx.VertxUtils; -import io.servicecomb.foundation.vertx.client.ClientPoolManager; -import io.servicecomb.foundation.vertx.client.http.HttpClientVerticle; -import io.servicecomb.foundation.vertx.client.http.HttpClientWithContext; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.transport.rest.client.http.HttpMethodFactory; -import io.servicecomb.transport.rest.client.http.VertxHttpMethod; -import io.vertx.core.DeploymentOptions; -import io.vertx.core.Vertx; -import io.vertx.core.http.HttpClientOptions; - -public final class RestTransportClient { - private static final Logger LOGGER = LoggerFactory.getLogger(RestTransportClient.class); - - private static final String SSL_KEY = "rest.consumer"; - - private ClientPoolManager clientMgr = new ClientPoolManager<>(); - - private final boolean sslEnabled; - - public RestTransportClient(boolean sslEnabled) { - this.sslEnabled = sslEnabled; - } - - public void init(Vertx vertx) throws Exception { - HttpClientOptions httpClientOptions = createHttpClientOptions(); - DeploymentOptions deployOptions = VertxUtils.createClientDeployOptions(clientMgr, - TransportClientConfig.getThreadCount(), - TransportClientConfig.getConnectionPoolPerThread(), - httpClientOptions); - VertxUtils.blockDeploy(vertx, HttpClientVerticle.class, deployOptions); - } - - private HttpClientOptions createHttpClientOptions() { - HttpClientOptions httpClientOptions = new HttpClientOptions(); - - if (this.sslEnabled) { - SSLOptionFactory factory = - SSLOptionFactory.createSSLOptionFactory(SSL_KEY, - null); - SSLOption sslOption; - if (factory == null) { - sslOption = SSLOption.buildFromYaml(SSL_KEY); - } else { - sslOption = factory.createSSLOption(); - } - SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); - VertxTLSBuilder.buildHttpClientOptions(sslOption, sslCustom, httpClientOptions); - } - return httpClientOptions; - } - - public void send(Invocation invocation, AsyncResponse asyncResp) throws Exception { - HttpClientWithContext httpClientWithContext = clientMgr.findThreadBindClientPool(); - - OperationMeta operationMeta = invocation.getOperationMeta(); - RestOperationMeta swaggerRestOperation = operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); - String method = swaggerRestOperation.getHttpMethod(); - try { - VertxHttpMethod httpMethod = HttpMethodFactory.findHttpMethodInstance(method); - LOGGER.debug("Calling method {} of {} by rest", method, invocation.getMicroserviceName()); - httpMethod.doMethod(httpClientWithContext, invocation, asyncResp); - } catch (Exception e) { - asyncResp.fail(invocation.getInvocationType(), e); - LOGGER.error("vertx rest transport send error.", e); - } - } -} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/RestTransportClientManager.java b/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/RestTransportClientManager.java deleted file mode 100644 index 0c49dd972ff..00000000000 --- a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/RestTransportClientManager.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.servicecomb.foundation.vertx.VertxUtils; -import io.vertx.core.Vertx; - -/** - * REST客户端。只需要两个实例, 一个ssl,一个非ssl. - */ -public final class RestTransportClientManager { - private static final Logger LOGGER = LoggerFactory.getLogger(RestTransportClientManager.class); - - public static final RestTransportClientManager INSTANCE = new RestTransportClientManager(); - - // same instance in AbstractTranport. need refactor in future. - private final Vertx transportVertx = VertxUtils.getOrCreateVertxByName("transport", null); - - private static final Object LOCK = new Object(); - - private volatile RestTransportClient sslClient = null; - - private volatile RestTransportClient nonSslCient = null; - - private RestTransportClientManager() { - } - - public RestTransportClient getRestTransportClient(boolean sslEnabled) { - try { - if (sslEnabled) { - if (sslClient == null) { - synchronized (LOCK) { - if (sslClient == null) { - RestTransportClient client = new RestTransportClient(true); - client.init(transportVertx); - sslClient = client; - } - } - } - return sslClient; - } else { - if (nonSslCient == null) { - synchronized (LOCK) { - if (nonSslCient == null) { - RestTransportClient client = new RestTransportClient(false); - client.init(transportVertx); - nonSslCient = client; - } - } - } - return nonSslCient; - } - } catch (Exception e) { - LOGGER.error(""); - throw new IllegalStateException("init rest client transport failed."); - } - } -} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/TransportClientConfig.java b/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/TransportClientConfig.java deleted file mode 100644 index 3a54cb1dbc6..00000000000 --- a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/TransportClientConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client; - -import com.netflix.config.DynamicIntProperty; -import com.netflix.config.DynamicPropertyFactory; - -public final class TransportClientConfig { - private TransportClientConfig() { - } - - public static int getThreadCount() { - DynamicIntProperty address = - DynamicPropertyFactory.getInstance().getIntProperty("cse.rest.client.thread-count", 1); - return address.get(); - } - - public static int getConnectionPoolPerThread() { - DynamicIntProperty address = - DynamicPropertyFactory.getInstance().getIntProperty("cse.rest.client.connection-pool-per-thread", 1); - return address.get(); - } -} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/HttpMethodFactory.java b/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/HttpMethodFactory.java deleted file mode 100644 index cd415455bc8..00000000000 --- a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/HttpMethodFactory.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client.http; - -import java.util.HashMap; -import java.util.Map; - -import javax.ws.rs.HttpMethod; - -public final class HttpMethodFactory { - - private static Map httpMethodMap = new HashMap<>(); - - static { - addHttpMethod(HttpMethod.GET, VertxGetMethod.INSTANCE); - addHttpMethod(HttpMethod.POST, VertxPostMethod.INSTANCE); - addHttpMethod(HttpMethod.PUT, VertxPutMethod.INSTANCE); - addHttpMethod(HttpMethod.DELETE, VertxDeleteMethod.INSTANCE); - } - - static void addHttpMethod(String httpMethod, VertxHttpMethod instance) { - httpMethodMap.put(httpMethod, instance); - } - - private HttpMethodFactory() { - } - - public static VertxHttpMethod findHttpMethodInstance(String method) throws Exception { - VertxHttpMethod httpMethod = httpMethodMap.get(method); - if (httpMethod == null) { - throw new Exception(String.format("Http method %s is not supported", method)); - } - - return httpMethod; - } -} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxDeleteMethod.java b/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxDeleteMethod.java deleted file mode 100644 index f55e2dfb4b5..00000000000 --- a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxDeleteMethod.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client.http; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.Invocation; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientRequest; - -public final class VertxDeleteMethod extends VertxHttpMethod { - public static final VertxDeleteMethod INSTANCE = new VertxDeleteMethod(); - - private VertxDeleteMethod() { - } - - @Override - protected HttpClientRequest createRequest(HttpClient client, Invocation invocation, IpPort ipPort, String path, - RestOperationMeta operation, - AsyncResponse asyncResp) { - HttpClientRequest clientRequest = - client.delete(ipPort.getPort(), ipPort.getHostOrIp(), path, response -> { - handleResponse(invocation, response, operation, asyncResp); - }); - return clientRequest; - } -} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxGetMethod.java b/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxGetMethod.java deleted file mode 100644 index 0220da3a516..00000000000 --- a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxGetMethod.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client.http; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.Invocation; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientRequest; - -public final class VertxGetMethod extends VertxHttpMethod { - public static final VertxGetMethod INSTANCE = new VertxGetMethod(); - - private VertxGetMethod() { - } - - @Override - protected HttpClientRequest createRequest(HttpClient client, Invocation invocation, IpPort ipPort, String path, - RestOperationMeta operation, AsyncResponse asyncResp) { - HttpClientRequest clientRequest = - client.get(ipPort.getPort(), ipPort.getHostOrIp(), path, response -> { - handleResponse(invocation, response, operation, asyncResp); - }); - - return clientRequest; - } -} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxHttpMethod.java b/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxHttpMethod.java deleted file mode 100644 index 273cdef78af..00000000000 --- a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxHttpMethod.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client.http; - -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.common.rest.codec.RestCodec; -import io.servicecomb.common.rest.codec.param.RestClientRequestImpl; -import io.servicecomb.common.rest.codec.produce.ProduceProcessor; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.transport.AbstractTransport; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.foundation.common.utils.JsonUtils; -import io.servicecomb.foundation.vertx.client.http.HttpClientWithContext; -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.servicecomb.swagger.invocation.exception.ExceptionFactory; -import io.servicecomb.swagger.invocation.response.ResponseMeta; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientRequest; -import io.vertx.core.http.HttpClientResponse; - -public abstract class VertxHttpMethod { - private static final Logger LOGGER = LoggerFactory.getLogger(VertxHttpMethod.class); - - public void doMethod(HttpClientWithContext httpClientWithContext, Invocation invocation, - AsyncResponse asyncResp) throws Exception { - OperationMeta operationMeta = invocation.getOperationMeta(); - RestOperationMeta swaggerRestOperation = operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); - - String path = this.createRequestPath(invocation, swaggerRestOperation); - IpPort ipPort = (IpPort) invocation.getEndpoint().getAddress(); - - HttpClientRequest clientRequest = - this.createRequest(httpClientWithContext.getHttpClient(), - invocation, - ipPort, - path, - swaggerRestOperation, - asyncResp); - clientRequest.putHeader(io.servicecomb.core.Const.TARGET_MICROSERVICE, invocation.getMicroserviceName()); - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(clientRequest); - RestCodec.argsToRest(invocation.getArgs(), swaggerRestOperation, restClientRequest); - - clientRequest.exceptionHandler(e -> { - LOGGER.error(e.toString()); - asyncResp.fail(invocation.getInvocationType(), e); - }); - - LOGGER.debug( - "Running HTTP method {} on {} at {}:{}{}", - invocation.getOperationMeta().getMethod(), - invocation.getMicroserviceName(), - ipPort.getHostOrIp(), - ipPort.getPort(), - path); - - // 从业务线程转移到网络线程中去发送 - httpClientWithContext.runOnContext(httpClient -> { - this.setCseContext(invocation, clientRequest); - clientRequest.setTimeout(AbstractTransport.getRequestTimeout()); - try { - restClientRequest.end(); - } catch (Exception e) { - LOGGER.error("send http reqeust failed,", e); - asyncResp.fail(invocation.getInvocationType(), e); - } - }); - } - - protected abstract HttpClientRequest createRequest(HttpClient client, Invocation invocation, IpPort ipPort, - String path, - RestOperationMeta operation, - AsyncResponse asyncResp); - - protected void handleResponse(Invocation invocation, HttpClientResponse httpResponse, - RestOperationMeta restOperation, - AsyncResponse asyncResp) { - ProduceProcessor produceProcessor = null; - String contentType = httpResponse.getHeader("Content-Type"); - if (contentType != null) { - String contentTypeForFind = contentType; - int idx = contentType.indexOf(';'); - if (idx != -1) { - contentTypeForFind = contentType.substring(0, idx); - } - produceProcessor = restOperation.findProduceProcessor(contentTypeForFind); - } - if (produceProcessor == null) { - String msg = - String.format("path %s, statusCode %d, reasonPhrase %s, response content-type %s is not supported", - restOperation.getAbsolutePath(), - httpResponse.statusCode(), - httpResponse.statusMessage(), - contentType); - Exception exception = ExceptionFactory.createConsumerException(new CommonExceptionData(msg)); - asyncResp.fail(invocation.getInvocationType(), exception); - return; - } - - ProduceProcessor finalProduceProcessor = produceProcessor; - httpResponse.bodyHandler(responseBuf -> { - // 此时是在网络线程中,不应该就地处理,通过dispatcher转移线程 - invocation.getResponseExecutor().execute(() -> { - try { - ResponseMeta responseMeta = - restOperation.getOperationMeta().findResponseMeta(httpResponse.statusCode()); - Object result = finalProduceProcessor.decodeResponse(responseBuf, responseMeta.getJavaType()); - Response response = - Response.create(httpResponse.statusCode(), httpResponse.statusMessage(), result); - for (String headerName : responseMeta.getHeaders().keySet()) { - List headerValues = httpResponse.headers().getAll(headerName); - for (String headerValue : headerValues) { - response.getHeaders().addHeader(headerName, headerValue); - } - } - asyncResp.complete(response); - } catch (Throwable e) { - asyncResp.fail(invocation.getInvocationType(), e); - } - }); - }); - } - - protected void setCseContext(Invocation invocation, HttpClientRequest request) { - try { - String cseContext = JsonUtils.writeValueAsString(invocation.getContext()); - request.putHeader(io.servicecomb.core.Const.CSE_CONTEXT, cseContext); - } catch (Exception e) { - LOGGER.debug(e.toString()); - } - } - - protected String createRequestPath(Invocation invocation, - RestOperationMeta swaggerRestOperation) throws Exception { - URIEndpointObject address = (URIEndpointObject) invocation.getEndpoint().getAddress(); - String urlPrefix = address.getFirst(Const.URL_PREFIX); - - String path = (String) invocation.getHandlerContext().get(RestConst.REST_CLIENT_REQUEST_PATH); - if (path == null) { - path = swaggerRestOperation.getPathBuilder().createRequestPath(invocation.getArgs()); - } - - if (StringUtils.isEmpty(urlPrefix) || path.startsWith(urlPrefix)) { - return path; - } - - return urlPrefix + path; - } -} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxPostMethod.java b/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxPostMethod.java deleted file mode 100644 index 718d70d3892..00000000000 --- a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxPostMethod.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client.http; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.Invocation; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientRequest; - -public final class VertxPostMethod extends VertxHttpMethod { - public static final VertxPostMethod INSTANCE = new VertxPostMethod(); - - private VertxPostMethod() { - } - - @Override - protected HttpClientRequest createRequest(HttpClient client, Invocation invocation, IpPort ipPort, String path, - RestOperationMeta operation, - AsyncResponse asyncResp) { - HttpClientRequest clientRequest = - client.post(ipPort.getPort(), ipPort.getHostOrIp(), path, response -> { - handleResponse(invocation, response, operation, asyncResp); - }); - return clientRequest; - } -} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxPutMethod.java b/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxPutMethod.java deleted file mode 100644 index af5aadf8a80..00000000000 --- a/transports/transport-rest/transport-rest-client/src/main/java/io/servicecomb/transport/rest/client/http/VertxPutMethod.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client.http; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.Invocation; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientRequest; - -public final class VertxPutMethod extends VertxHttpMethod { - public static final VertxPutMethod INSTANCE = new VertxPutMethod(); - - private VertxPutMethod() { - } - - @Override - protected HttpClientRequest createRequest(HttpClient client, Invocation invocation, IpPort ipPort, String path, - RestOperationMeta operation, - AsyncResponse asyncResp) { - HttpClientRequest clientRequest = - client.put(ipPort.getPort(), ipPort.getHostOrIp(), path, response -> { - handleResponse(invocation, response, operation, asyncResp); - }); - return clientRequest; - } -} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/BoundaryFactory.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/BoundaryFactory.java new file mode 100644 index 00000000000..d50193b4aba --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/BoundaryFactory.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.transport.rest.client; + +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; + +public interface BoundaryFactory { + String BOUNDARY_PREFIX = "boundary-" + UUID.randomUUID() + "-"; + + AtomicLong BOUNDARY_INDEX = new AtomicLong(); + + BoundaryFactory DEFAULT = () -> BOUNDARY_PREFIX + BOUNDARY_INDEX.getAndIncrement(); + + String create(); +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/Http2TransportHttpClientOptionsSPI.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/Http2TransportHttpClientOptionsSPI.java new file mode 100644 index 00000000000..cfa6e9356be --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/Http2TransportHttpClientOptionsSPI.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.transport.rest.client; + +import io.vertx.core.http.HttpVersion; + +public class Http2TransportHttpClientOptionsSPI extends HttpTransportHttpClientOptionsSPI { + public static final String CLIENT_NAME = "http2-transport-client"; + + @Override + public String clientName() { + return CLIENT_NAME; + } + + @Override + public int getOrder() { + return super.getOrder() + 1; + } + + @Override + public boolean enabled() { + return TransportClientConfig.isHttp2TransportClientEnabled(); + } + + @Override + public HttpVersion getHttpVersion() { + return HttpVersion.HTTP_2; + } + + @Override + public String getWorkerPoolName() { + return "pool-worker-transport-client-http2"; + } + + @Override + public boolean isUseAlpn() { + return TransportClientConfig.getUseAlpn(); + } + + @Override + public int getHttp2MultiplexingLimit() { + return TransportClientConfig.getHttp2MultiplexingLimit(); + } + + @Override + public int getHttp2MaxPoolSize() { + return TransportClientConfig.getHttp2ConnectionMaxPoolSize(); + } + + @Override + public int getIdleTimeoutInSeconds() { + return TransportClientConfig.getHttp2ConnectionIdleTimeoutInSeconds(); + } + + @Override + public int getKeepAliveTimeout() { + return TransportClientConfig.getHttp2ConnectionKeepAliveTimeoutInSeconds(); + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpClientRequestFactory.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpClientRequestFactory.java new file mode 100644 index 00000000000..8b2f3afc136 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpClientRequestFactory.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.transport.rest.client; + +import org.apache.servicecomb.core.Invocation; + +import io.vertx.core.Future; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.RequestOptions; + +/** + * some service has special domain name rule, eg: k8s
+ * assume k8s domain name is https://k8s.com:1234, and clusterId is my-id
+ * then must send request to https://my-id.k8s.com:1234
+ *
+ * this interface allowed to modify host by invocation argument, eg:
+ *
+ * {@code
+ *  Future create(Invocation invocation, HttpClient httpClient, HttpMethod method, RequestOptions options) {
+ *    if ("k8s".equals(invocation.getMicroserviceName())) {
+ *      options.setHost(invocation.getSwaggerArgument("clusterId") + "." + options.getHost());
+ *    }
+ *
+ *    return httpClient.request(options);
+ *  }
+ * }
+ * 
+ */ +public interface HttpClientRequestFactory { + HttpClientRequestFactory DEFAULT = (invocation, httpClient, options) -> httpClient.request(options); + + Future create(Invocation invocation, HttpClient httpClient, RequestOptions options) + throws Throwable; +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpTransportHttpClientOptionsSPI.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpTransportHttpClientOptionsSPI.java new file mode 100644 index 00000000000..1091167ceed --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpTransportHttpClientOptionsSPI.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.client; + +import org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI; + +import io.vertx.core.VertxOptions; +import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.HttpVersion; +import io.vertx.core.http.PoolOptions; + +public class HttpTransportHttpClientOptionsSPI implements HttpClientOptionsSPI { + public static final String CLIENT_NAME = "http-transport-client"; + + public static final String CLIENT_TAG = "rest.consumer"; + + @Override + public String clientName() { + return CLIENT_NAME; + } + + @Override + public int getOrder() { + return 100; + } + + @Override + public boolean enabled() { + return TransportClientConfig.isHttpTransportClientEnabled(); + } + + @Override + public String getConfigTag() { + return CLIENT_TAG; + } + + @Override + public int getEventLoopPoolSize() { + // not reading this, using shared transport vert.x + return -1; + } + + @Override + public boolean useSharedVertx() { + return true; + } + + @Override + public int getInstanceCount() { + return TransportClientConfig.getThreadCount(); + } + + @Override + public boolean isWorker() { + return false; + } + + @Override + public String getWorkerPoolName() { + return "pool-worker-transport-client-http"; + } + + @Override + public int getWorkerPoolSize() { + return VertxOptions.DEFAULT_WORKER_POOL_SIZE; + } + + @Override + public HttpVersion getHttpVersion() { + return HttpVersion.HTTP_1_1; + } + + @Override + public int getConnectTimeoutInMillis() { + return TransportClientConfig.getConnectionTimeoutInMillis(); + } + + @Override + public int getIdleTimeoutInSeconds() { + return TransportClientConfig.getConnectionIdleTimeoutInSeconds(); + } + + @Override + public boolean isTryUseCompression() { + return TransportClientConfig.getConnectionCompression(); + } + + @Override + public int getMaxWaitQueueSize() { + return TransportClientConfig.getMaxWaitQueueSize(); + } + + @Override + public int getMaxPoolSize() { + return TransportClientConfig.getConnectionMaxPoolSize(); + } + + @Override + public boolean isKeepAlive() { + return TransportClientConfig.getConnectionKeepAlive(); + } + + @Override + public int getMaxHeaderSize() { + return TransportClientConfig.getMaxHeaderSize(); + } + + @Override + public int getKeepAliveTimeout() { + return TransportClientConfig.getConnectionKeepAliveTimeoutInSeconds(); + } + + @Override + public boolean enableLogActivity() { + return TransportClientConfig.enableLogActivity(); + } + + @Override + public int getHttp2MultiplexingLimit() { + return HttpClientOptions.DEFAULT_HTTP2_MULTIPLEXING_LIMIT; + } + + @Override + public int getHttp2MaxPoolSize() { + return PoolOptions.DEFAULT_HTTP2_MAX_POOL_SIZE; + } + + @Override + public boolean isUseAlpn() { + return !HttpClientOptions.DEFAULT_ALPN_VERSIONS.isEmpty(); + } + + @Override + public boolean isProxyEnable() { + // now transport proxy not implemented + return false; + } + + @Override + public String getProxyHost() { + return null; + } + + @Override + public int getProxyPort() { + return 0; + } + + @Override + public String getProxyUsername() { + return null; + } + + @Override + public String getProxyPassword() { + return null; + } + + @Override + public boolean isSsl() { + return true; + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientCodecFilter.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientCodecFilter.java new file mode 100644 index 00000000000..72ff3f00e85 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientCodecFilter.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.transport.rest.client; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +import org.apache.servicecomb.common.rest.RestConst; +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.Response; +import org.springframework.beans.factory.annotation.Autowired; + +import io.vertx.core.http.HttpClientRequest; + +public class RestClientCodecFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { + public static final String NAME = "rest-client-codec"; + + protected RestClientTransportContextFactory transportContextFactory; + + protected RestClientEncoder encoder; + + protected RestClientDecoder decoder; + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean enabledForTransport(String transport) { + return CoreConst.RESTFUL.equals(transport); + } + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1990; + } + + @Autowired + public RestClientCodecFilter setTransportContextFactory(RestClientTransportContextFactory transportContextFactory) { + this.transportContextFactory = transportContextFactory; + return this; + } + + @Autowired + public RestClientCodecFilter setEncoder(RestClientEncoder encoder) { + this.encoder = encoder; + return this; + } + + @Autowired + public RestClientCodecFilter setDecoder(RestClientDecoder decoder) { + this.decoder = decoder; + return this; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + invocation.getInvocationStageTrace().startConsumerConnection(); + CompletionStage createRequest = + transportContextFactory.createHttpClientRequest(invocation).toCompletionStage() + .whenComplete((c, e) -> invocation.getInvocationStageTrace().finishConsumerConnection()); + return CompletableFuture.completedFuture(null) + .thenCompose(v -> createRequest) + .thenAccept(httpClientRequest -> prepareTransportContext(invocation, httpClientRequest)) + .thenAccept(v -> invocation.onStartSendRequest()) + .thenAccept(v -> encoder.encode(invocation)) + .thenCompose(v -> nextNode.onFilter(invocation)) + .thenApply(response -> decoder.decode(invocation, response)); + } + + protected void prepareTransportContext(Invocation invocation, HttpClientRequest httpClientRequest) { + copyExtraHttpHeaders(invocation, httpClientRequest); + + RestClientTransportContext transportContext = transportContextFactory.create(invocation, httpClientRequest); + invocation.setTransportContext(transportContext); + } + + @SuppressWarnings("unchecked") + protected void copyExtraHttpHeaders(Invocation invocation, HttpClientRequest httpClientRequest) { + Map httpHeaders = (Map) invocation.getHandlerContext() + .get(RestConst.CONSUMER_HEADER); + if (httpHeaders == null) { + return; + } + httpHeaders.forEach((key, value) -> { + if ("Content-Length".equalsIgnoreCase(key)) { + return; + } + if (null != value) { + httpClientRequest.putHeader(key, value); + } + }); + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientDecoder.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientDecoder.java new file mode 100644 index 00000000000..a2534d85450 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientDecoder.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.transport.rest.client; + +import static org.apache.servicecomb.transport.rest.client.RestClientExceptionCodes.FAILED_TO_DECODE_REST_FAIL_RESPONSE; +import static org.apache.servicecomb.transport.rest.client.RestClientExceptionCodes.FAILED_TO_DECODE_REST_SUCCESS_RESPONSE; + +import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; +import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.exception.Exceptions; +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.fasterxml.jackson.databind.JavaType; + +import io.reactivex.rxjava3.core.Flowable; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClientRequest; +import jakarta.ws.rs.core.HttpHeaders; + +public class RestClientDecoder { + private static final Logger LOGGER = LoggerFactory.getLogger(RestClientDecoder.class); + + public Response decode(Invocation invocation, Response response) { + invocation.getInvocationStageTrace().startConsumerDecodeResponse(); + if (response.getResult() instanceof Buffer) { + Object result = extractBody(invocation, response, response.getResult()); + response.setResult(result); + + if (response.isFailed()) { + throw Exceptions.create(response.getStatus(), response.getResult()); + } + } + + if (response.getResult() instanceof Flowable) { + Flowable flowable = response.getResult(); + Flowable encoded = flowable.map(buffer -> extractBody(invocation, response, buffer)); + response.setResult(encoded); + } + + invocation.getInvocationStageTrace().finishConsumerDecodeResponse(); + return response; + } + + protected Object extractBody(Invocation invocation, Response response, Buffer buffer) { + ProduceProcessor produceProcessor = safeFindProduceProcessor(invocation, response); + JavaType responseType = invocation.findResponseType(response.getStatusCode()); + + try { + return produceProcessor.decodeResponse(buffer, responseType); + } catch (Exception e) { + throw createDecodeException(invocation, response, e); + } + } + + private ProduceProcessor safeFindProduceProcessor(Invocation invocation, Response response) { + String contentType = extractContentType(response); + ProduceProcessor produceProcessor = + ProduceProcessorManager.INSTANCE.createProduceProcessor(invocation.getOperationMeta(), response.getStatusCode(), + contentType, null); + if (produceProcessor != null) { + return produceProcessor; + } + + RestClientTransportContext transportContext = invocation.getTransportContext(); + HttpClientRequest httpClientRequest = transportContext.getHttpClientRequest(); + LOGGER.warn( + "operation={}, method={}, endpoint={}, uri={}, statusCode={}, reasonPhrase={}, response content-type={} is not supported in operation.", + invocation.getMicroserviceQualifiedName(), + httpClientRequest.getMethod(), + invocation.getEndpoint().getEndpoint(), + httpClientRequest.getURI(), + response.getStatusCode(), + response.getReasonPhrase(), + response.getHeader(HttpHeaders.CONTENT_TYPE)); + + // This happens outside the runtime such as Servlet filter response. Here we give a default json parser to it + // and keep user data not get lost. + return ProduceProcessorManager.INSTANCE.findDefaultProcessor(); + } + + protected String extractContentType(Response response) { + String contentType = response.getHeader(HttpHeaders.CONTENT_TYPE); + if (contentType == null) { + return null; + } + + int idx = contentType.indexOf(";"); + return idx == -1 ? contentType : contentType.substring(0, idx); + } + + protected InvocationException createDecodeException(Invocation invocation, Response response, Exception e) { + RestClientTransportContext transportContext = invocation.getTransportContext(); + HttpClientRequest httpClientRequest = transportContext.getHttpClientRequest(); + + LOGGER.warn("failed to decode response body, " + + "operation={}, method={}, endpoint={}, uri={}, " + + "statusCode={}, reasonPhrase={}, content-type={}.", + invocation.getMicroserviceQualifiedName(), + httpClientRequest.getMethod(), + invocation.getEndpoint().getEndpoint(), + httpClientRequest.getURI(), + response.getStatusCode(), + response.getReasonPhrase(), + response.getHeader(HttpHeaders.CONTENT_TYPE)); + + if (response.isSucceed()) { + return Exceptions.consumer(FAILED_TO_DECODE_REST_SUCCESS_RESPONSE, "failed to decode success response body.", e); + } + return Exceptions.consumer(FAILED_TO_DECODE_REST_FAIL_RESPONSE, "failed to decode fail response body.", e); + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientEncoder.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientEncoder.java new file mode 100644 index 00000000000..cc16c832f08 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientEncoder.java @@ -0,0 +1,243 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.transport.rest.client; + +import static jakarta.ws.rs.core.HttpHeaders.CONTENT_TYPE; +import static jakarta.ws.rs.core.MediaType.MULTIPART_FORM_DATA; +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; +import static org.apache.servicecomb.transport.rest.client.RestClientExceptionCodes.FAILED_TO_ENCODE_REST_CLIENT_REQUEST; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.servicecomb.common.rest.codec.RestCodec; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.common.rest.codec.query.QueryCodec; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.OperationConfig; +import org.apache.servicecomb.foundation.common.utils.StringBuilderUtils; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClientRequest; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; + +/** + * encode all send data except upload + */ +public class RestClientEncoder { + private static final Logger LOGGER = LoggerFactory.getLogger(RestClientEncoder.class); + + public static final int FORM_BUFFER_SIZE = 1024; + + public void encode(Invocation invocation) { + try { + invocation.getInvocationStageTrace().startConsumerEncodeRequest(); + EncoderSession encoderSession = new EncoderSession(invocation); + encoderSession.doEncode(); + invocation.getInvocationStageTrace().finishConsumerEncodeRequest(); + } catch (Exception e) { + throw new InvocationException(BAD_REQUEST, FAILED_TO_ENCODE_REST_CLIENT_REQUEST, e.getMessage(), e); + } + } + + public static class EncoderSession { + protected final Invocation invocation; + + protected final RestClientTransportContext transportContext; + + protected final RestClientRequestParameters requestParameters; + + protected final HttpClientRequest httpClientRequest; + + public EncoderSession(Invocation invocation) { + this.invocation = invocation; + this.transportContext = invocation.getTransportContext(); + this.requestParameters = this.transportContext.getRequestParameters(); + this.httpClientRequest = this.transportContext.getHttpClientRequest(); + } + + protected void doEncode() throws Exception { + RestClientEncoder.LOGGER.debug("encode rest client request, operation={}, method={}, endpoint={}, uri={}.", + invocation.getMicroserviceQualifiedName(), + httpClientRequest.getMethod(), + invocation.getEndpoint().getEndpoint(), + httpClientRequest.getURI()); + + swaggerArgumentsToRequest(); + + writeCookies(requestParameters.getCookieMap()); + writeScbHeaders(); + writeForm(requestParameters.getFormMap()); + } + + protected void swaggerArgumentsToRequest() throws Exception { + RestCodec.argsToRest(invocation.getSwaggerArguments(), + transportContext.getRestOperationMeta(), requestParameters); + } + + protected void writeCookies(Map cookieMap) { + if (CollectionUtils.isEmpty(cookieMap)) { + return; + } + + StringBuilder builder = new StringBuilder(); + for (Entry entry : cookieMap.entrySet()) { + builder.append(entry.getKey()) + .append('=') + .append(entry.getValue()) + .append("; "); + } + StringBuilderUtils.deleteLast(builder, 2); + httpClientRequest.putHeader(HttpHeaders.COOKIE, builder.toString()); + } + + protected void writeScbHeaders() throws JsonProcessingException { + OperationConfig operationConfig = invocation.getOperationMeta().getConfig(); + if (operationConfig.isClientRequestHeaderFilterEnabled()) { + return; + } + + httpClientRequest.putHeader(CoreConst.TARGET_MICROSERVICE, invocation.getMicroserviceName()); + httpClientRequest.putHeader(CoreConst.CSE_CONTEXT, + RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(invocation.getContext())); + } + + protected void writeForm(Map formMap) throws Exception { + if (requestParameters.getUploads() == null) { + writeUrlEncodedForm(formMap); + return; + } + + writeChunkedForm(formMap); + } + + protected void writeUrlEncodedForm(Map formMap) throws Exception { + if (formMap == null) { + return; + } + + httpClientRequest.putHeader(CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED); + + Buffer bodyBuffer = genUrlEncodedFormBuffer(formMap); + requestParameters.setBodyBuffer(bodyBuffer); + } + + protected Buffer genUrlEncodedFormBuffer(Map formMap) throws Exception { + Buffer buffer = Buffer.buffer(RestClientEncoder.FORM_BUFFER_SIZE); + for (Entry entry : formMap.entrySet()) { + writeCharSequence(buffer, entry.getKey()); + buffer.appendByte(((byte) '=')); + + String value = QueryCodec.convertToString(entry.getValue()); + String encodedValue = URLEncoder.encode(value, StandardCharsets.UTF_8); + writeCharSequence(buffer, encodedValue); + + buffer.appendByte(((byte) '&')); + } + + return buffer; + } + + protected void writeChunkedForm(Map formMap) throws Exception { + String boundary = transportContext.getOrCreateBoundary(); + + httpClientRequest.setChunked(true); + httpClientRequest.putHeader(CONTENT_TYPE, MULTIPART_FORM_DATA + "; charset=UTF-8; boundary=" + boundary); + + if (formMap == null) { + return; + } + + Buffer bodyBuffer = genChunkedFormBuffer(formMap, boundary); + requestParameters.setBodyBuffer(bodyBuffer); + } + + protected Buffer genChunkedFormBuffer(Map formMap, String boundary) throws Exception { + Buffer buffer = Buffer.buffer(RestClientEncoder.FORM_BUFFER_SIZE); + for (Entry entry : formMap.entrySet()) { + Object content = entry.getValue(); + if (content instanceof List) { + for (Object item : ((List) content)) { + writeFormData(buffer, boundary, entry.getKey(), item); + } + } else { + writeFormData(buffer, boundary, entry.getKey(), entry.getValue()); + } + } + return buffer; + } + + private void writeFormData(Buffer buffer, String boundary, String key, Object data) throws Exception { + writeCharSequence(buffer, "\r\n--"); + writeCharSequence(buffer, boundary); + writeCharSequence(buffer, "\r\nContent-Disposition: form-data; name=\""); + writeCharSequence(buffer, key); + writeCharSequence(buffer, "\"\r\n\r\n"); + + String value = QueryCodec.convertToString(data); + writeCharSequence(buffer, value); + } + } + + protected static void writeCharSequence(Buffer buffer, String value) { + buffer.appendString(value, "UTF-8"); + } + + public static Buffer genFileBoundaryBuffer(Part part, String name, String boundary) { + Buffer buffer = Buffer.buffer(RestClientEncoder.FORM_BUFFER_SIZE); + + writeCharSequence(buffer, "\r\n--"); + writeCharSequence(buffer, boundary); + writeCharSequence(buffer, "\r\nContent-Disposition: form-data; name=\""); + writeCharSequence(buffer, name); + writeCharSequence(buffer, "\"; filename=\""); + writeCharSequence(buffer, String.valueOf(part.getSubmittedFileName())); + writeCharSequence(buffer, "\"\r\n"); + + writeCharSequence(buffer, "Content-Type: "); + writeCharSequence(buffer, part.getContentType()); + writeCharSequence(buffer, "\r\n"); + + writeCharSequence(buffer, "Content-Transfer-Encoding: binary\r\n"); + + writeCharSequence(buffer, "\r\n"); + + return buffer; + } + + public static Buffer genBoundaryEndBuffer(String boundary) { + Buffer buffer = Buffer.buffer(RestClientEncoder.FORM_BUFFER_SIZE); + + writeCharSequence(buffer, "\r\n--"); + writeCharSequence(buffer, boundary); + writeCharSequence(buffer, "--\r\n"); + + return buffer; + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientExceptionCodes.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientExceptionCodes.java new file mode 100644 index 00000000000..9c99683f669 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientExceptionCodes.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.transport.rest.client; + +public interface RestClientExceptionCodes { + String FAILED_TO_CREATE_REST_CLIENT_TRANSPORT_CONTEXT = "scb_rest_client.40000000"; + + String FAILED_TO_ENCODE_REST_CLIENT_REQUEST = "scb_rest_client.40000001"; + + String FAILED_TO_DECODE_REST_SUCCESS_RESPONSE = "scb_rest_client.40000002"; + + String FAILED_TO_DECODE_REST_FAIL_RESPONSE = "scb_rest_client.40000003"; +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientRequestParameters.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientRequestParameters.java new file mode 100644 index 00000000000..5031250b51f --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientRequestParameters.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.transport.rest.client; + +import java.util.Map; + +import jakarta.servlet.http.Part; + +import org.apache.servicecomb.common.rest.codec.RestClientRequest; + +import com.google.common.collect.Multimap; + +import io.vertx.core.Future; +import io.vertx.core.buffer.Buffer; + +public interface RestClientRequestParameters extends RestClientRequest { + Map getCookieMap(); + + Map getFormMap(); + + Multimap getUploads(); + + @Override + Buffer getBodyBuffer(); + + void setBodyBuffer(Buffer bodyBuffer); + + @Override + default void write(Buffer bodyBuffer) { + setBodyBuffer(bodyBuffer); + } + + @Override + default Future end() { + throw new UnsupportedOperationException("should not invoke this method"); + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientRequestParametersImpl.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientRequestParametersImpl.java new file mode 100644 index 00000000000..398bde4f402 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientRequestParametersImpl.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.transport.rest.client; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.utils.PartUtils; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + +import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClientRequest; +import jakarta.servlet.http.Part; + +public class RestClientRequestParametersImpl implements RestClientRequestParameters { + protected final HttpClientRequest httpClientRequest; + + protected final MultiMap headers; + + protected Map cookieMap; + + protected Map formMap; + + protected Multimap uploads; + + protected Buffer bodyBuffer; + + public RestClientRequestParametersImpl(HttpClientRequest httpClientRequest) { + this.httpClientRequest = httpClientRequest; + this.headers = this.httpClientRequest.headers(); + } + + @Override + 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 Map getFormMap() { + return formMap; + } + + @Override + public void addForm(String name, Object value) { + if (formMap == null) { + formMap = new HashMap<>(); + } + + if (value != null) { + formMap.put(name, value); + } + } + + @Override + public MultiMap getHeaders() { + return headers; + } + + @Override + public void putHeader(String name, String value) { + headers.add(name, value); + } + + @Override + public Buffer getBodyBuffer() { + return bodyBuffer; + } + + @Override + public void setBodyBuffer(Buffer bodyBuffer) { + this.bodyBuffer = bodyBuffer; + } + + @Override + public Multimap getUploads() { + return uploads; + } + + @SuppressWarnings("unchecked") + @Override + public void attach(String name, Object partOrList) { + if (uploads == null) { + uploads = ArrayListMultimap.create(); + } + + if (partOrList == null) { + return; + } + + if (partOrList.getClass().isArray()) { + for (Object part : (Object[]) partOrList) { + uploads.put(name, PartUtils.getSinglePart(name, part)); + } + return; + } + + if (partOrList instanceof Collection) { + for (Object part : ((Collection) partOrList)) { + uploads.put(name, PartUtils.getSinglePart(name, part)); + } + return; + } + + uploads.put(name, PartUtils.getSinglePart(name, partOrList)); + } + + @Override + public HttpClientRequest getHttpClientRequest() { + return this.httpClientRequest; + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSender.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSender.java new file mode 100644 index 00000000000..b737736c243 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSender.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.transport.rest.client; + +import static org.apache.servicecomb.transport.rest.client.RestClientEncoder.genBoundaryEndBuffer; +import static org.apache.servicecomb.transport.rest.client.RestClientEncoder.genFileBoundaryBuffer; + +import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.common.http.HttpStatus; +import org.apache.servicecomb.foundation.vertx.executor.VertxContextExecutor; +import org.apache.servicecomb.foundation.vertx.http.ReadStreamPart; +import org.apache.servicecomb.foundation.vertx.stream.PumpFromPart; +import org.apache.servicecomb.swagger.invocation.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Multimap; + +import io.vertx.core.Future; +import io.vertx.core.MultiMap; +import io.vertx.core.Promise; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; +import io.vertx.rxjava3.FlowableHelper; +import jakarta.servlet.http.Part; + +public class RestClientSender { + private static final Logger LOGGER = LoggerFactory.getLogger(RestClientSender.class); + + protected final Invocation invocation; + + protected final RestClientTransportContext transportContext; + + protected final RestClientRequestParameters requestParameters; + + protected final HttpClientRequest httpClientRequest; + + protected final CompletableFuture future = new CompletableFuture<>(); + + public RestClientSender(Invocation invocation) { + this.invocation = invocation; + this.transportContext = invocation.getTransportContext(); + this.requestParameters = transportContext.getRequestParameters(); + this.httpClientRequest = transportContext.getHttpClientRequest(); + } + + public CompletableFuture send() { + invocation.getInvocationStageTrace().startConsumerSendRequest(); + httpClientRequest.response().compose(this::processResponse).onFailure(future::completeExceptionally); + + CompletableFuture actualFuture = future.whenComplete(this::afterSend); + VertxContextExecutor.create(transportContext.getVertxContext()).execute(this::runInVertxContext); + return actualFuture; + } + + protected void runInVertxContext() { + sendInVertxContext() + .whenComplete((v, e) -> { + if (e != null) { + future.completeExceptionally(e); + } + invocation.getInvocationStageTrace().finishConsumerSendRequest(); + invocation.getInvocationStageTrace().startWaitResponse(); + }); + } + + protected CompletableFuture sendInVertxContext() { + httpClientRequest.idleTimeout(invocation.getOperationMeta().getConfig().getMsRequestTimeout()); + + Multimap uploads = requestParameters.getUploads(); + if (uploads == null) { + if (requestParameters.getBodyBuffer() != null) { + return CompletableFuture.completedFuture(null).thenCompose( + v -> httpClientRequest.end(requestParameters.getBodyBuffer()).toCompletionStage()); + } else { + return CompletableFuture.completedFuture(null).thenCompose( + v -> httpClientRequest.end().toCompletionStage()); + } + } + + if (requestParameters.getBodyBuffer() != null) { + httpClientRequest.write(requestParameters.getBodyBuffer()); + } + return sendFiles(); + } + + protected CompletableFuture sendFiles() { + CompletableFuture sendFileFuture = CompletableFuture.completedFuture(null); + + String boundary = transportContext.getOrCreateBoundary(); + for (Entry entry : requestParameters.getUploads().entries()) { + // do not use part.getName() to get parameter name + // because pojo consumer not easy to set name to part + String name = entry.getKey(); + sendFileFuture = sendFileFuture.thenCompose(v -> sendFile(entry.getValue(), name, boundary)); + } + + return sendFileFuture.thenCompose(v -> httpClientRequest.end(genBoundaryEndBuffer(boundary)).toCompletionStage()); + } + + private CompletableFuture sendFile(Part part, String name, String boundary) { + Buffer fileHeader = genFileBoundaryBuffer(part, name, boundary); + httpClientRequest.write(fileHeader); + + return new PumpFromPart(transportContext.getVertxContext(), part) + .toWriteStream(httpClientRequest, future::completeExceptionally) + .whenComplete((v, e) -> { + if (e != null) { + LOGGER.debug("Failed to send file [{}:{}].", name, part.getSubmittedFileName(), e); + return; + } + + LOGGER.debug("finish send file [{}:{}].", name, part.getSubmittedFileName()); + }); + } + + protected Future processResponse(HttpClientResponse httpClientResponse) { + Promise result = Promise.promise(); + transportContext.setHttpClientResponse(httpClientResponse); + + if (!HttpStatus.isSuccess(httpClientResponse.statusCode())) { + httpClientResponse.body().compose(buffer -> { + future.complete(createResponse(httpClientResponse, buffer)); + result.complete(); + return Future.succeededFuture(); + }); + return result.future(); + } + + if (transportContext.isDownloadFile()) { + ReadStreamPart streamPart = new ReadStreamPart(transportContext.getVertxContext(), httpClientResponse); + future.complete(createResponse(httpClientResponse, streamPart)); + result.complete(); + return result.future(); + } + + if (transportContext.isServerSendEvents()) { + future.complete(createResponse(httpClientResponse, FlowableHelper.toFlowable(httpClientResponse))); + result.complete(); + return result.future(); + } + + httpClientResponse.body().compose(buffer -> { + future.complete(createResponse(httpClientResponse, buffer)); + result.complete(); + return Future.succeededFuture(); + }); + return result.future(); + } + + protected Response createResponse(HttpClientResponse httpClientResponse, Object result) { + // http2's :status header will cause edge forward failed + MultiMap headers = httpClientResponse.headers(); + headers.remove(":status"); + + HttpStatus httpStatus = new HttpStatus(httpClientResponse.statusCode(), httpClientResponse.statusMessage()); + return Response + .status(httpStatus) + .setHeaders(headers) + .entity(result); + } + + protected void afterSend(Response response, Throwable throwable) { + invocation.getInvocationStageTrace().finishWaitResponse(); + + if (throwable != null) { + LOGGER.error("rest client send or receive failed, operation={}, method={}, endpoint={}, uri={}.", + invocation.getMicroserviceQualifiedName(), + httpClientRequest.getMethod(), + invocation.getEndpoint().getEndpoint(), + httpClientRequest.getURI()); + } + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSenderFilter.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSenderFilter.java new file mode 100644 index 00000000000..cf088c764eb --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSenderFilter.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.transport.rest.client; + +import java.util.concurrent.CompletableFuture; + +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.Response; + +public class RestClientSenderFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { + public static final String NAME = "rest-client-sender"; + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean enabledForTransport(String transport) { + return CoreConst.RESTFUL.equals(transport); + } + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 2000; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + CompletableFuture future = new RestClientSender(invocation) + .send(); + + return invocation.optimizeSyncConsumerThread(future); + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContext.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContext.java new file mode 100644 index 00000000000..5648588a714 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContext.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.transport.rest.client; + +import java.util.Optional; + +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +import org.apache.servicecomb.swagger.invocation.context.VertxTransportContext; + +import io.vertx.core.Context; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; +import io.vertx.core.http.HttpConnection; + +public class RestClientTransportContext implements VertxTransportContext { + protected final RestOperationMeta restOperationMeta; + + protected final Context vertxContext; + + protected final HttpClientRequest httpClientRequest; + + protected final RestClientRequestParameters requestParameters; + + protected final BoundaryFactory boundaryFactory; + + protected String boundary; + + protected HttpClientResponse httpClientResponse; + + public RestClientTransportContext(RestOperationMeta restOperationMeta, Context vertxContext, + HttpClientRequest httpClientRequest, BoundaryFactory boundaryFactory) { + this.restOperationMeta = restOperationMeta; + this.vertxContext = vertxContext; + this.httpClientRequest = httpClientRequest; + this.boundaryFactory = boundaryFactory; + this.requestParameters = new RestClientRequestParametersImpl(httpClientRequest); + } + + public RestOperationMeta getRestOperationMeta() { + return restOperationMeta; + } + + public boolean isDownloadFile() { + return restOperationMeta.isDownloadFile(); + } + + public boolean isServerSendEvents() { + return restOperationMeta.isServerSendEvents(); + } + + @Override + public Context getVertxContext() { + return vertxContext; + } + + public HttpClientRequest getHttpClientRequest() { + return httpClientRequest; + } + + public RestClientRequestParameters getRequestParameters() { + return requestParameters; + } + + public String getOrCreateBoundary() { + if (boundary == null) { + boundary = boundaryFactory.create(); + } + + return boundary; + } + + public HttpClientResponse getHttpClientResponse() { + return httpClientResponse; + } + + public RestClientTransportContext setHttpClientResponse(HttpClientResponse httpClientResponse) { + this.httpClientResponse = httpClientResponse; + return this; + } + + public String getLocalAddress() { + return Optional.ofNullable(httpClientRequest.connection()) + .map(HttpConnection::localAddress) + .map(Object::toString) + .orElse("not connected"); + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContextFactory.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContextFactory.java new file mode 100644 index 00000000000..a4b0fc12dcf --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContextFactory.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.transport.rest.client; + +import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; +import static org.apache.servicecomb.transport.rest.client.RestClientExceptionCodes.FAILED_TO_CREATE_REST_CLIENT_TRANSPORT_CONTEXT; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.common.rest.definition.RestMetaUtils; +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +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.registry.definition.DefinitionConst; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.beans.factory.annotation.Autowired; + +import io.vertx.core.Future; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.RequestOptions; + +public class RestClientTransportContextFactory { + private BoundaryFactory boundaryFactory = BoundaryFactory.DEFAULT; + + private HttpClientRequestFactory httpClientRequestFactory = HttpClientRequestFactory.DEFAULT; + + @Autowired(required = false) + public RestClientTransportContextFactory setBoundaryFactory(BoundaryFactory boundaryFactory) { + this.boundaryFactory = boundaryFactory; + return this; + } + + @Autowired(required = false) + public RestClientTransportContextFactory setHttpClientRequestFactory(HttpClientRequestFactory factory) { + this.httpClientRequestFactory = factory; + return this; + } + + public RestClientTransportContext create(Invocation invocation, HttpClientRequest httpClientRequest) { + try { + return doCreate(invocation, httpClientRequest); + } catch (Throwable e) { + throw new InvocationException(BAD_REQUEST, FAILED_TO_CREATE_REST_CLIENT_TRANSPORT_CONTEXT, e.getMessage(), e); + } + } + + protected RestClientTransportContext doCreate(Invocation invocation, HttpClientRequest httpClientRequest) + throws Throwable { + RestOperationMeta restOperationMeta = RestMetaUtils.getRestOperationMeta(invocation.getOperationMeta()); + HttpClientWithContext httpClientWithContext = findHttpClientPool(invocation); + RestClientTransportContext context = new RestClientTransportContext(restOperationMeta, + httpClientWithContext.context(), + httpClientRequest, + boundaryFactory); + invocation.getHandlerContext().put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, + context.getRequestParameters()); + return context; + } + + protected HttpClientWithContext findHttpClientPool(Invocation invocation) { + URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); + if (endpoint.isHttp2Enabled()) { + return HttpClients.getClient(Http2TransportHttpClientOptionsSPI.CLIENT_NAME, invocation.isSync()); + } + + return HttpClients.getClient(HttpTransportHttpClientOptionsSPI.CLIENT_NAME, invocation.isSync()); + } + + protected Future createHttpClientRequest(Invocation invocation) { + try { + RestOperationMeta restOperationMeta = RestMetaUtils.getRestOperationMeta(invocation.getOperationMeta()); + HttpClientWithContext httpClientWithContext = findHttpClientPool(invocation); + + URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); + HttpMethod method = HttpMethod.valueOf(restOperationMeta.getHttpMethod()); + RequestOptions requestOptions = new RequestOptions() + .setHost(endpoint.getHostOrIp()) + .setPort(endpoint.getPort()) + .setSsl(endpoint.isSslEnabled()) + .setMethod(method) + .setURI(createRequestPath(invocation, restOperationMeta)); + return httpClientRequestFactory.create(invocation, httpClientWithContext.getHttpClient(), requestOptions); + } catch (Throwable e) { + throw new InvocationException(BAD_REQUEST, FAILED_TO_CREATE_REST_CLIENT_TRANSPORT_CONTEXT, e.getMessage(), e); + } + } + + protected String createRequestPath(Invocation invocation, RestOperationMeta restOperationMeta) throws Exception { + String path = invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH); + if (path == null) { + path = restOperationMeta.getPathBuilder().createRequestPath(invocation.getSwaggerArguments()); + } + + URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); + String urlPrefix = endpoint.getFirst(DefinitionConst.URL_PREFIX); + if (StringUtils.isEmpty(urlPrefix) || path.startsWith(urlPrefix)) { + return path; + } + + return urlPrefix + path; + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportClientConfig.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportClientConfig.java new file mode 100644 index 00000000000..e1f2c6042d1 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportClientConfig.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.client; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.transport.common.TransportConfigUtils; + +import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.PoolOptions; + +public final class TransportClientConfig { + private static final int DEFAULT_IDLE_TIME_OUT = 150; + + private static final int DEFAULT_KEEP_ALIVE_TIME_OUT = 60; + + private TransportClientConfig() { + } + + public static int getThreadCount() { + return TransportConfigUtils.readVerticleCount( + "servicecomb.rest.client.verticle-count", + "servicecomb.rest.client.thread-count"); + } + + public static int getHttp2ConnectionMaxPoolSize() { + return LegacyPropertyFactory.getIntProperty("servicecomb.rest.client.http2.maxPoolSize", + PoolOptions.DEFAULT_HTTP2_MAX_POOL_SIZE); + } + + public static int getHttp2MultiplexingLimit() { + return LegacyPropertyFactory.getIntProperty("servicecomb.rest.client.http2.multiplexingLimit", + HttpClientOptions.DEFAULT_HTTP2_MULTIPLEXING_LIMIT); + } + + public static boolean getUseAlpn() { + return LegacyPropertyFactory.getBooleanProperty("servicecomb.rest.client.http2.useAlpnEnabled", true); + } + + public static boolean isHttp2TransportClientEnabled() { + return LegacyPropertyFactory.getBooleanProperty("servicecomb.rest.client.http2.enabled", + true); + } + + public static int getConnectionMaxPoolSize() { + return LegacyPropertyFactory.getIntProperty("servicecomb.rest.client.connection.maxPoolSize", + PoolOptions.DEFAULT_MAX_POOL_SIZE); + } + + public static int getHttp2ConnectionIdleTimeoutInSeconds() { + return LegacyPropertyFactory.getIntProperty("servicecomb.rest.client.http2.connection.idleTimeoutInSeconds", + DEFAULT_IDLE_TIME_OUT); + } + + public static int getConnectionIdleTimeoutInSeconds() { + return LegacyPropertyFactory.getIntProperty("servicecomb.rest.client.connection.idleTimeoutInSeconds", + DEFAULT_IDLE_TIME_OUT); + } + + public static boolean getConnectionKeepAlive() { + return LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.client.connection.keepAlive", HttpClientOptions.DEFAULT_KEEP_ALIVE); + } + + public static int getConnectionKeepAliveTimeoutInSeconds() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.client.connection.keepAliveTimeoutInSeconds", DEFAULT_KEEP_ALIVE_TIME_OUT); + } + + public static boolean enableLogActivity() { + return LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.client.enableLogActivity", false); + } + + public static int getHttp2ConnectionKeepAliveTimeoutInSeconds() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.client.http2.connection.keepAliveTimeoutInSeconds", + DEFAULT_KEEP_ALIVE_TIME_OUT); + } + + public static boolean getConnectionCompression() { + return LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.client.connection.compression", + HttpClientOptions.DEFAULT_DECOMPRESSION_SUPPORTED); + } + + public static int getMaxHeaderSize() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.client.maxHeaderSize", HttpClientOptions.DEFAULT_MAX_HEADER_SIZE); + } + + public static int getMaxWaitQueueSize() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.client.maxWaitQueueSize", + PoolOptions.DEFAULT_MAX_WAIT_QUEUE_SIZE); + } + + public static boolean isHttpTransportClientEnabled() { + return LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.client.enabled", + true); + } + + public static int getConnectionTimeoutInMillis() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.client.connection.timeoutInMillis", 60000); + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportRestClientConfiguration.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportRestClientConfiguration.java new file mode 100644 index 00000000000..3775744e173 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportRestClientConfiguration.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.transport.rest.client; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TransportRestClientConfiguration { + @Bean + public RestClientSenderFilter restClientSenderFilter() { + return new RestClientSenderFilter(); + } + + @Bean + public RestClientCodecFilter restClientCodecFilter() { + return new RestClientCodecFilter(); + } + + @Bean + public WebSocketClientCodecFilter webSocketClientCodecFilter() { + return new WebSocketClientCodecFilter(); + } + + @Bean + public RestClientDecoder restClientDecoder() { + return new RestClientDecoder(); + } + + @Bean + public RestClientTransportContextFactory restClientTransportContextFactory() { + return new RestClientTransportContextFactory(); + } + + @Bean + public RestClientEncoder restClientEncoder() { + return new RestClientEncoder(); + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientCodecFilter.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientCodecFilter.java new file mode 100644 index 00000000000..73a38dfe8f2 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientCodecFilter.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.client; + +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.common.rest.WebSocketTransportContext; +import org.apache.servicecomb.common.rest.definition.RestMetaUtils; +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +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.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI; +import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; +import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.apache.servicecomb.swagger.invocation.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.http.ServerWebSocket; +import io.vertx.core.http.WebSocket; +import io.vertx.core.http.WebSocketClient; + +public class WebSocketClientCodecFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketClientCodecFilter.class); + + public static final String NAME = "websocket-client"; + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean enabledForTransport(String transport) { + return CoreConst.WEBSOCKET.equals(transport); + } + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 2000; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + invocation.getInvocationStageTrace().startConsumerConnection(); + + CompletableFuture createWebSocket = new CompletableFuture<>(); + URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); + HttpClientOptionsSPI optionsSPI; + if (endpoint.isHttp2Enabled()) { + optionsSPI = SPIServiceUtils.getTargetService(HttpClientOptionsSPI.class, + HttpTransportHttpClientOptionsSPI.class); + } else { + optionsSPI = SPIServiceUtils.getTargetService(HttpClientOptionsSPI.class, + Http2TransportHttpClientOptionsSPI.class); + } + WebSocketClient webSocketClient = HttpClients.createWebSocketClient(optionsSPI, endpoint.isSslEnabled()); + + try { + webSocketClient.connect(endpoint.getPort(), endpoint.getHostOrIp(), createRequestPath(invocation, + RestMetaUtils.getRestOperationMeta(invocation.getOperationMeta()))) + .onComplete(asyncResult -> { + invocation.getInvocationStageTrace().finishConsumerConnection(); + if (asyncResult.failed()) { + createWebSocket.completeExceptionally(asyncResult.cause()); + return; + } + if (invocation.isEdge()) { + WebSocketTransportContext parentContext = invocation.getTransportContext(); + ServerWebSocket serverWebSocket = parentContext.getServerWebSocket(); + WebSocket clientWebSocket = asyncResult.result(); + serverWebSocket.closeHandler(v -> { + if (!clientWebSocket.isClosed()) { + clientWebSocket.close(); + } + }); + serverWebSocket.textMessageHandler(clientWebSocket::writeTextMessage); + serverWebSocket.binaryMessageHandler(clientWebSocket::writeBinaryMessage); + serverWebSocket.exceptionHandler(e -> { + LOGGER.warn("consumer exception.", e); + if (!serverWebSocket.isClosed()) { + serverWebSocket.close(); + } + }); + clientWebSocket.closeHandler(v -> { + if (!serverWebSocket.isClosed()) { + serverWebSocket.close(); + } + }); + clientWebSocket.textMessageHandler(serverWebSocket::writeTextMessage); + clientWebSocket.binaryMessageHandler(serverWebSocket::writeBinaryMessage); + clientWebSocket.exceptionHandler(e -> { + LOGGER.warn("producer exception.", e); + if (!clientWebSocket.isClosed()) { + clientWebSocket.close(); + } + }); + } + invocation.setTransportContext(new WebSocketClientTransportContext( + asyncResult.result())); + createWebSocket.complete(Response.createSuccess(asyncResult.result())); + }); + } catch (Exception e) { + createWebSocket.completeExceptionally(e); + } + + return createWebSocket; + } + + protected String createRequestPath(Invocation invocation, RestOperationMeta restOperationMeta) throws Exception { + String path = invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH); + if (path == null) { + path = restOperationMeta.getPathBuilder().createRequestPath(invocation.getSwaggerArguments()); + } + + URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); + String urlPrefix = endpoint.getFirst(DefinitionConst.URL_PREFIX); + if (StringUtils.isEmpty(urlPrefix) || path.startsWith(urlPrefix)) { + return path; + } + + return urlPrefix + path; + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientTransportContext.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientTransportContext.java new file mode 100644 index 00000000000..89d43bcde41 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientTransportContext.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.transport.rest.client; + +import org.apache.servicecomb.swagger.invocation.context.TransportContext; + +import io.vertx.core.http.WebSocket; + +public class WebSocketClientTransportContext implements TransportContext { + + protected final WebSocket webSocketClient; + + public WebSocketClientTransportContext(WebSocket webSocketClient) { + this.webSocketClient = webSocketClient; + } + + public WebSocket getWebSocketClient() { + return webSocketClient; + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI b/transports/transport-rest/transport-rest-client/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI new file mode 100644 index 00000000000..5eb8c592516 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI @@ -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.transport.rest.client.HttpTransportHttpClientOptionsSPI +org.apache.servicecomb.transport.rest.client.Http2TransportHttpClientOptionsSPI diff --git a/transports/transport-rest/transport-rest-client/src/main/resources/META-INF/spring/cse.transport.rest.bean.xml b/transports/transport-rest/transport-rest-client/src/main/resources/META-INF/spring/cse.transport.rest.bean.xml deleted file mode 100644 index 15e98363984..00000000000 --- a/transports/transport-rest/transport-rest-client/src/main/resources/META-INF/spring/cse.transport.rest.bean.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/transports/transport-rest/transport-rest-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/transports/transport-rest/transport-rest-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..37f7477013c --- /dev/null +++ b/transports/transport-rest/transport-rest-client/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.transport.rest.client.TransportRestClientConfiguration diff --git a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/TestRestTransportClient.java b/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/TestRestTransportClient.java deleted file mode 100644 index 2d566f34951..00000000000 --- a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/TestRestTransportClient.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.foundation.vertx.VertxUtils; -import io.servicecomb.foundation.vertx.client.ClientPoolManager; -import io.servicecomb.foundation.vertx.client.http.HttpClientWithContext; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.transport.rest.client.http.HttpMethodFactory; -import io.servicecomb.transport.rest.client.http.VertxHttpMethod; -import io.vertx.core.AbstractVerticle; -import io.vertx.core.DeploymentOptions; -import io.vertx.core.Vertx; -import io.vertx.core.http.HttpClientOptions; -import mockit.Deencapsulation; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestRestTransportClient { - - private RestTransportClient instance = null; - - private HttpClientOptions options; - - Invocation invocation = Mockito.mock(Invocation.class); - - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - - RestOperationMeta swaggerRestOperation = Mockito.mock(RestOperationMeta.class); - - @Before - public void setUp() throws Exception { - instance = new RestTransportClient(false); - } - - @After - public void tearDown() throws Exception { - instance = null; - } - - @Test - public void testGetInstance() { - Assert.assertNotNull(instance); - } - - @Test - public void testSSL(@Mocked Vertx vertx, @Mocked VertxUtils utils) throws Exception { - new MockUp() { - @Mock - DeploymentOptions createClientDeployOptions( - ClientPoolManager clientMgr, - int instanceCount, - int poolCountPerVerticle, CLIENT_OPTIONS clientOptions) { - options = (HttpClientOptions) clientOptions; - return null; - } - - @Mock - boolean blockDeploy(Vertx vertx, - Class cls, - DeploymentOptions options) throws InterruptedException { - return true; - } - }; - RestTransportClient client = new RestTransportClient(true); - client.init(vertx); - Assert.assertEquals(options.isSsl(), true); - } - - @Test - public void testRestTransportClientException() { - boolean status = true; - Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(operationMeta); - try { - instance.send(invocation, asyncResp); - } catch (Exception e) { - status = false; - } - Assert.assertFalse(status); - } - - @Test - public void testCreateHttpClientOptions() { - - HttpClientOptions obj = (HttpClientOptions) Deencapsulation.invoke(instance, "createHttpClientOptions"); - Assert.assertNotNull(obj); - } - - @Test - public void testSend() { - boolean validAssert; - Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - - new MockUp>() { - - @Mock - public HttpClientWithContext findThreadBindClientPool() { - return new HttpClientWithContext(null, null); - } - }; - - Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerRestOperation); - - try { - validAssert = true; - instance.send(invocation, asyncResp); - } catch (Exception e) { - validAssert = false; - } - Assert.assertTrue(validAssert); - } - - @Test - public void testSendWithVertexMethod() { - boolean validAssert; - Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - - new MockUp>() { - - @Mock - public HttpClientWithContext findThreadBindClientPool() { - return new HttpClientWithContext(null, null); - } - }; - - new MockUp() { - - @Mock - public VertxHttpMethod findHttpMethodInstance(String method) throws Exception { - return Mockito.mock(VertxHttpMethod.class); - } - }; - - Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerRestOperation); - - try { - validAssert = true; - instance.send(invocation, asyncResp); - } catch (Exception e) { - validAssert = false; - } - Assert.assertTrue(validAssert); - } -} diff --git a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/TestRestTransportClientManager.java b/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/TestRestTransportClientManager.java deleted file mode 100644 index 3c8068c3453..00000000000 --- a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/TestRestTransportClientManager.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client; - -import org.junit.Assert; -import org.junit.Test; - -public class TestRestTransportClientManager { - @Test - public void testRestTransportClientManager() { - RestTransportClient client1 = RestTransportClientManager.INSTANCE.getRestTransportClient(false); - RestTransportClient client2 = RestTransportClientManager.INSTANCE.getRestTransportClient(false); - Assert.assertEquals(client1, client2); - - RestTransportClient client3 = RestTransportClientManager.INSTANCE.getRestTransportClient(true); - RestTransportClient client4 = RestTransportClientManager.INSTANCE.getRestTransportClient(true); - Assert.assertEquals(client3, client4); - } -} diff --git a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/TestTransportClientConfig.java b/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/TestTransportClientConfig.java deleted file mode 100644 index 8861dc6c1ce..00000000000 --- a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/TestTransportClientConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client; - -import org.junit.Assert; -import org.junit.Test; - -public class TestTransportClientConfig { - - @Test - public void testGetThreadCount() { - Assert.assertEquals(1, TransportClientConfig.getThreadCount()); - } - - @Test - public void testGetConnectionPoolPerThread() { - Assert.assertEquals(1, TransportClientConfig.getConnectionPoolPerThread()); - } -} diff --git a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestHttpMethodFactory.java b/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestHttpMethodFactory.java deleted file mode 100644 index 8d13793e68c..00000000000 --- a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestHttpMethodFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client.http; - -import org.junit.Assert; -import org.junit.Test; - -public class TestHttpMethodFactory { - - @Test - public void testHttpMethodFactory() { - boolean status = true; - try { - VertxHttpMethod method = HttpMethodFactory.findHttpMethodInstance("httpMethod"); - Assert.assertNotNull(method); - } catch (Exception e) { - status = false; - } - Assert.assertFalse(status); - } -} diff --git a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxDeleteMethod.java b/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxDeleteMethod.java deleted file mode 100644 index 56384e75768..00000000000 --- a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxDeleteMethod.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client.http; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.Invocation; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientRequest; - -public class TestVertxDeleteMethod { - @Test - public void testCreateRequest() { - - HttpClient client = Mockito.mock(HttpClient.class); - IpPort ipPort = Mockito.mock(IpPort.class); - Mockito.when(ipPort.getPort()).thenReturn(23); - Mockito.when(ipPort.getHostOrIp()).thenReturn("test"); - Invocation invocation = Mockito.mock(Invocation.class); - RestOperationMeta operation = Mockito.mock(RestOperationMeta.class); - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - HttpClientRequest obj = (HttpClientRequest) VertxDeleteMethod.INSTANCE - .createRequest(client, invocation, ipPort, "testCall", operation, asyncResp); - - Assert.assertNull(obj); - } -} diff --git a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxGetMethod.java b/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxGetMethod.java deleted file mode 100644 index 2ec29c60927..00000000000 --- a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxGetMethod.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client.http; - -import static org.junit.Assert.assertEquals; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.Invocation; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientRequest; - -public class TestVertxGetMethod { - - @Test - public void testVertxGetMethod() { - - HttpClient client = Mockito.mock(HttpClient.class); - Invocation invocation = Mockito.mock(Invocation.class); - IpPort ipPort = Mockito.mock(IpPort.class); - Mockito.when(ipPort.getPort()).thenReturn(10); - assertEquals(10, ipPort.getPort()); - Mockito.when(ipPort.getHostOrIp()).thenReturn("ever"); - assertEquals("ever", ipPort.getHostOrIp()); - RestOperationMeta operation = Mockito.mock(RestOperationMeta.class); - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - HttpClientRequest obj = - VertxGetMethod.INSTANCE.createRequest(client, invocation, ipPort, "good", operation, asyncResp); - Assert.assertNull(obj); - } -} diff --git a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxHttpMethod.java b/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxHttpMethod.java deleted file mode 100644 index 6874af2fb73..00000000000 --- a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxHttpMethod.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client.http; - -import static org.mockito.Mockito.when; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.common.rest.codec.produce.ProduceProcessor; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.common.rest.definition.path.URLPathBuilder; -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.foundation.vertx.client.http.HttpClientWithContext; -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.vertx.core.Context; -import io.vertx.core.Handler; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientRequest; -import io.vertx.core.http.HttpClientResponse; -import mockit.Expectations; -import mockit.Injectable; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestVertxHttpMethod extends VertxHttpMethod { - - HttpClientRequest request; - - @Before - public void setup() { - request = Mockito.mock(HttpClientRequest.class); - } - - @Test - public void testDoMethod(@Mocked HttpClient httpClient, @Injectable URIEndpointObject address) throws Exception { - Context context = new MockUp() { - @Mock - public void runOnContext(Handler action) { - action.handle(null); - } - }.getMockInstance(); - HttpClientWithContext httpClientWithContext = new HttpClientWithContext(httpClient, context); - - Invocation invocation = Mockito.mock(Invocation.class); - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - RestOperationMeta swaggerRestOperation = Mockito.mock(RestOperationMeta.class); - - Endpoint endpoint = Mockito.mock(Endpoint.class); - when(invocation.getOperationMeta()).thenReturn(operationMeta); - URLPathBuilder urlPathBuilder = Mockito.mock(URLPathBuilder.class); - when(swaggerRestOperation.getPathBuilder()).thenReturn(urlPathBuilder); - operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); - when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerRestOperation); - when(invocation.getEndpoint()).thenReturn(endpoint); - when(endpoint.getAddress()).thenReturn(address); - - when(request.exceptionHandler(Mockito.any())).then(answer -> null); - - this.doMethod(httpClientWithContext, invocation, asyncResp); - Assert.assertTrue(true); - } - - @Test - public void testSetCseContext() { - boolean status = false; - try { - Invocation invocation = Mockito.mock(Invocation.class); - HttpClientResponse httpResponse = Mockito.mock(HttpClientResponse.class); - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - RestOperationMeta swaggerRestOperation = Mockito.mock(RestOperationMeta.class); - HttpClientRequest request = Mockito.mock(HttpClientRequest.class); - - Endpoint endpoint = Mockito.mock(Endpoint.class); - when(invocation.getOperationMeta()).thenReturn(operationMeta); - URLPathBuilder urlPathBuilder = Mockito.mock(URLPathBuilder.class); - when(swaggerRestOperation.getPathBuilder()).thenReturn(urlPathBuilder); - operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); - when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerRestOperation); - when(invocation.getEndpoint()).thenReturn(endpoint); - String contentType = httpResponse.getHeader("Content-Type"); - ProduceProcessor produceProcessor = Mockito.mock(ProduceProcessor.class); - when(swaggerRestOperation.findProduceProcessor(contentType)).thenReturn(produceProcessor); - this.setCseContext(invocation, request); - } catch (Exception ex) { - status = true; - } - Assert.assertFalse(status); - } - - @Test - public void testHandleResponse() { - boolean status = false; - try { - Invocation invocation = Mockito.mock(Invocation.class); - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - HttpClientResponse httpResponse = Mockito.mock(HttpClientResponse.class); - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - RestOperationMeta swaggerRestOperation = Mockito.mock(RestOperationMeta.class); - - Endpoint endpoint = Mockito.mock(Endpoint.class); - when(invocation.getOperationMeta()).thenReturn(operationMeta); - URLPathBuilder urlPathBuilder = Mockito.mock(URLPathBuilder.class); - when(swaggerRestOperation.getPathBuilder()).thenReturn(urlPathBuilder); - operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); - when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(swaggerRestOperation); - when(invocation.getEndpoint()).thenReturn(endpoint); - - String contentType = httpResponse.getHeader("Content-Type"); - ProduceProcessor produceProcessor = Mockito.mock(ProduceProcessor.class); - when(swaggerRestOperation.findProduceProcessor(contentType)).thenReturn(produceProcessor); - this.handleResponse(invocation, httpResponse, swaggerRestOperation, asyncResp); - } catch (Exception ex) { - status = true; - } - Assert.assertFalse(status); - } - - @Override - protected HttpClientRequest createRequest(HttpClient client, Invocation invocation, IpPort ipPort, String path, - RestOperationMeta operation, AsyncResponse asyncResp) { - return request; - } - - @Test - public void testCreateRequestPathNoUrlPrefixNoPath(@Injectable Invocation invocation, - @Injectable RestOperationMeta swaggerRestOperation, @Injectable Endpoint endpoint, - @Injectable URIEndpointObject address, @Injectable URLPathBuilder builder) throws Exception { - new Expectations() { - { - endpoint.getAddress(); - result = address; - builder.createRequestPath((Object[]) any); - result = "/path"; - } - }; - String path = this.createRequestPath(invocation, swaggerRestOperation); - Assert.assertEquals("/path", path); - } - - @Test - public void testCreateRequestPathNoUrlPrefixHavePath(@Injectable Invocation invocation, - @Injectable RestOperationMeta swaggerRestOperation, @Injectable Endpoint endpoint, - @Injectable URIEndpointObject address, @Injectable URLPathBuilder builder) throws Exception { - Map contextMap = new HashMap<>(); - contextMap.put(RestConst.REST_CLIENT_REQUEST_PATH, "/client/path"); - - new Expectations() { - { - endpoint.getAddress(); - result = address; - invocation.getHandlerContext(); - result = contextMap; - } - }; - String path = this.createRequestPath(invocation, swaggerRestOperation); - Assert.assertEquals("/client/path", path); - } - - @Test - public void testCreateRequestPathHaveUrlPrefixNoPath(@Injectable Invocation invocation, - @Injectable RestOperationMeta swaggerRestOperation, @Injectable Endpoint endpoint, - @Injectable URIEndpointObject address, @Injectable URLPathBuilder builder) throws Exception { - new Expectations() { - { - endpoint.getAddress(); - result = address; - address.getFirst(Const.URL_PREFIX); - result = "/root"; - builder.createRequestPath((Object[]) any); - result = "/path"; - } - }; - String path = this.createRequestPath(invocation, swaggerRestOperation); - Assert.assertEquals("/root/path", path); - } - - @Test - public void testCreateRequestPathHaveUrlPrefixHavePath(@Injectable Invocation invocation, - @Injectable RestOperationMeta swaggerRestOperation, @Injectable Endpoint endpoint, - @Injectable URIEndpointObject address, @Injectable URLPathBuilder builder) throws Exception { - Map contextMap = new HashMap<>(); - contextMap.put(RestConst.REST_CLIENT_REQUEST_PATH, "/client/path"); - - new Expectations() { - { - endpoint.getAddress(); - result = address; - address.getFirst(Const.URL_PREFIX); - result = "/root"; - invocation.getHandlerContext(); - result = contextMap; - } - }; - String path = this.createRequestPath(invocation, swaggerRestOperation); - Assert.assertEquals("/root/client/path", path); - } - - @Test - public void testCreateRequestPathHaveUrlPrefixHavePathAndStartWith(@Injectable Invocation invocation, - @Injectable RestOperationMeta swaggerRestOperation, @Injectable Endpoint endpoint, - @Injectable URIEndpointObject address, @Injectable URLPathBuilder builder) throws Exception { - Map contextMap = new HashMap<>(); - contextMap.put(RestConst.REST_CLIENT_REQUEST_PATH, "/client/path"); - - new Expectations() { - { - endpoint.getAddress(); - result = address; - address.getFirst(Const.URL_PREFIX); - result = "/client"; - invocation.getHandlerContext(); - result = contextMap; - } - }; - String path = this.createRequestPath(invocation, swaggerRestOperation); - Assert.assertEquals("/client/path", path); - } -} diff --git a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxPostMethod.java b/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxPostMethod.java deleted file mode 100644 index b676a0e3e33..00000000000 --- a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxPostMethod.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.Invocation; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientRequest; - -public class TestVertxPostMethod { - @Test - public void testVertxPostMethod() { - - Invocation invocation = Mockito.mock(Invocation.class); - HttpClient client = Mockito.mock(HttpClient.class); - IpPort ipPort = Mockito.mock(IpPort.class); - RestOperationMeta operation = Mockito.mock(RestOperationMeta.class); - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - - Mockito.when(ipPort.getPort()).thenReturn(23); - assertEquals(23, ipPort.getPort()); - Mockito.when(ipPort.getHostOrIp()).thenReturn("testCall"); - assertNotNull("testCall", ipPort.getHostOrIp()); - HttpClientRequest obj = - VertxPostMethod.INSTANCE.createRequest(client, invocation, ipPort, "test", operation, asyncResp); - Assert.assertNull(obj); - } -} diff --git a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxPutMethod.java b/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxPutMethod.java deleted file mode 100644 index 62e24844198..00000000000 --- a/transports/transport-rest/transport-rest-client/src/test/java/io/servicecomb/transport/rest/client/http/TestVertxPutMethod.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.client.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.Invocation; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientRequest; - -public class TestVertxPutMethod { - - @Test - public void testVertxPutMethod() { - - HttpClient client = Mockito.mock(HttpClient.class); - Invocation invocation = Mockito.mock(Invocation.class); - IpPort ipPort = Mockito.mock(IpPort.class); - RestOperationMeta operation = Mockito.mock(RestOperationMeta.class); - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - Mockito.when(ipPort.getHostOrIp()).thenReturn("test"); - assertNotNull("test", ipPort.getHostOrIp()); - Mockito.when(ipPort.getPort()).thenReturn(13); - assertEquals(13, ipPort.getPort()); - HttpClientRequest obj = - VertxPutMethod.INSTANCE.createRequest(client, invocation, ipPort, "testCall", operation, asyncResp); - Assert.assertNull(obj); - } -} diff --git a/transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/FakeRestTransport.java b/transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/FakeRestTransport.java new file mode 100644 index 00000000000..50896650aba --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/FakeRestTransport.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.transport.rest.client; + +import org.apache.servicecomb.core.transport.AbstractTransport; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; + +class FakeRestTransport extends AbstractTransport { + @Override + public String getName() { + return null; + } + + @Override + public boolean init() { + return false; + } + + @Override + public Object parseAddress(String address) { + return new URIEndpointObject(address); + } +} diff --git a/transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/RestFeatureController.java b/transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/RestFeatureController.java new file mode 100644 index 00000000000..88127270084 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/test/java/org/apache/servicecomb/transport/rest/client/RestFeatureController.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.transport.rest.client; + +import java.io.File; +import java.util.List; + +import jakarta.ws.rs.CookieParam; +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.QueryParam; + +@Path("/") +public class RestFeatureController { + public static final String SCHEMA_ID = "rest-feature"; + + @GET + @Path("/query") + public String query(@QueryParam("query") String query) { + return query; + } + + @GET + @Path("/header") + public String header(@HeaderParam("header") String header) { + return header; + } + + @GET + @Path("/cookie") + public String cookie(@CookieParam("cookie1") String cookie1, @CookieParam("cookie2") String cookie2) { + return cookie1 + ":" + cookie2; + } + + @POST + @Path("/form") + public String form(@FormParam("form1") String form1, @FormParam("form2") String form2) { + return form1 + ":" + form2; + } + + @POST + @Path("/formWithUpload") + public String formWithUpload(@FormParam("form1") String form1, @FormParam("form2") File form2) { + return form1 + ":" + form2.getName(); + } + + @POST + @Path("/formWithUploadList") + public String formWithUploadList(@FormParam("files") List files) { + return files.toString(); + } + + @POST + @Path("/body") + public String body(String body) { + return body; + } +} diff --git a/transports/transport-rest/transport-rest-client/src/test/resources/log4j.properties b/transports/transport-rest/transport-rest-client/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/transports/transport-rest/transport-rest-client/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/transports/transport-rest/transport-rest-client/src/test/resources/log4j2.xml b/transports/transport-rest/transport-rest-client/src/test/resources/log4j2.xml new file mode 100644 index 00000000000..8cee07220ec --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/test/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/transports/transport-rest/transport-rest-servlet/pom.xml b/transports/transport-rest/transport-rest-servlet/pom.xml index 55332bdf0a6..0bdac6777ef 100644 --- a/transports/transport-rest/transport-rest-servlet/pom.xml +++ b/transports/transport-rest/transport-rest-servlet/pom.xml @@ -1,12 +1,13 @@ - - 4.0.0 - - io.servicecomb - transport-rest - 0.3.0-SNAPSHOT - - transport-rest-servlet - - - - javax.servlet - javax.servlet-api - + + 4.0.0 + + org.apache.servicecomb + transport-rest + 3.4.0-SNAPSHOT + + transport-rest-servlet + Java Chassis::Transports::Rest::Servlet - - org.springframework - spring-web - - - io.servicecomb - transport-rest-client - + + + jakarta.servlet + jakarta.servlet-api + - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + + org.springframework + spring-webmvc + + + org.apache.servicecomb + transport-rest-client + + + org.apache.servicecomb + registry-service-center + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.servicecomb + foundation-test-scaffolding + + + org.jmockit + jmockit + test + + diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/CseXmlWebApplicationContext.java b/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/CseXmlWebApplicationContext.java deleted file mode 100644 index 720ea357cd3..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/CseXmlWebApplicationContext.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.Set; - -import javax.servlet.ServletContext; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.web.context.support.XmlWebApplicationContext; - -import io.servicecomb.foundation.common.utils.BeanUtils; - -public class CseXmlWebApplicationContext extends XmlWebApplicationContext { - private static final Logger LOGGER = LoggerFactory.getLogger(CseXmlWebApplicationContext.class); - - static final String KEY_LOCATION = "contextConfigLocation"; - - private String defaultBeanResource = BeanUtils.DEFAULT_BEAN_RESOURCE; - - public CseXmlWebApplicationContext(ServletContext servletContext) { - setServletContext(servletContext); - } - - public void setDefaultBeanResource(String defaultBeanResource) { - this.defaultBeanResource = defaultBeanResource; - } - - @Override - protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { - super.invokeBeanFactoryPostProcessors(beanFactory); - - // inject servlet after config installed and before transport init - RestServletInjector.defaultInject(getServletContext()); - ServletUtils.saveUrlPrefix(getServletContext()); - } - - @Override - public String[] getConfigLocations() { - String contextConfigLocation = getServletContext().getInitParameter(KEY_LOCATION); - String[] locationArray = splitLocations(contextConfigLocation); - - LOGGER.info("init spring context: {}.", Arrays.toString(locationArray)); - return locationArray; - } - - private String[] splitLocations(String locations) { - Set locationSet = new LinkedHashSet<>(); - if (!StringUtils.isEmpty(locations)) { - for (String location : locations.split("[,\n]")) { - location = location.trim(); - if (StringUtils.isEmpty(location)) { - continue; - } - - locationSet.add(location); - } - } - - if (!StringUtils.isEmpty(defaultBeanResource)) { - locationSet.add(defaultBeanResource); - } - - return locationSet.toArray(new String[locationSet.size()]); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ProducerServletHttpRequestArgMapper.java b/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ProducerServletHttpRequestArgMapper.java deleted file mode 100644 index 99210c17cf3..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ProducerServletHttpRequestArgMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import javax.servlet.http.HttpServletRequest; - -import io.servicecomb.swagger.invocation.SwaggerInvocation; -import io.servicecomb.swagger.invocation.arguments.producer.AbstractProducerContextArgMapper; - -public class ProducerServletHttpRequestArgMapper extends AbstractProducerContextArgMapper { - - private HttpServletRequest httpRequest; - - public ProducerServletHttpRequestArgMapper(HttpServletRequest httpRequest) { - super(-1); - this.httpRequest = httpRequest; - } - - @Override - public Object createContextArg(SwaggerInvocation invocation) { - return httpRequest; - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestAsyncListener.java b/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestAsyncListener.java deleted file mode 100644 index 6ff315bd6c9..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestAsyncListener.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import java.io.IOException; -import java.io.PrintWriter; - -import javax.servlet.AsyncEvent; -import javax.servlet.AsyncListener; -import javax.servlet.ServletResponse; - -public class RestAsyncListener implements AsyncListener { - @Override - public void onComplete(AsyncEvent event) throws IOException { - // 未使用 - } - - @Override - public void onTimeout(AsyncEvent event) throws IOException { - // TODO:超时的处理,要重新考虑 - ServletResponse response = event.getAsyncContext().getResponse(); - PrintWriter out = response.getWriter(); - out.write("TimeOut Error in Processing"); - } - - @Override - public void onError(AsyncEvent event) throws IOException { - // 未使用 - } - - @Override - public void onStartAsync(AsyncEvent event) throws IOException { - // 未使用 - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestServlet.java b/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestServlet.java deleted file mode 100644 index 27f7d25ae4e..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestServlet.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Rest Servlet Server, load by web container - */ -public class RestServlet extends HttpServlet { - private static final long serialVersionUID = 5797523329773923112L; - - private static final Logger LOGGER = LoggerFactory.getLogger(RestServlet.class); - - private ServletRestServer servletRestServer = new ServletRestServer(); - - @Override - public void init() throws ServletException { - super.init(); - - LOGGER.info("Rest Servlet inited"); - } - - @Override - public void service(final HttpServletRequest request, - final HttpServletResponse response) throws ServletException, IOException { - servletRestServer.service(request, response); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestServletContextListener.java b/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestServletContextListener.java deleted file mode 100644 index 20cde1c24dc..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestServletContextListener.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import static java.lang.Boolean.FALSE; - -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; - -import org.springframework.context.support.AbstractApplicationContext; - -import io.servicecomb.foundation.common.utils.Log4jUtils; - -public class RestServletContextListener implements ServletContextListener { - private AbstractApplicationContext context; - - @Override - public void contextInitialized(ServletContextEvent sce) { - try { - initLog(sce); - initSpring(sce); - } catch (Exception e) { - throw new Error(e); - } - } - - public void initLog(ServletContextEvent sce) throws Exception { - String logMerged = sce.getServletContext().getInitParameter("servicecomb.logging.merged"); - if (!FALSE.toString().equalsIgnoreCase(logMerged)) { - Log4jUtils.init(); - } - } - - public AbstractApplicationContext initSpring(ServletContextEvent sce) { - context = new CseXmlWebApplicationContext(sce.getServletContext()); - context.refresh(); - return context; - } - - @Override - public void contextDestroyed(ServletContextEvent sce) { - if (context == null) { - return; - } - - context.close(); - context = null; - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestServletHttpRequest.java b/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestServletHttpRequest.java deleted file mode 100644 index 94fd00b0a3b..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestServletHttpRequest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; - -import javax.servlet.AsyncContext; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; - -import io.servicecomb.common.rest.codec.RestServerRequestInternal; - -/** - * 封装HttpServletRequest为具有RestHttpRequest接口的类,统一多种rest transport request - */ -public class RestServletHttpRequest implements RestServerRequestInternal { - private HttpServletRequest request; - - private AsyncContext asyncCtx; - - private Map pathParamMap; - - public RestServletHttpRequest(HttpServletRequest request, AsyncContext asyncCtx) { - this.request = request; - this.asyncCtx = asyncCtx; - } - - @Override - public String getPath() { - return request.getRequestURI(); - } - - @Override - public String getMethod() { - return request.getMethod(); - } - - @Override - public String getContentType() { - return request.getContentType(); - } - - @Override - public void setPathParamMap(Map pathParamMap) { - this.pathParamMap = pathParamMap; - } - - @Override - public void complete() { - asyncCtx.complete(); - } - - @Override - public String[] getQueryParam(String key) { - return request.getParameterMap().get(key); - } - - @Override - public String getPathParam(String key) { - return this.pathParamMap.get(key); - } - - @Override - public String getHeaderParam(String key) { - return request.getHeader(key); - } - - @Override - public Object getFormParam(String key) { - return request.getParameter(key); - } - - @Override - public String getCookieParam(String key) { - for (Cookie cookie : request.getCookies()) { - if (cookie.getName().equals(key)) { - return cookie.getValue(); - } - } - return null; - } - - @Override - public InputStream getBody() throws IOException { - return request.getInputStream(); - } - - @Override - public Map getQueryParams() { - return request.getParameterMap(); - } - - @SuppressWarnings("unchecked") - @Override - public T getHttpRequest() { - return (T) request; - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestServletInjector.java b/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestServletInjector.java deleted file mode 100644 index 2657f0418b0..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/RestServletInjector.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import java.util.Arrays; - -import javax.servlet.ServletContext; -import javax.servlet.ServletRegistration.Dynamic; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -public class RestServletInjector { - private static final Logger LOGGER = LoggerFactory.getLogger(RestServletInjector.class); - - public static final String SERVLET_NAME = "ServicecombRestServlet"; - - public static Dynamic defaultInject(ServletContext servletContext) { - RestServletInjector injector = new RestServletInjector(); - - String urlPattern = ServletConfig.getServletUrlPattern(); - return injector.inject(servletContext, urlPattern); - } - - public Dynamic inject(ServletContext servletContext, String urlPattern) { - String[] urlPatterns = splitUrlPattern(urlPattern); - if (urlPatterns.length == 0) { - LOGGER.warn("urlPattern is empty, ignore register {}.", SERVLET_NAME); - return null; - } - - String listenAddress = ServletConfig.getLocalServerAddress(); - if (!ServletUtils.canPublishEndpoint(listenAddress)) { - LOGGER.warn("ignore register {}.", SERVLET_NAME); - return null; - } - - // dynamic deploy a servlet to handle serviceComb RESTful request - Dynamic dynamic = servletContext.addServlet(SERVLET_NAME, RestServlet.class); - dynamic.setAsyncSupported(true); - dynamic.addMapping(urlPatterns); - dynamic.setLoadOnStartup(0); - LOGGER.info("RESTful servlet url pattern: {}.", Arrays.toString(urlPatterns)); - - return dynamic; - } - - private String[] splitUrlPattern(String urlPattern) { - if (StringUtils.isEmpty(urlPattern)) { - return new String[] {}; - } - - return ServletUtils.filterUrlPatterns(urlPattern); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ServletConfig.java b/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ServletConfig.java deleted file mode 100644 index 04efd410d92..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ServletConfig.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import com.netflix.config.DynamicLongProperty; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicStringProperty; - -public final class ServletConfig { - static final long DEFAULT_TIMEOUT = 3000; - - static final String KEY_SERVLET_URL_PATTERN = "servicecomb.rest.servlet.urlPattern"; - - private ServletConfig() { - } - - public static long getServerTimeout() { - DynamicLongProperty address = - DynamicPropertyFactory.getInstance().getLongProperty("cse.rest.server.timeout", DEFAULT_TIMEOUT); - return address.get(); - } - - public static String getLocalServerAddress() { - DynamicStringProperty address = - DynamicPropertyFactory.getInstance().getStringProperty("cse.rest.address", null); - return address.get(); - } - - public static String getServletUrlPattern() { - DynamicStringProperty address = - DynamicPropertyFactory.getInstance().getStringProperty(KEY_SERVLET_URL_PATTERN, null); - return address.get(); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ServletRestServer.java b/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ServletRestServer.java deleted file mode 100644 index ae83a62d1c1..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ServletRestServer.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import java.io.OutputStream; -import java.util.List; -import java.util.Map.Entry; - -import javax.servlet.AsyncContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import io.servicecomb.common.rest.AbstractRestServer; -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.common.rest.codec.RestServerRequestInternal; -import io.servicecomb.common.rest.codec.produce.ProduceProcessor; -import io.servicecomb.core.Invocation; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.InvocationException; - -public class ServletRestServer extends AbstractRestServer { - protected RestAsyncListener restAsyncListener = new RestAsyncListener(); - - public void service(HttpServletRequest request, HttpServletResponse response) { - // 异步场景 - final AsyncContext asyncCtx = request.startAsync(); - asyncCtx.addListener(restAsyncListener); - asyncCtx.setTimeout(ServletConfig.getServerTimeout()); - - RestServerRequestInternal restRequest = new RestServletHttpRequest(request, asyncCtx); - handleRequest(restRequest, response); - } - - @SuppressWarnings("deprecation") - @Override - protected void doSendResponse(HttpServletResponse httpServerResponse, ProduceProcessor produceProcessor, - Response response) throws Exception { - httpServerResponse.setStatus(response.getStatusCode(), response.getReasonPhrase()); - httpServerResponse.setContentType(produceProcessor.getName()); - - if (response.getHeaders().getHeaderMap() != null) { - for (Entry> entry : response.getHeaders().getHeaderMap().entrySet()) { - for (Object value : entry.getValue()) { - httpServerResponse.addHeader(entry.getKey(), String.valueOf(value)); - } - } - } - - // 直接写到stream中去,避免重复分配内存,这是chunk模式,不必设置contentLength - // TODO:设置buffer大小,这很影响性能 - Object body = response.getResult(); - if (response.isFailed()) { - body = ((InvocationException) body).getErrorData(); - } - OutputStream output = httpServerResponse.getOutputStream(); - produceProcessor.encodeResponse(output, body); - httpServerResponse.flushBuffer(); - } - - @Override - protected void setHttpRequestContext(Invocation invocation, RestServerRequestInternal restRequest) { - invocation.getHandlerContext().put(RestConst.HTTP_REQUEST_CREATOR, - new ProducerServletHttpRequestArgMapper(restRequest.getHttpRequest())); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ServletRestTransport.java b/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ServletRestTransport.java deleted file mode 100644 index 315c265d4bd..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ServletRestTransport.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import java.util.HashMap; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - -import io.servicecomb.core.Invocation; -import io.servicecomb.core.transport.AbstractTransport; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.transport.rest.client.RestTransportClient; -import io.servicecomb.transport.rest.client.RestTransportClientManager; - -@Component -public class ServletRestTransport extends AbstractTransport { - private static final Logger LOGGER = LoggerFactory.getLogger(ServletRestTransport.class); - - @Override - public String getName() { - return io.servicecomb.core.Const.RESTFUL; - } - - @Override - public boolean canInit() { - String listenAddress = ServletConfig.getLocalServerAddress(); - if (listenAddress == null) { - // not publish, but can init and be RESTful client - return true; - } - - if (!ServletUtils.canPublishEndpoint(listenAddress)) { - LOGGER.info("ignore transport {}.", this.getClass().getName()); - return false; - } - - return true; - } - - @Override - public boolean init() { - String urlPrefix = System.getProperty(Const.URL_PREFIX); - Map queryMap = new HashMap<>(); - if (!StringUtils.isEmpty(urlPrefix)) { - queryMap.put(Const.URL_PREFIX, urlPrefix); - } - - String listenAddress = ServletConfig.getLocalServerAddress(); - setListenAddressWithoutSchema(listenAddress, queryMap); - - return deployClient(); - } - - private boolean deployClient() { - return RestTransportClientManager.INSTANCE.getRestTransportClient(true) != null && - RestTransportClientManager.INSTANCE.getRestTransportClient(false) != null; - } - - @Override - public void send(Invocation invocation, AsyncResponse asyncResp) throws Exception { - URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); - RestTransportClient client = - RestTransportClientManager.INSTANCE.getRestTransportClient(endpoint.isSslEnabled()); - client.send(invocation, asyncResp); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ServletUtils.java b/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ServletUtils.java deleted file mode 100644 index b8a697b8017..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/main/java/io/servicecomb/transport/rest/servlet/ServletUtils.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -import javax.servlet.ServletContext; -import javax.servlet.ServletRegistration; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -import io.servicecomb.foundation.common.exceptions.ServiceCombException; -import io.servicecomb.foundation.common.net.IpPort; -import io.servicecomb.foundation.common.net.NetUtils; -import io.servicecomb.serviceregistry.api.Const; - -public class ServletUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(ServletUtils.class); - - public static boolean canPublishEndpoint(String listenAddress) { - if (StringUtils.isEmpty(listenAddress)) { - LOGGER.info("listenAddress is null, can not publish."); - return false; - } - - IpPort ipPort = NetUtils.parseIpPortFromURI("http://" + listenAddress); - if (ipPort == null) { - LOGGER.info("invalid listenAddress {}, can not publish, format should be ip:port.", listenAddress); - return false; - } - - if (NetUtils.canTcpListen(ipPort.getSocketAddress().getAddress(), ipPort.getPort())) { - LOGGER.info("{} is not listened, can not publish.", ipPort.getSocketAddress()); - return false; - } - - return true; - } - - // we only support path prefix rule - // other invalid urlPattern will be check by web container, we do not handle that - static void checkUrlPattern(String urlPattern) { - if (!urlPattern.startsWith("/")) { - throw new ServiceCombException("only support rule like /* or /path/* or /path1/path2/* and so on."); - } - - int idx = urlPattern.indexOf("/*"); - if (idx < 0 || (idx >= 0 && idx != urlPattern.length() - 2)) { - throw new ServiceCombException("only support rule like /* or /path/* or /path1/path2/* and so on."); - } - } - - static String[] filterUrlPatterns(String... urlPatterns) { - return filterUrlPatterns(Arrays.asList(urlPatterns)); - } - - static String[] filterUrlPatterns(Collection urlPatterns) { - return urlPatterns.stream() - .filter(pattern -> { - return !pattern.trim().isEmpty(); - }) - .filter(pattern -> { - checkUrlPattern(pattern.trim()); - return true; - }) - .toArray(String[]::new); - } - - static String[] collectUrlPatterns(ServletContext servletContext, Class servletCls) { - List servlets = servletContext.getServletRegistrations() - .values() - .stream() - .filter(predicate -> { - return predicate.getClassName().equals(servletCls.getName()); - }) - .collect(Collectors.toList()); - if (servlets.isEmpty()) { - return new String[] {}; - } - - ServletRegistration servletRegistration = servlets.get(0); - Collection mappings = servletRegistration.getMappings(); - if (servlets.size() > 1) { - LOGGER.info("Found {} {} registered, select the first one, mappings={}.", - servlets.size(), - servletCls.getName(), - mappings); - } - return filterUrlPatterns(mappings); - } - - static String collectUrlPrefix(ServletContext servletContext, Class servletCls) { - String[] urlPatterns = collectUrlPatterns(servletContext, servletCls); - if (urlPatterns.length == 0) { - return null; - } - - // even have multiple urlPattern, we only choose a to set as urlPrefix - // only make sure sdk can invoke - String urlPattern = urlPatterns[0]; - return servletContext.getContextPath() + urlPattern.substring(0, urlPattern.length() - 2); - } - - public static void saveUrlPrefix(ServletContext servletContext) { - String urlPrefix = collectUrlPrefix(servletContext, RestServlet.class); - if (urlPrefix == null) { - LOGGER.info("RestServlet not found, will not save UrlPrefix."); - return; - } - - System.setProperty(Const.URL_PREFIX, urlPrefix); - LOGGER.info("UrlPrefix of this instance is \"{}\".", urlPrefix); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestAsyncListener.java b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestAsyncListener.java new file mode 100644 index 00000000000..0ed7ad203b6 --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestAsyncListener.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.transport.rest.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import jakarta.servlet.AsyncEvent; +import jakarta.servlet.AsyncListener; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response.Status; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonProcessingException; + +public class RestAsyncListener implements AsyncListener { + private static final Logger LOGGER = LoggerFactory.getLogger(RestAsyncListener.class); + + private static String TIMEOUT_MESSAGE; + + static { + try { + TIMEOUT_MESSAGE = JsonUtils.writeValueAsString(new CommonExceptionData("Timeout when processing the request.")); + } catch (JsonProcessingException e) { + LOGGER.error("Failed to init timeout message.", e); + } + } + + @Override + public void onComplete(AsyncEvent event) throws IOException { + } + + @Override + public void onTimeout(AsyncEvent event) throws IOException { + // in this time, maybe: + // 1.invocation in executor's queue + // 2.already executing in executor + // 3.already send response + // to avoid concurrent, must lock request + ServletRequest request = event.getSuppliedRequest(); + HttpServletRequestEx requestEx = (HttpServletRequestEx) request.getAttribute(RestConst.REST_REQUEST); + LOGGER.error("Rest request timeout, method {}, path {}.", requestEx.getMethod(), requestEx.getRequestURI()); + + // Waiting till executing in executor done. This operation may block container pool and make timeout requests in executor's + // queue getting executed, and will cause bad performance. So default timeout is setting to -1 to disable timeout. + synchronized (requestEx) { + ServletResponse response = event.getAsyncContext().getResponse(); + if (!response.isCommitted()) { + // invocation in executor's queue + response.setContentType(MediaType.APPLICATION_JSON); + + // we don't know if developers declared one statusCode in contract + // so we use cse inner statusCode here + ((HttpServletResponse) response).setStatus(Status.INTERNAL_SERVER_ERROR.getStatusCode()); + PrintWriter out = response.getWriter(); + out.write(TIMEOUT_MESSAGE); + response.flushBuffer(); + } + + request.removeAttribute(RestConst.REST_REQUEST); + } + + LOGGER.error("Rest request timeout committed, method {}, path {}.", requestEx.getMethod(), requestEx.getRequestURI()); + } + + @Override + public void onError(AsyncEvent event) throws IOException { + // 未使用 + } + + @Override + public void onStartAsync(AsyncEvent event) throws IOException { + // 未使用 + } +} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServlet.java b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServlet.java new file mode 100644 index 00000000000..d4d26f0117b --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServlet.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.transport.rest.servlet; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Rest Servlet Server, load by web container + */ +public class RestServlet extends HttpServlet { + private static final long serialVersionUID = 5797523329773923112L; + + private static final Logger LOGGER = LoggerFactory.getLogger(RestServlet.class); + + private final ServletRestDispatcher servletRestServer = new ServletRestDispatcher(); + + @Override + public void init() throws ServletException { + super.init(); + + LOGGER.info("Rest Servlet inited"); + } + + @Override + public void service(final HttpServletRequest request, final HttpServletResponse response) { + servletRestServer.service(request, response); + } +} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServletInjector.java b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServletInjector.java new file mode 100644 index 00000000000..24879517664 --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServletInjector.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.transport.rest.servlet; + +import java.util.Arrays; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletRegistration.Dynamic; + +public class RestServletInjector { + private static final Logger LOGGER = LoggerFactory.getLogger(RestServletInjector.class); + + public static final String SERVLET_NAME = "ServicecombRestServlet"; + + public static Dynamic defaultInject(ServletContext servletContext, Environment environment) { + RestServletInjector injector = new RestServletInjector(); + + String urlPattern = ServletConfig.getServletUrlPattern(environment); + return injector.inject(servletContext, urlPattern, environment); + } + + public Dynamic inject(ServletContext servletContext, String urlPattern, Environment environment) { + String[] urlPatterns = splitUrlPattern(urlPattern); + if (urlPatterns.length == 0) { + LOGGER.warn("urlPattern is empty, ignore register {}.", SERVLET_NAME); + return null; + } + + String listenAddress = ServletConfig.getLocalServerAddress(environment); + if (!ServletUtils.canPublishEndpoint(listenAddress)) { + LOGGER.warn("ignore register {}.", SERVLET_NAME); + return null; + } + + // dynamic deploy a servlet to handle serviceComb RESTful request + Dynamic dynamic = servletContext.addServlet(SERVLET_NAME, RestServlet.class); + dynamic.setAsyncSupported(true); + dynamic.addMapping(urlPatterns); + dynamic.setLoadOnStartup(0); + LOGGER.info("RESTful servlet url pattern: {}.", Arrays.toString(urlPatterns)); + + return dynamic; + } + + private String[] splitUrlPattern(String urlPattern) { + if (StringUtils.isEmpty(urlPattern)) { + return new String[] {}; + } + + return ServletUtils.filterUrlPatterns(urlPattern); + } +} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServletProducerInvocationCreator.java b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServletProducerInvocationCreator.java new file mode 100644 index 00000000000..1b31cf95336 --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServletProducerInvocationCreator.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.transport.rest.servlet; + +import org.apache.servicecomb.common.rest.HttpTransportContext; +import org.apache.servicecomb.common.rest.RestProducerInvocationCreator; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; +import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; + +public class RestServletProducerInvocationCreator extends RestProducerInvocationCreator { + public RestServletProducerInvocationCreator(MicroserviceMeta microserviceMeta, Endpoint endpoint, + HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { + super(microserviceMeta, endpoint, requestEx, responseEx); + } + + @Override + protected void initTransportContext(Invocation invocation) { + HttpTransportContext transportContext = new HttpTransportContext(requestEx, responseEx); + invocation.setTransportContext(transportContext); + } +} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletConfig.java b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletConfig.java new file mode 100644 index 00000000000..195e3fe3681 --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletConfig.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.transport.rest.servlet; + +import org.springframework.core.env.Environment; + +public final class ServletConfig { + public static final long DEFAULT_ASYN_SERVLET_TIMEOUT = -1; + + public static final String KEY_SERVLET_URL_PATTERN = "servicecomb.rest.servlet.urlPattern"; + + public static final String SERVICECOMB_REST_ADDRESS = "servicecomb.rest.address"; + + public static final String KEY_SERVICECOMB_ASYC_SERVLET_TIMEOUT = "servicecomb.rest.server.timeout"; + + public static final String DEFAULT_URL_PATTERN = "/*"; + + private ServletConfig() { + } + + public static long getAsyncServletTimeout(Environment environment) { + return environment.getProperty(KEY_SERVICECOMB_ASYC_SERVLET_TIMEOUT, long.class, + DEFAULT_ASYN_SERVLET_TIMEOUT); + } + + public static String getLocalServerAddress(Environment environment) { + return environment.getProperty(SERVICECOMB_REST_ADDRESS); + } + + public static String getServletUrlPattern(Environment environment) { + return environment.getProperty(KEY_SERVLET_URL_PATTERN, DEFAULT_URL_PATTERN); + } +} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletRestDispatcher.java b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletRestDispatcher.java new file mode 100644 index 00000000000..b0f465d9c87 --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletRestDispatcher.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.transport.rest.servlet; + +import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.core.invocation.InvocationCreator; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; +import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; +import org.apache.servicecomb.foundation.vertx.http.StandardHttpServletRequestEx; +import org.apache.servicecomb.foundation.vertx.http.StandardHttpServletResponseEx; + +import jakarta.servlet.AsyncContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class ServletRestDispatcher { + private final RestAsyncListener restAsyncListener = new RestAsyncListener(); + + private Transport transport; + + private MicroserviceMeta microserviceMeta; + + public void service(HttpServletRequest request, HttpServletResponse response) { + if (transport == null) { + transport = SCBEngine.getInstance().getTransportManager().findTransport(CoreConst.RESTFUL); + microserviceMeta = SCBEngine.getInstance().getProducerMicroserviceMeta(); + } + + // 异步场景 + AsyncContext asyncCtx = request.startAsync(); + asyncCtx.addListener(restAsyncListener); + asyncCtx.setTimeout(ServletConfig.getAsyncServletTimeout(SCBEngine.getInstance().getEnvironment())); + + HttpServletRequestEx requestEx = new StandardHttpServletRequestEx(request); + HttpServletResponseEx responseEx = new StandardHttpServletResponseEx(response); + + ((StandardHttpServletRequestEx) requestEx).setCacheRequest(true); + InvocationCreator creator = new RestServletProducerInvocationCreator(microserviceMeta, transport.getEndpoint(), + requestEx, responseEx); + new RestProducerInvocationFlow(creator, requestEx, responseEx) + .run(); + } +} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletRestTransport.java b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletRestTransport.java new file mode 100644 index 00000000000..b06fbfcba04 --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletRestTransport.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.transport.rest.servlet; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.transport.AbstractTransport; +import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; +import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ServletRestTransport extends AbstractTransport { + private static final Logger LOGGER = LoggerFactory.getLogger(ServletRestTransport.class); + + @Override + public String getName() { + return CoreConst.RESTFUL; + } + + @Override + public boolean canInit() { + String listenAddress = ServletConfig.getLocalServerAddress(environment); + if (listenAddress == null) { + // not publish, but can init and be RESTful client + return true; + } + + if (!ServletUtils.canPublishEndpoint(listenAddress)) { + LOGGER.info("ignore transport {}.", this.getClass().getName()); + return false; + } + + return true; + } + + @Override + public boolean init() { + String urlPrefix = ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX); + Map queryMap = new HashMap<>(); + if (!StringUtils.isEmpty(urlPrefix)) { + queryMap.put(DefinitionConst.URL_PREFIX, urlPrefix); + } + + String listenAddress = ServletConfig.getLocalServerAddress(environment); + setListenAddressWithoutSchema(listenAddress, queryMap); + + return true; + } +} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletUtils.java b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletUtils.java new file mode 100644 index 00000000000..6bdead6974b --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/ServletUtils.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.transport.rest.servlet; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.common.rest.UploadConfig; +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.apache.servicecomb.foundation.common.net.IpPort; +import org.apache.servicecomb.foundation.common.net.NetUtils; +import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; +import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; + +import jakarta.servlet.MultipartConfigElement; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletRegistration; +import jakarta.servlet.ServletRegistration.Dynamic; + +public class ServletUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(ServletUtils.class); + + public static boolean canPublishEndpoint(String listenAddress) { + if (StringUtils.isEmpty(listenAddress)) { + LOGGER.info("listenAddress is null, can not publish."); + return false; + } + + IpPort ipPort = NetUtils.parseIpPortFromURI("http://" + listenAddress); + if (ipPort == null) { + LOGGER.info("invalid listenAddress {}, can not publish, format should be ip:port.", listenAddress); + return false; + } + + if (NetUtils.canTcpListen(ipPort.getSocketAddress().getAddress(), ipPort.getPort())) { + LOGGER.info("{} is not listened, can not publish.", ipPort.getSocketAddress()); + return false; + } + + return true; + } + + // we only support path prefix rule + // other invalid urlPattern will be check by web container, we do not handle that + static void checkUrlPattern(String urlPattern) { + if (!urlPattern.startsWith("/")) { + throw new ServiceCombException("only support rule like /* or /path/* or /path1/path2/* and so on."); + } + + int idx = urlPattern.indexOf("/*"); + if (idx < 0 || idx != urlPattern.length() - 2) { + throw new ServiceCombException("only support rule like /* or /path/* or /path1/path2/* and so on."); + } + } + + static String[] filterUrlPatterns(String... urlPatterns) { + return filterUrlPatterns(Arrays.asList(urlPatterns)); + } + + static String[] filterUrlPatterns(Collection urlPatterns) { + return urlPatterns.stream() + .filter(pattern -> !pattern.trim().isEmpty()) + .filter(pattern -> { + checkUrlPattern(pattern.trim()); + return true; + }) + .toArray(String[]::new); + } + + static String[] collectUrlPatterns(ServletContext servletContext, Class servletCls) { + List servlets = findServletRegistrations(servletContext, servletCls); + if (servlets.isEmpty()) { + return new String[] {}; + } + + ServletRegistration servletRegistration = servlets.get(0); + Collection mappings = servletRegistration.getMappings(); + if (servlets.size() > 1) { + LOGGER.info("Found {} {} registered, select the first one, mappings={}.", + servlets.size(), + servletCls.getName(), + mappings); + } + return filterUrlPatterns(mappings); + } + + static List findServletRegistrations(ServletContext servletContext, + Class servletCls) { + if (servletContext == null) { + return null; + } + return servletContext.getServletRegistrations() + .values() + .stream() + .filter(predicate -> predicate.getClassName().equals(servletCls.getName())) + .collect(Collectors.toList()); + } + + static String collectUrlPrefix(ServletContext servletContext, Class servletCls) { + String[] urlPatterns = collectUrlPatterns(servletContext, servletCls); + if (urlPatterns.length == 0) { + return null; + } + + // even have multiple urlPattern, we only choose one to set as urlPrefix + // only make sure sdk can invoke + String urlPattern = urlPatterns[0]; + return servletContext.getContextPath() + urlPattern.substring(0, urlPattern.length() - 2); + } + + public static void saveUrlPrefix(ServletContext servletContext) { + String urlPrefix = collectUrlPrefix(servletContext, RestServlet.class); + if (urlPrefix == null) { + LOGGER.info("RestServlet not found, will not save UrlPrefix."); + return; + } + + ClassLoaderScopeContext.setClassLoaderScopeProperty(DefinitionConst.URL_PREFIX, urlPrefix); + LOGGER.info("UrlPrefix of this instance is \"{}\".", urlPrefix); + } + + static File createUploadDir(ServletContext servletContext, String location) { + // If relative, it is relative to TEMPDIR + File dir = new File(location); + if (!dir.isAbsolute()) { + dir = new File((File) servletContext.getAttribute(ServletContext.TEMPDIR), location).getAbsoluteFile(); + } + + if (!dir.exists()) { + dir.mkdirs(); + } + + return dir; + } + + static void setServletParameters(ServletContext servletContext, Environment environment) { + UploadConfig uploadConfig = new UploadConfig(environment); + MultipartConfigElement multipartConfig = uploadConfig.toMultipartConfigElement(); + + File dir = createUploadDir(servletContext, multipartConfig.getLocation()); + LOGGER.info("set uploads directory to \"{}\".", dir.getAbsolutePath()); + + List servlets = findServletRegistrations(servletContext, RestServlet.class); + if (servlets != null) { + for (ServletRegistration servletRegistration : servlets) { + if (!Dynamic.class.isInstance(servletRegistration)) { + continue; + } + + Dynamic dynamic = (Dynamic) servletRegistration; + dynamic.setMultipartConfig(multipartConfig); + } + } + } + + public static void init(ServletContext servletContext, Environment environment) { + RestServletInjector.defaultInject(servletContext, environment); + ServletUtils.saveUrlPrefix(servletContext); + setServletParameters(servletContext, environment); + } +} diff --git a/transports/transport-rest/transport-rest-servlet/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport b/transports/transport-rest/transport-rest-servlet/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport new file mode 100644 index 00000000000..f4603761582 --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport @@ -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.transport.rest.servlet.ServletRestTransport diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/ArchaiusUtils.java b/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/ArchaiusUtils.java deleted file mode 100644 index f7afd5dac1d..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/ArchaiusUtils.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.servicecomb.transport.rest.servlet; - -import com.netflix.config.ConfigurationManager; -import com.netflix.config.DynamicProperty; -import com.netflix.config.DynamicPropertyFactory; - -import mockit.Deencapsulation; - -// copy this everywhere, :( -public class ArchaiusUtils { - public static void resetConfig() { - Deencapsulation.setField(ConfigurationManager.class, "instance", null); - Deencapsulation.setField(ConfigurationManager.class, "customConfigurationInstalled", false); - Deencapsulation.setField(DynamicPropertyFactory.class, "config", null); - Deencapsulation.setField(DynamicPropertyFactory.class, "initializedWithDefaultConfig", false); - Deencapsulation.setField(DynamicProperty.class, "dynamicPropertySupportImpl", null); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestCseXmlWebApplicationContext.java b/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestCseXmlWebApplicationContext.java deleted file mode 100644 index 2f36fd9da4b..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestCseXmlWebApplicationContext.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import javax.servlet.ServletContext; -import javax.servlet.ServletRegistration.Dynamic; -import javax.xml.ws.Holder; - -import org.hamcrest.Matchers; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; - -import io.servicecomb.foundation.common.utils.BeanUtils; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestCseXmlWebApplicationContext { - @Mocked - ServletContext servletContext; - - CseXmlWebApplicationContext context; - - @Before - public void setup() { - context = new CseXmlWebApplicationContext(servletContext); - } - - @Test - public void testGetConfigLocationsEmpty() { - String[] result = context.getConfigLocations(); - Assert.assertThat(result, Matchers.arrayContaining(BeanUtils.DEFAULT_BEAN_RESOURCE)); - } - - @Test - public void testGetConfigLocationsEmptyAndDefaultEmpty() { - context.setDefaultBeanResource(""); - String[] result = context.getConfigLocations(); - Assert.assertThat(result.length, Matchers.is(0)); - } - - @Test - public void testGetConfigLocationsComma() { - new Expectations() { - { - servletContext.getInitParameter(CseXmlWebApplicationContext.KEY_LOCATION); - result = "a,b"; - } - }; - - String[] result = context.getConfigLocations(); - Assert.assertThat(result, Matchers.arrayContaining("a", "b", BeanUtils.DEFAULT_BEAN_RESOURCE)); - } - - @Test - public void testGetConfigLocationsPartEmpty() { - new Expectations() { - { - servletContext.getInitParameter(CseXmlWebApplicationContext.KEY_LOCATION); - result = "a,,b"; - } - }; - String[] result = context.getConfigLocations(); - Assert.assertThat(result, Matchers.arrayContaining("a", "b", BeanUtils.DEFAULT_BEAN_RESOURCE)); - } - - @Test - public void testGetConfigLocationsLine() { - new Expectations() { - { - servletContext.getInitParameter(CseXmlWebApplicationContext.KEY_LOCATION); - result = "a\r\nb"; - } - }; - String[] result = context.getConfigLocations(); - Assert.assertThat(result, Matchers.arrayContaining("a", "b", BeanUtils.DEFAULT_BEAN_RESOURCE)); - } - - @Test - public void testGetConfigLocationsMix() { - new Expectations() { - { - servletContext.getInitParameter(CseXmlWebApplicationContext.KEY_LOCATION); - result = "a\r\nb,,c"; - } - }; - String[] result = context.getConfigLocations(); - Assert.assertThat(result, Matchers.arrayContaining("a", "b", "c", BeanUtils.DEFAULT_BEAN_RESOURCE)); - } - - @Test - public void testInjectServlet(@Mocked ConfigurableListableBeanFactory beanFactory) { - Holder holder = new Holder<>(); - new MockUp() { - @Mock - public Dynamic defaultInject(ServletContext servletContext) { - holder.value = true; - return null; - } - }; - - context.invokeBeanFactoryPostProcessors(beanFactory); - - Assert.assertTrue(holder.value); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestAsyncListener.java b/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestAsyncListener.java deleted file mode 100644 index 567ab3849d5..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestAsyncListener.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import java.io.IOException; -import java.io.PrintWriter; - -import javax.servlet.AsyncContext; -import javax.servlet.AsyncEvent; -import javax.servlet.ServletResponse; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -public class TestRestAsyncListener { - - @Test - public void testOnTimeout() throws IOException { - - boolean status = true; - try { - RestAsyncListener restasynclistener = new RestAsyncListener(); - AsyncEvent event = Mockito.mock(AsyncEvent.class); - AsyncContext asynccontext = Mockito.mock(AsyncContext.class); - Mockito.when(event.getAsyncContext()).thenReturn(asynccontext); - ServletResponse response = Mockito.mock(ServletResponse.class); - Mockito.when(asynccontext.getResponse()).thenReturn(response); - - PrintWriter out = Mockito.mock(PrintWriter.class); - Mockito.when(response.getWriter()).thenReturn(out); - restasynclistener.onTimeout(event); - restasynclistener.onComplete(event); - restasynclistener.onError(event); - restasynclistener.onStartAsync(event); - Assert.assertNotNull(restasynclistener); - } catch (Exception e) { - status = false; - Assert.assertNotNull(e); - } - Assert.assertTrue(status); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestServlet.java b/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestServlet.java deleted file mode 100644 index 5cdd2288896..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestServlet.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.CseContext; -import io.servicecomb.core.transport.TransportManager; -import io.servicecomb.transport.rest.servlet.common.MockUtil; - -public class TestRestServlet { - private RestServlet restservlet = null; - - private HttpServletRequest request = null; - - private HttpServletResponse response = null; - - @Before - public void setUp() throws Exception { - restservlet = new RestServlet(); - request = Mockito.mock(HttpServletRequest.class); - response = Mockito.mock(HttpServletResponse.class); - - CseContext.getInstance().setTransportManager(Mockito.mock(TransportManager.class)); - } - - @After - public void tearDown() throws Exception { - restservlet = null; - } - - @Test - public void testInit() throws ServletException { - restservlet.init(); - Assert.assertTrue(true); - } - - @Test - public void testService() { - boolean status = true; - try { - MockUtil.getInstance().mockServletRestServer(); - restservlet.service(request, response); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestServletContextListener.java b/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestServletContextListener.java deleted file mode 100644 index 3f588259df4..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestServletContextListener.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import javax.servlet.ServletContext; -import javax.servlet.ServletContextEvent; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -public class TestRestServletContextListener { - @Test - public void testcontextInitializedException() { - boolean status = true; - RestServletContextListener listener = new RestServletContextListener(); - ServletContextEvent sce = Mockito.mock(ServletContextEvent.class); - - try { - listener.contextInitialized(sce); - } catch (Exception e) { - status = false; - } catch (Error e) { - status = false; - } - Assert.assertFalse(status); - } - - @Test - public void testInitSpring() { - boolean status = true; - RestServletContextListener listener = new RestServletContextListener(); - ServletContextEvent sce = Mockito.mock(ServletContextEvent.class); - ServletContext context = Mockito.mock(ServletContext.class); - Mockito.when(sce.getServletContext()).thenReturn(context); - Mockito.when(sce.getServletContext().getInitParameter("contextConfigLocation")).thenReturn("locations"); - try { - listener.initSpring(sce); - } catch (Exception e) { - status = false; - } - Assert.assertFalse(status); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestServletHttpRequest.java b/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestServletHttpRequest.java deleted file mode 100644 index 16b356da2f8..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestServletHttpRequest.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.AsyncContext; -import javax.servlet.http.HttpServletRequest; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -public class TestRestServletHttpRequest { - - private RestServletHttpRequest lrequest = null; - - @Before - public void setUp() throws Exception { - HttpServletRequest request = Mockito.mock(HttpServletRequest.class); - AsyncContext asyncCtx = Mockito.mock(AsyncContext.class); - lrequest = new RestServletHttpRequest(request, asyncCtx); - } - - @After - public void tearDown() throws Exception { - lrequest = null; - } - - @Test - public void testGetPath() { - Assert.assertNull(lrequest.getPath()); - } - - @Test - public void testGetMethod() { - Assert.assertNull(lrequest.getMethod()); - } - - @Test - public void testComplete() { - boolean status = true; - try { - lrequest.complete(); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testSetPathParamMap() { - Map pathParamMap = new HashMap(); - boolean status = true; - try { - lrequest.setPathParamMap(pathParamMap); - } catch (Exception e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testGetQueryParam() { - Assert.assertNull(lrequest.getQueryParam("key")); - } - - @Test - public void testGetPathParam() { - boolean status = false; - try { - Assert.assertNull(lrequest.getPathParam("key")); - } catch (Exception ex) { - status = true; - } - Assert.assertTrue(status); - } - - @Test - public void testGetHeaderParam() { - Assert.assertNull(lrequest.getHeaderParam("key")); - } - - @Test - public void testGetFormParam() { - Assert.assertNull(lrequest.getFormParam("key")); - } - - @Test - public void testGetBody() { - boolean status = true; - try { - Assert.assertNull(lrequest.getBody()); - } catch (IOException e) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testGetQueryParams() { - Assert.assertNotNull(lrequest.getQueryParams()); - } - - @Test - public void testGetHttpRequest() { - Assert.assertNotNull(lrequest.getHttpRequest()); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestServletInjector.java b/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestServletInjector.java deleted file mode 100644 index 4a0d8060365..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestRestServletInjector.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.UnknownHostException; - -import javax.servlet.ServletContext; -import javax.servlet.ServletRegistration.Dynamic; - -import org.junit.Assert; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Mocked; - -public class TestRestServletInjector { - @Test - public void testDefaultInjectEmptyUrlPattern(@Mocked ServletContext servletContext, @Mocked Dynamic dynamic) { - new Expectations(ServletConfig.class) { - { - ServletConfig.getServletUrlPattern(); - result = null; - } - }; - - Assert.assertEquals(null, RestServletInjector.defaultInject(servletContext)); - } - - @Test - public void testDefaultInjectNotListen(@Mocked ServletContext servletContext, - @Mocked Dynamic dynamic) throws UnknownHostException, IOException { - try (ServerSocket ss = new ServerSocket(0, 0, InetAddress.getByName("127.0.0.1"))) { - int port = ss.getLocalPort(); - - new Expectations(ServletConfig.class) { - { - ServletConfig.getServletUrlPattern(); - result = "/*"; - ServletConfig.getLocalServerAddress(); - result = "127.0.0.1:" + port; - } - }; - } - - Assert.assertEquals(null, RestServletInjector.defaultInject(servletContext)); - } - - @Test - public void testDefaultInjectListen(@Mocked ServletContext servletContext, - @Mocked Dynamic dynamic) throws UnknownHostException, IOException { - try (ServerSocket ss = new ServerSocket(0, 0, InetAddress.getByName("127.0.0.1"))) { - int port = ss.getLocalPort(); - - new Expectations(ServletConfig.class) { - { - ServletConfig.getServletUrlPattern(); - result = "/rest/*"; - ServletConfig.getLocalServerAddress(); - result = "127.0.0.1:" + port; - } - }; - - Assert.assertEquals(dynamic, RestServletInjector.defaultInject(servletContext)); - } - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletConfig.java b/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletConfig.java deleted file mode 100644 index 7e0b4c3cf73..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletConfig.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import org.apache.commons.configuration.Configuration; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -import com.netflix.config.DynamicPropertyFactory; - -public class TestServletConfig { - @BeforeClass - public static void classSetup() { - ArchaiusUtils.resetConfig(); - } - - @AfterClass - public static void classTeardown() { - ArchaiusUtils.resetConfig(); - } - - @Test - public void testGetLocalServerAddress() { - Assert.assertNull(ServletConfig.getLocalServerAddress()); - } - - @Test - public void testGetServerTimeout() { - Assert.assertEquals(ServletConfig.DEFAULT_TIMEOUT, ServletConfig.getServerTimeout()); - } - - @Test - public void testGetServletUrlPattern() { - DynamicPropertyFactory.getInstance(); - Configuration configuration = (Configuration) DynamicPropertyFactory.getBackingConfigurationSource(); - configuration.setProperty(ServletConfig.KEY_SERVLET_URL_PATTERN, "/*"); - Assert.assertEquals("/*", ServletConfig.getServletUrlPattern()); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletHttpRequestCreator.java b/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletHttpRequestCreator.java deleted file mode 100644 index 924eeeda178..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletHttpRequestCreator.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import javax.servlet.http.HttpServletRequest; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.Invocation; - -public class TestServletHttpRequestCreator { - - @Test - public void testCreateMockParam() { - boolean status = true; - try { - HttpServletRequest httpRequest = Mockito.mock(HttpServletRequest.class); - ProducerServletHttpRequestArgMapper servletHttpRequestCreator = - new ProducerServletHttpRequestArgMapper(httpRequest); - Invocation invocation = Mockito.mock(Invocation.class); - servletHttpRequestCreator.createContextArg(invocation); - } catch (Exception ex) { - status = false; - } - Assert.assertTrue(status); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletRestServer.java b/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletRestServer.java deleted file mode 100644 index 49fa11b3bce..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletRestServer.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import static org.junit.Assert.assertEquals; - -import javax.servlet.AsyncContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.codec.RestServerRequestInternal; -import io.servicecomb.common.rest.codec.produce.ProduceProcessor; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.CseContext; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.core.transport.TransportManager; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.transport.rest.servlet.common.MockUtil; -import mockit.Mocked; - -public class TestServletRestServer { - - private ServletRestServer servletRestServer = null; - - private HttpServletRequest request = null; - - private HttpServletResponse response = null; - - private AsyncContext asyncCtx = null; - - private RestOperationMeta restOperation = null; - - private OperationMeta operationMeta = null; - - @Before - public void setUp() throws Exception { - servletRestServer = new ServletRestServer(); - request = Mockito.mock(HttpServletRequest.class); - response = Mockito.mock(HttpServletResponse.class); - - asyncCtx = Mockito.mock(AsyncContext.class); - restOperation = Mockito.mock(RestOperationMeta.class); - operationMeta = Mockito.mock(OperationMeta.class); - Mockito.when(request.startAsync()).thenReturn(asyncCtx); - Mockito.when(restOperation.getOperationMeta()).thenReturn(operationMeta); - } - - @After - public void tearDown() throws Exception { - servletRestServer = null; - } - - @Test - public void testServiceException(@Mocked TransportManager transportManager) { - CseContext.getInstance().setTransportManager(transportManager); - - boolean status = true; - try { - MockUtil.getInstance().mockAbstactRestServer(); - servletRestServer.service(request, response); - } catch (Exception exce) { - Assert.assertNotNull(exce); - status = false; - } - Assert.assertTrue(status); - - CseContext.getInstance().setTransportManager(null); - } - - @Test - public void testSetHttpRequestContext() { - boolean status = true; - try { - Invocation invocation = Mockito.mock(Invocation.class); - RestServerRequestInternal restRequest = Mockito.mock(RestServerRequestInternal.class); - servletRestServer.setHttpRequestContext(invocation, restRequest); - } catch (Exception exce) { - Assert.assertNotNull(exce); - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testDoSendResponse() { - boolean status = true; - HttpServletResponse httpServerResponse = Mockito.mock(HttpServletResponse.class); - ProduceProcessor produceProcessor = Mockito.mock(ProduceProcessor.class); - Object result = Mockito.mock(Object.class); - Mockito.when(produceProcessor.getName()).thenReturn("testCall"); - assertEquals("testCall", produceProcessor.getName()); - try { - Response response = Response.create(12, "gjhghjgk", result); - servletRestServer.doSendResponse(httpServerResponse, produceProcessor, response); - } catch (Exception exce) { - Assert.assertNotNull(exce); - status = false; - } - Assert.assertTrue(status); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletRestTransport.java b/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletRestTransport.java deleted file mode 100644 index 055885b2ecd..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletRestTransport.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import java.io.IOException; -import java.net.ServerSocket; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Invocation; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.serviceregistry.Features; -import io.servicecomb.serviceregistry.RegistryUtils; -import io.servicecomb.serviceregistry.ServiceRegistry; -import io.servicecomb.serviceregistry.api.Const; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.transport.rest.client.RestTransportClient; -import io.servicecomb.transport.rest.client.RestTransportClientManager; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestServletRestTransport { - ServletRestTransport transport = new ServletRestTransport(); - - @Test - public void testInitNotPublish(@Mocked RestTransportClient restTransportClient) { - new MockUp() { - @Mock - public RestTransportClient getRestTransportClient(boolean sslEnabled) { - return restTransportClient; - } - }; - - new Expectations(ServletConfig.class) { - { - ServletConfig.getLocalServerAddress(); - result = null; - } - }; - Assert.assertTrue(transport.init()); - Assert.assertNull(transport.getPublishEndpoint()); - } - - @Test - public void testInitPublishNoUrlPrefix(@Mocked RestTransportClient restTransportClient) { - new MockUp() { - @Mock - public RestTransportClient getRestTransportClient(boolean sslEnabled) { - return restTransportClient; - } - }; - - new Expectations(ServletConfig.class) { - { - ServletConfig.getLocalServerAddress(); - result = "1.1.1.1:1234"; - } - }; - System.clearProperty(Const.URL_PREFIX); - - Assert.assertTrue(transport.init()); - Assert.assertEquals("rest://1.1.1.1:1234", transport.getPublishEndpoint().getEndpoint()); - } - - @Test - public void testInitPublishWithUrlPrefix(@Mocked RestTransportClient restTransportClient, - @Mocked ServiceRegistry serviceRegistry) { - Features features = new Features(); - new Expectations(RegistryUtils.class) { - { - RegistryUtils.getServiceRegistry(); - result = serviceRegistry; - serviceRegistry.getFeatures(); - result = features; - } - }; - - new MockUp() { - @Mock - public RestTransportClient getRestTransportClient(boolean sslEnabled) { - return restTransportClient; - } - }; - - new Expectations(ServletConfig.class) { - { - ServletConfig.getLocalServerAddress(); - result = "1.1.1.1:1234"; - } - }; - System.setProperty(Const.URL_PREFIX, "/root"); - - Assert.assertTrue(transport.init()); - Assert.assertEquals("rest://1.1.1.1:1234?urlPrefix=/root", transport.getPublishEndpoint().getEndpoint()); - - System.clearProperty(Const.URL_PREFIX); - } - - @Test - public void testSendException() { - boolean status = true; - Invocation invocation = Mockito.mock(Invocation.class); - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - URIEndpointObject endpoint = Mockito.mock(URIEndpointObject.class); - Endpoint endpoint1 = Mockito.mock(Endpoint.class); - Mockito.when(invocation.getEndpoint()).thenReturn(endpoint1); - Mockito.when(invocation.getEndpoint().getAddress()).thenReturn(endpoint); - try { - transport.send(invocation, asyncResp); - } catch (Exception exce) { - Assert.assertNotNull(exce); - status = false; - } - Assert.assertFalse(status); - } - - @Test - public void testGetOrder() { - ServletRestTransport transport = new ServletRestTransport(); - Assert.assertEquals(0, transport.getOrder()); - } - - @Test - public void testCanInitNullAddress() throws IOException { - new Expectations(ServletConfig.class) { - { - ServletConfig.getLocalServerAddress(); - result = null; - } - }; - - ServletRestTransport transport = new ServletRestTransport(); - Assert.assertTrue(transport.canInit()); - } - - @Test - public void testCanInitListened() throws IOException { - ServerSocket ss = new ServerSocket(0); - int port = ss.getLocalPort(); - - new Expectations(ServletConfig.class) { - { - ServletConfig.getLocalServerAddress(); - result = "0.0.0.0:" + port; - } - }; - - ServletRestTransport transport = new ServletRestTransport(); - Assert.assertTrue(transport.canInit()); - - ss.close(); - } - - @Test - public void testCanInitNotListened() throws IOException { - ServerSocket ss = new ServerSocket(0); - int port = ss.getLocalPort(); - ss.close(); - - new Expectations(ServletConfig.class) { - { - ServletConfig.getLocalServerAddress(); - result = "0.0.0.0:" + port; - } - }; - - ServletRestTransport transport = new ServletRestTransport(); - Assert.assertFalse(transport.canInit()); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletUtils.java b/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletUtils.java deleted file mode 100644 index 75a0f63e3ac..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/TestServletUtils.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.servlet.ServletContext; -import javax.servlet.ServletRegistration; - -import org.hamcrest.Matchers; -import org.junit.Assert; -import org.junit.Test; - -import io.servicecomb.foundation.common.exceptions.ServiceCombException; -import io.servicecomb.serviceregistry.api.Const; -import mockit.Expectations; -import mockit.Mocked; - -public class TestServletUtils { - @Test - public void testCheckUrlPatternNormal() { - ServletUtils.checkUrlPattern("/*"); - ServletUtils.checkUrlPattern("/abc/*"); - ServletUtils.checkUrlPattern("/abc/def/*"); - - // normal, must not throw exception, no need to check - } - - @Test - public void testCheckUrlPatternMiddleWideChar() { - try { - ServletUtils.checkUrlPattern("/abc/*def"); - Assert.fail("must throw exception"); - } catch (ServiceCombException e) { - Assert.assertEquals("only support rule like /* or /path/* or /path1/path2/* and so on.", e.getMessage()); - } - } - - @Test - public void testCheckUrlPatternNoWideChar() { - try { - ServletUtils.checkUrlPattern("/abcdef"); - Assert.fail("must throw exception"); - } catch (ServiceCombException e) { - Assert.assertEquals("only support rule like /* or /path/* or /path1/path2/* and so on.", e.getMessage()); - } - } - - @Test - public void testCheckUrlPatternNotStartWithSlash() { - try { - ServletUtils.checkUrlPattern("abcdef/*"); - Assert.fail("must throw exception"); - } catch (ServiceCombException e) { - Assert.assertEquals("only support rule like /* or /path/* or /path1/path2/* and so on.", e.getMessage()); - } - } - - @Test - public void testFilterUrlPatternsNormal() { - String urlPattern = "/r1/*"; - - Collection urlPatterns = Arrays.asList(urlPattern); - String[] result = ServletUtils.filterUrlPatterns(urlPatterns); - Assert.assertThat(result, Matchers.arrayContaining("/r1/*")); - - result = ServletUtils.filterUrlPatterns(urlPattern); - Assert.assertThat(result, Matchers.arrayContaining("/r1/*")); - } - - @Test - public void testFilterUrlPatternsEmpty() { - Collection urlPatterns = Arrays.asList(" ", "\t"); - String[] result = ServletUtils.filterUrlPatterns(urlPatterns); - Assert.assertThat(result, Matchers.emptyArray()); - } - - @Test - public void testFilterUrlPatternsInvalid() { - Collection urlPatterns = Arrays.asList("/abc"); - try { - ServletUtils.filterUrlPatterns(urlPatterns); - Assert.fail("must throw exception"); - } catch (ServiceCombException e) { - Assert.assertEquals("only support rule like /* or /path/* or /path1/path2/* and so on.", e.getMessage()); - } - } - - @Test - public void testcollectUrlPatternsNoRestServlet(@Mocked ServletContext servletContext, - @Mocked ServletRegistration servletRegistration) { - new Expectations() { - { - servletRegistration.getClassName(); - result = "test"; - servletContext.getServletRegistrations(); - result = Collections.singletonMap("test", servletRegistration); - } - }; - - String[] result = ServletUtils.collectUrlPatterns(servletContext, RestServlet.class); - Assert.assertThat(result, Matchers.emptyArray()); - } - - @Test - public void testcollectUrlPatternsNormalMapping(@Mocked ServletContext servletContext, - @Mocked ServletRegistration r1, @Mocked ServletRegistration r2) { - Map servletRegistrationMap = new LinkedHashMap<>(); - servletRegistrationMap.put("r1", r1); - servletRegistrationMap.put("r2", r2); - - new Expectations() { - { - r1.getClassName(); - result = RestServlet.class.getName(); - r1.getMappings(); - result = Arrays.asList("/r1/*", "/r1/1/*"); - - r2.getClassName(); - result = RestServlet.class.getName(); - - servletContext.getServletRegistrations(); - result = servletRegistrationMap; - } - }; - - String[] result = ServletUtils.collectUrlPatterns(servletContext, RestServlet.class); - Assert.assertThat(result, Matchers.arrayContaining("/r1/*", "/r1/1/*")); - } - - @Test - public void testSaveUrlPrefixNull(@Mocked ServletContext servletContext) { - System.clearProperty(Const.URL_PREFIX); - - ServletUtils.saveUrlPrefix(servletContext); - - Assert.assertNull(System.getProperty(Const.URL_PREFIX)); - System.clearProperty(Const.URL_PREFIX); - } - - @Test - public void testSaveUrlPrefixNormal(@Mocked ServletContext servletContext, - @Mocked ServletRegistration servletRegistration) { - System.clearProperty(Const.URL_PREFIX); - new Expectations() { - { - servletContext.getContextPath(); - result = "/root"; - servletRegistration.getClassName(); - result = RestServlet.class.getName(); - servletRegistration.getMappings(); - result = Arrays.asList("/rest/*"); - servletContext.getServletRegistrations(); - result = Collections.singletonMap("test", servletRegistration); - } - }; - - ServletUtils.saveUrlPrefix(servletContext); - - Assert.assertThat(System.getProperty(Const.URL_PREFIX), Matchers.is("/root/rest")); - System.clearProperty(Const.URL_PREFIX); - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/common/MockUtil.java b/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/common/MockUtil.java deleted file mode 100644 index 4520f428e39..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/io/servicecomb/transport/rest/servlet/common/MockUtil.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.servlet.common; - -import java.util.concurrent.Executor; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.mockito.Mockito; - -import io.servicecomb.common.rest.AbstractRestServer; -import io.servicecomb.common.rest.codec.RestServerRequestInternal; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.definition.OperationMeta; -import io.servicecomb.transport.rest.servlet.ServletRestServer; -import mockit.Mock; -import mockit.MockUp; - -public final class MockUtil { - private static MockUtil instance = new MockUtil(); - - private MockUtil() { - - } - - public static MockUtil getInstance() { - return instance; - } - - public void mockAbstactRestServer() { - - new MockUp>() { - @Mock - protected RestOperationMeta findRestOperation(RestServerRequestInternal restRequest) { - RestOperationMeta restOperationMeta = Mockito.mock(RestOperationMeta.class); - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - Executor executor = Mockito.mock(Executor.class); - operationMeta.setExecutor(executor); - return restOperationMeta; - } - }; - } - - public void mockServletRestServer() { - - new MockUp() { - @Mock - public void service(HttpServletRequest request, HttpServletResponse response) { - - } - }; - } -} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestAsyncListener.java b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestAsyncListener.java new file mode 100644 index 00000000000..2302d6f13b1 --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestAsyncListener.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.servlet; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; + +import jakarta.servlet.AsyncContext; +import jakarta.servlet.AsyncEvent; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.ws.rs.core.MediaType; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.foundation.vertx.http.AbstractHttpServletRequest; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; +import org.apache.servicecomb.foundation.vertx.http.StandardHttpServletRequestEx; +import org.junit.Before; +import org.junit.Test; + +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; +import org.junit.jupiter.api.Assertions; + +public class TestRestAsyncListener { + HttpServletRequest request = new AbstractHttpServletRequest() { + public String getMethod() { + return "GET"; + } + + public String getRequestURI() { + return "path"; + } + }; + + HttpServletRequestEx requestEx = new StandardHttpServletRequestEx(request); + + boolean committed; + + boolean flushed; + + int statusCode; + + String contentType; + + Writer writer = new StringWriter(); + + PrintWriter printWriter = new PrintWriter(writer); + + @Mocked + HttpServletResponse response; + + @Mocked + AsyncContext context; + + AsyncEvent event; + + RestAsyncListener listener = new RestAsyncListener(); + + + @Before + public void setup() { + event = new AsyncEvent(context, requestEx, response); + requestEx.setAttribute(RestConst.REST_REQUEST, requestEx); + + new MockUp(response) { + @Mock + void setContentType(String type) { + contentType = type; + } + + @Mock + void setStatus(int sc) { + statusCode = sc; + } + + @Mock + boolean isCommitted() { + return committed; + } + + @Mock + PrintWriter getWriter() throws IOException { + return printWriter; + } + + @Mock + void flushBuffer() throws IOException { + flushed = true; + } + }; + } + + @Test + public void onTimeoutCommitted() throws IOException { + committed = true; + listener.onTimeout(event); + + Assertions.assertNull(request.getAttribute(RestConst.REST_REQUEST)); + Assertions.assertFalse(flushed); + } + + @Test + public void onTimeoutNotCommitted() throws IOException { + committed = false; + listener.onTimeout(event); + + Assertions.assertNull(request.getAttribute(RestConst.REST_REQUEST)); + Assertions.assertEquals(MediaType.APPLICATION_JSON, contentType); + Assertions.assertEquals(500, statusCode); + Assertions.assertTrue(flushed); + Assertions.assertEquals("{\"message\":\"Timeout when processing the request.\"}", writer.toString()); + } +} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestServlet.java b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestServlet.java new file mode 100644 index 00000000000..9c94e49d58c --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestServlet.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.servlet; + +import static org.apache.servicecomb.core.transport.AbstractTransport.PUBLISH_ADDRESS; + +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.bootstrap.SCBBootstrap; +import org.apache.servicecomb.foundation.common.Holder; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +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 io.vertx.core.impl.SysProps; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import mockit.Deencapsulation; +import mockit.Mock; +import mockit.MockUp; + +public class TestRestServlet { + private RestServlet restservlet = null; + + Environment environment = Mockito.mock(Environment.class); + + @Before + public void setUp() { + Mockito.when(environment.getProperty(PUBLISH_ADDRESS, String.class, "")) + .thenReturn(""); + Mockito.when(environment.getProperty("servicecomb.rest.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); + restservlet = new RestServlet(); + + SCBBootstrap.createSCBEngineForTest(environment); + } + + @After + public void tearDown() { + restservlet = null; + SCBEngine.getInstance().destroy(); + } + + @Test + public void testInit() throws ServletException { + restservlet.init(); + Assertions.assertTrue(true); + } + + // useless, but for coverage + @Test + public void testService() { + Holder holder = new Holder<>(); + ServletRestDispatcher servletRestServer = new MockUp() { + @Mock + void service(HttpServletRequest request, HttpServletResponse response) { + holder.value = true; + } + }.getMockInstance(); + + Deencapsulation.setField(restservlet, "servletRestServer", servletRestServer); + restservlet.service(null, null); + Assertions.assertTrue(holder.value); + } +} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestServletInjector.java b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestServletInjector.java new file mode 100644 index 00000000000..57b69000c6d --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestServletInjector.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.servlet; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.UnknownHostException; + +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletRegistration.Dynamic; +import mockit.Expectations; +import mockit.Mocked; + +public class TestRestServletInjector { + Environment environment = Mockito.mock(Environment.class); + + @Test + public void testDefaultInjectEmptyUrlPattern(@Mocked ServletContext servletContext, @Mocked Dynamic dynamic) { + new Expectations(ServletConfig.class) { + { + ServletConfig.getServletUrlPattern(environment); + result = null; + } + }; + + Assertions.assertNull(RestServletInjector.defaultInject(servletContext, environment)); + } + + @Test + public void testDefaultInjectNotListen(@Mocked ServletContext servletContext, + @Mocked Dynamic dynamic) throws UnknownHostException, IOException { + try (ServerSocket ss = new ServerSocket(0, 0, InetAddress.getByName("127.0.0.1"))) { + int port = ss.getLocalPort(); + + new Expectations(ServletConfig.class) { + { + ServletConfig.getServletUrlPattern(environment); + result = "/*"; + ServletConfig.getLocalServerAddress(environment); + result = "127.0.0.1:" + port; + } + }; + } + + Assertions.assertNull(RestServletInjector.defaultInject(servletContext, environment)); + } + + @Test + public void testDefaultInjectListen(@Mocked ServletContext servletContext, + @Mocked Dynamic dynamic) throws UnknownHostException, IOException { + try (ServerSocket ss = new ServerSocket(0, 0, InetAddress.getByName("127.0.0.1"))) { + int port = ss.getLocalPort(); + + new Expectations(ServletConfig.class) { + { + ServletConfig.getServletUrlPattern(environment); + result = "/rest/*"; + ServletConfig.getLocalServerAddress(environment); + result = "127.0.0.1:" + port; + } + }; + + Assertions.assertEquals(dynamic, RestServletInjector.defaultInject(servletContext, environment)); + } + } +} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestServletRestTransport.java b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestServletRestTransport.java new file mode 100644 index 00000000000..6875965a109 --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestServletRestTransport.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.servlet; + +import static org.apache.servicecomb.core.transport.AbstractTransport.PUBLISH_ADDRESS; + +import java.io.IOException; +import java.net.ServerSocket; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; +import org.apache.servicecomb.registry.definition.DefinitionConst; +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 io.vertx.core.impl.SysProps; +import mockit.Expectations; + +public class TestServletRestTransport { + ServletRestTransport transport; + + Environment environment = Mockito.mock(Environment.class); + + @Before + public void setUp() { + Mockito.when(environment.getProperty(PUBLISH_ADDRESS, "")) + .thenReturn(""); + Mockito.when(environment.getProperty("servicecomb.rest.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); + transport = new ServletRestTransport(); + transport.setEnvironment(environment); + } + + @After + public void tearDown() { + ClassLoaderScopeContext.clearClassLoaderScopeProperty(); + } + + @Test + public void testInitNotPublish() { + new Expectations(ServletConfig.class) { + { + ServletConfig.getLocalServerAddress(environment); + result = null; + } + }; + Assertions.assertTrue(transport.init()); + Assertions.assertNull(transport.getPublishEndpoint()); + } + + @Test + public void testInitPublishNoUrlPrefix() { + new Expectations(ServletConfig.class) { + { + ServletConfig.getLocalServerAddress(environment); + result = "1.1.1.1:1234"; + } + }; + Assertions.assertTrue(transport.init()); + Assertions.assertEquals("rest://1.1.1.1:1234", transport.getPublishEndpoint().getEndpoint()); + } + + @Test + public void testInitPublishWithUrlPrefix() { + new Expectations(ServletConfig.class) { + { + ServletConfig.getLocalServerAddress(environment); + result = "1.1.1.1:1234"; + } + }; + ClassLoaderScopeContext.setClassLoaderScopeProperty(DefinitionConst.URL_PREFIX, "/root"); + + Assertions.assertTrue(transport.init()); + Assertions.assertEquals("rest://1.1.1.1:1234?urlPrefix=%2Froot", transport.getPublishEndpoint().getEndpoint()); + } + + @Test + public void testGetOrder() { + ServletRestTransport transport = new ServletRestTransport(); + Assertions.assertEquals(0, transport.getOrder()); + } + + @Test + public void testCanInitNullAddress() throws IOException { + new Expectations(ServletConfig.class) { + { + ServletConfig.getLocalServerAddress(environment); + result = null; + } + }; + + ServletRestTransport transport = new ServletRestTransport(); + transport.setEnvironment(environment); + Assertions.assertTrue(transport.canInit()); + } + + @Test + public void testCanInitListened() throws IOException { + ServerSocket ss = new ServerSocket(0); + int port = ss.getLocalPort(); + + new Expectations(ServletConfig.class) { + { + ServletConfig.getLocalServerAddress(environment); + result = "0.0.0.0:" + port; + } + }; + + ServletRestTransport transport = new ServletRestTransport(); + transport.setEnvironment(environment); + Assertions.assertTrue(transport.canInit()); + + ss.close(); + } + + @Test + public void testCanInitNotListened() throws IOException { + ServerSocket ss = new ServerSocket(0); + int port = ss.getLocalPort(); + ss.close(); + + new Expectations(ServletConfig.class) { + { + ServletConfig.getLocalServerAddress(environment); + result = "0.0.0.0:" + port; + } + }; + + ServletRestTransport transport = new ServletRestTransport(); + transport.setEnvironment(environment); + Assertions.assertFalse(transport.canInit()); + } +} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestServletUtils.java b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestServletUtils.java new file mode 100644 index 00000000000..b40eb473aba --- /dev/null +++ b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestServletUtils.java @@ -0,0 +1,308 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.servlet; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; +import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; +import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +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 jakarta.servlet.MultipartConfigElement; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletRegistration; +import jakarta.servlet.ServletRegistration.Dynamic; +import jakarta.servlet.http.HttpServlet; +import mockit.Expectations; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; + +public class TestServletUtils { + Environment environment = Mockito.mock(Environment.class); + + @Before + public void setUp() { + Mockito.when(environment.getProperty(RestConst.UPLOAD_DIR, RestConst.UPLOAD_DEFAULT_DIR)) + .thenReturn(RestConst.UPLOAD_DEFAULT_DIR); + Mockito.when(environment.getProperty(RestConst.UPLOAD_MAX_FILE_SIZE, long.class, -1L)) + .thenReturn(-1L); + Mockito.when(environment.getProperty(RestConst.UPLOAD_MAX_SIZE, long.class, -1L)) + .thenReturn(-1L); + Mockito.when(environment.getProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, int.class, 0)) + .thenReturn(0); + LegacyPropertyFactory.setEnvironment(environment); + } + + @Test + public void testCheckUrlPatternNormal() { + ServletUtils.checkUrlPattern("/*"); + ServletUtils.checkUrlPattern("/abc/*"); + ServletUtils.checkUrlPattern("/abc/def/*"); + + // normal, must not throw exception, no need to check + } + + @Test + public void testCheckUrlPatternMiddleWideChar() { + try { + ServletUtils.checkUrlPattern("/abc/*def"); + Assertions.fail("must throw exception"); + } catch (ServiceCombException e) { + Assertions.assertEquals("only support rule like /* or /path/* or /path1/path2/* and so on.", e.getMessage()); + } + } + + @Test + public void testCheckUrlPatternNoWideChar() { + try { + ServletUtils.checkUrlPattern("/abcdef"); + Assertions.fail("must throw exception"); + } catch (ServiceCombException e) { + Assertions.assertEquals("only support rule like /* or /path/* or /path1/path2/* and so on.", e.getMessage()); + } + } + + @Test + public void testCheckUrlPatternNotStartWithSlash() { + try { + ServletUtils.checkUrlPattern("abcdef/*"); + Assertions.fail("must throw exception"); + } catch (ServiceCombException e) { + Assertions.assertEquals("only support rule like /* or /path/* or /path1/path2/* and so on.", e.getMessage()); + } + } + + @Test + public void testFilterUrlPatternsNormal() { + String urlPattern = "/r1/*"; + + Collection urlPatterns = Arrays.asList(urlPattern); + String[] result = ServletUtils.filterUrlPatterns(urlPatterns); + MatcherAssert.assertThat(result, Matchers.arrayContaining("/r1/*")); + + result = ServletUtils.filterUrlPatterns(urlPattern); + MatcherAssert.assertThat(result, Matchers.arrayContaining("/r1/*")); + } + + @Test + public void testFilterUrlPatternsEmpty() { + Collection urlPatterns = Arrays.asList(" ", "\t"); + String[] result = ServletUtils.filterUrlPatterns(urlPatterns); + MatcherAssert.assertThat(result, Matchers.emptyArray()); + } + + @Test + public void testFilterUrlPatternsInvalid() { + Collection urlPatterns = Arrays.asList("/abc"); + try { + ServletUtils.filterUrlPatterns(urlPatterns); + Assertions.fail("must throw exception"); + } catch (ServiceCombException e) { + Assertions.assertEquals("only support rule like /* or /path/* or /path1/path2/* and so on.", e.getMessage()); + } + } + + @Test + public void testcollectUrlPatternsNoRestServlet(@Mocked ServletContext servletContext, + @Mocked ServletRegistration servletRegistration) { + new Expectations() { + { + servletRegistration.getClassName(); + result = "test"; + servletContext.getServletRegistrations(); + result = Collections.singletonMap("test", servletRegistration); + } + }; + + String[] result = ServletUtils.collectUrlPatterns(servletContext, RestServlet.class); + MatcherAssert.assertThat(result, Matchers.emptyArray()); + } + + @Test + public void testcollectUrlPatternsNormalMapping(@Mocked ServletContext servletContext, + @Mocked ServletRegistration r1, @Mocked ServletRegistration r2) { + Map servletRegistrationMap = new LinkedHashMap<>(); + servletRegistrationMap.put("r1", r1); + servletRegistrationMap.put("r2", r2); + + new Expectations() { + { + r1.getClassName(); + result = RestServlet.class.getName(); + r1.getMappings(); + result = Arrays.asList("/r1/*", "/r1/1/*"); + + r2.getClassName(); + result = RestServlet.class.getName(); + + servletContext.getServletRegistrations(); + result = servletRegistrationMap; + } + }; + + String[] result = ServletUtils.collectUrlPatterns(servletContext, RestServlet.class); + MatcherAssert.assertThat(result, Matchers.arrayContaining("/r1/*", "/r1/1/*")); + } + + @Test + public void testSaveUrlPrefixNull(@Mocked ServletContext servletContext) { + ClassLoaderScopeContext.clearClassLoaderScopeProperty(); + + ServletUtils.saveUrlPrefix(servletContext); + + Assertions.assertNull(ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX)); + ClassLoaderScopeContext.clearClassLoaderScopeProperty(); + } + + @Test + public void testSaveUrlPrefixNormal(@Mocked ServletContext servletContext, + @Mocked ServletRegistration servletRegistration) { + ClassLoaderScopeContext.clearClassLoaderScopeProperty(); + new Expectations() { + { + servletContext.getContextPath(); + result = "/root"; + servletRegistration.getClassName(); + result = RestServlet.class.getName(); + servletRegistration.getMappings(); + result = Arrays.asList("/rest/*"); + servletContext.getServletRegistrations(); + result = Collections.singletonMap("test", servletRegistration); + } + }; + + ServletUtils.saveUrlPrefix(servletContext); + + MatcherAssert.assertThat(ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX), + Matchers.is("/root/rest")); + ClassLoaderScopeContext.clearClassLoaderScopeProperty(); + } + + @Test + public void createUploadDir_relative(@Mocked ServletContext servletContext) throws IOException { + File tempDir = Files.createTempDirectory("temp").toFile(); + new Expectations() { + { + servletContext.getAttribute(ServletContext.TEMPDIR); + result = tempDir; + } + }; + + File expectDir = new File(tempDir, "upload"); + Assertions.assertFalse(expectDir.exists()); + + File dir = ServletUtils.createUploadDir(servletContext, "upload"); + Assertions.assertTrue(expectDir.exists()); + Assertions.assertEquals(expectDir.getAbsolutePath(), dir.getAbsolutePath()); + + dir.delete(); + Assertions.assertFalse(expectDir.exists()); + + tempDir.delete(); + Assertions.assertFalse(tempDir.exists()); + } + + @Test + public void createUploadDir_absolute(@Mocked ServletContext servletContext) throws IOException { + File tempDir = Files.createTempDirectory("temp").toFile(); + + File expectDir = new File(tempDir, "upload"); + Assertions.assertFalse(expectDir.exists()); + + File dir = ServletUtils.createUploadDir(servletContext, expectDir.getAbsolutePath()); + Assertions.assertTrue(expectDir.exists()); + Assertions.assertEquals(expectDir.getAbsolutePath(), dir.getAbsolutePath()); + + dir.delete(); + Assertions.assertFalse(expectDir.exists()); + + tempDir.delete(); + Assertions.assertFalse(tempDir.exists()); + } + + @Test + public void setServletParameters_notSupportUpload() { + // not support upload will not set parameters to servlet, so servletContext is null will not throw exception + ServletUtils.setServletParameters(null, environment); + } + + @Test + public void setServletParameters_supportUpload(@Mocked ServletContext servletContext, @Mocked Dynamic d1, + @Mocked ServletRegistration d2) throws IOException { + Map servletRegistrations = new HashMap<>(); + servletRegistrations.put("d1", d1); + servletRegistrations.put("d2", d2); + new Expectations() { + { + servletContext.getServletRegistrations(); + result = servletRegistrations; + d1.getClassName(); + result = RestServlet.class.getName(); + d2.getClassName(); + result = HttpServlet.class.getName(); + } + }; + + List multipartConfigs = new ArrayList<>(); + new MockUp(d1) { + @Mock + void setMultipartConfig(MultipartConfigElement multipartConfig) { + multipartConfigs.add(multipartConfig); + } + }; + + File tempDir = Files.createTempDirectory("temp").toFile(); + File uploadDir = new File(tempDir, "upload"); + Mockito.when(environment.getProperty(RestConst.UPLOAD_DIR, RestConst.UPLOAD_DEFAULT_DIR)) + .thenReturn(uploadDir.getAbsolutePath()); + + ServletUtils.setServletParameters(servletContext, environment); + + Assertions.assertEquals(1, multipartConfigs.size()); + + MultipartConfigElement multipartConfigElement = multipartConfigs.get(0); + Assertions.assertEquals(uploadDir.getAbsolutePath(), multipartConfigElement.getLocation()); + Assertions.assertEquals(-1, multipartConfigElement.getMaxFileSize()); + Assertions.assertEquals(-1, multipartConfigElement.getMaxRequestSize()); + Assertions.assertEquals(0, multipartConfigElement.getFileSizeThreshold()); + + uploadDir.delete(); + tempDir.delete(); + Assertions.assertFalse(tempDir.exists()); + } +} diff --git a/transports/transport-rest/transport-rest-servlet/src/test/resources/config/log4j.cse.transport.rest.properties b/transports/transport-rest/transport-rest-servlet/src/test/resources/config/log4j.cse.transport.rest.properties deleted file mode 100644 index 2fe9cc77e43..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/resources/config/log4j.cse.transport.rest.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -paas.logs.dir=target/ -paas.logs.file=cse_rest.log - -log4j.rootLogger=INFO,paas,stdout diff --git a/transports/transport-rest/transport-rest-servlet/src/test/resources/log4j.properties b/transports/transport-rest/transport-rest-servlet/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/transports/transport-rest/transport-rest-servlet/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true diff --git a/transports/transport-rest/transport-rest-vertx/pom.xml b/transports/transport-rest/transport-rest-vertx/pom.xml index f9379626564..7a3475d38dd 100644 --- a/transports/transport-rest/transport-rest-vertx/pom.xml +++ b/transports/transport-rest/transport-rest-vertx/pom.xml @@ -1,12 +1,13 @@ - - 4.0.0 - - io.servicecomb - transport-rest - 0.3.0-SNAPSHOT - - transport-rest-vertx - - - - io.servicecomb - transport-rest-client - + + 4.0.0 + + org.apache.servicecomb + transport-rest + 3.4.0-SNAPSHOT + + transport-rest-vertx + Java Chassis::Transports::Rest::Vertx - - org.slf4j - slf4j-log4j12 - test - - - log4j - log4j - test - - + + + org.apache.servicecomb + transport-rest-client + + + io.vertx + vertx-codegen + provided + + + org.apache.servicecomb + registry-service-center + test + + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.servicecomb + foundation-test-scaffolding + + + org.apache.servicecomb + common-access-log + + + org.jmockit + jmockit + test + + diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/ProducerVertxHttpRequestArgMapper.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/ProducerVertxHttpRequestArgMapper.java deleted file mode 100644 index 9d1dd68d507..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/ProducerVertxHttpRequestArgMapper.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import io.servicecomb.swagger.invocation.SwaggerInvocation; -import io.servicecomb.swagger.invocation.arguments.producer.AbstractProducerContextArgMapper; -import io.vertx.core.http.HttpServerRequest; - -/** - * 使用vertx http request构造其他各种http request - */ -public class ProducerVertxHttpRequestArgMapper extends AbstractProducerContextArgMapper { - private HttpServerRequest httpRequest; - - public ProducerVertxHttpRequestArgMapper(HttpServerRequest httpRequest) { - super(-1); - this.httpRequest = httpRequest; - } - - @Override - public Object createContextArg(SwaggerInvocation invocation) { - return new VertxToServletMockRequest(httpRequest); - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/RestBodyHandler.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/RestBodyHandler.java deleted file mode 100644 index 1d2901eceec..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/RestBodyHandler.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.servicecomb.transport.rest.vertx; - -import java.io.File; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.ws.rs.core.Response.Status; - -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; -import io.servicecomb.swagger.invocation.exception.CommonExceptionData; -import io.servicecomb.swagger.invocation.exception.ExceptionFactory; -import io.vertx.core.Handler; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.file.FileSystem; -import io.vertx.core.http.HttpHeaders; -import io.vertx.core.http.HttpServerRequest; -import io.vertx.core.logging.Logger; -import io.vertx.core.logging.LoggerFactory; -import io.vertx.ext.web.FileUpload; -import io.vertx.ext.web.RoutingContext; -import io.vertx.ext.web.handler.BodyHandler; -import io.vertx.ext.web.handler.impl.BodyHandlerImpl; -import io.vertx.ext.web.impl.FileUploadImpl; - -/** - * copy from io.vertx.ext.web.handler.impl.BodyHandlerImpl - * and modified. - * - * allowed to disable fileupload by setUploadsDirectory(null) - */ -public class RestBodyHandler implements BodyHandler { - - private static final Logger LOGGER = LoggerFactory.getLogger(BodyHandlerImpl.class); - - private static final String BODY_HANDLED = "__body-handled"; - - private long bodyLimit = DEFAULT_BODY_LIMIT; - - private String uploadsDir; - - private boolean mergeFormAttributes = DEFAULT_MERGE_FORM_ATTRIBUTES; - - private boolean deleteUploadedFilesOnEnd = DEFAULT_DELETE_UPLOADED_FILES_ON_END; - - public RestBodyHandler() { - setUploadsDirectory(DEFAULT_UPLOADS_DIRECTORY); - } - - public RestBodyHandler(String uploadDirectory) { - setUploadsDirectory(uploadDirectory); - } - - @Override - public void handle(RoutingContext context) { - HttpServerRequest request = context.request(); - // we need to keep state since we can be called again on reroute - Boolean handled = context.get(BODY_HANDLED); - if (handled == null || !handled) { - BHandler handler = new BHandler(context); - request.handler(handler); - request.endHandler(v -> handler.end()); - context.put(BODY_HANDLED, true); - } else { - // on reroute we need to re-merge the form params if that was desired - if (mergeFormAttributes && request.isExpectMultipart()) { - request.params().addAll(request.formAttributes()); - } - - context.next(); - } - } - - @Override - public BodyHandler setBodyLimit(long bodyLimit) { - this.bodyLimit = bodyLimit; - return this; - } - - @Override - public BodyHandler setUploadsDirectory(String uploadsDirectory) { - this.uploadsDir = uploadsDirectory; - return this; - } - - @Override - public BodyHandler setMergeFormAttributes(boolean mergeFormAttributes) { - this.mergeFormAttributes = mergeFormAttributes; - return this; - } - - @Override - public BodyHandler setDeleteUploadedFilesOnEnd(boolean deleteUploadedFilesOnEnd) { - this.deleteUploadedFilesOnEnd = deleteUploadedFilesOnEnd; - return this; - } - - private class BHandler implements Handler { - - private RoutingContext context; - - private Buffer body = Buffer.buffer(); - - private boolean failed; - - private AtomicInteger uploadCount = new AtomicInteger(); - - private boolean ended; - - private long uploadSize = 0L; - - private final boolean isMultipart; - - private final boolean isUrlEncoded; - - BHandler(RoutingContext context) { - this.context = context; - Set fileUploads = context.fileUploads(); - - final String contentType = context.request().getHeader(HttpHeaders.CONTENT_TYPE); - isMultipart = contentType != null && contentType.contains("multipart/form-data"); - isUrlEncoded = contentType != null && contentType.contains("application/x-www-form-urlencoded"); - - if (isMultipart || isUrlEncoded) { - makeUploadDir(context.vertx().fileSystem()); - context.request().setExpectMultipart(true); - context.request().uploadHandler(upload -> { - // *** cse begin *** - if (uploadsDir == null) { - failed = true; - CommonExceptionData data = new CommonExceptionData("not support file upload."); - throw new ErrorDataDecoderException(ExceptionFactory.createConsumerException(data)); - } - // *** cse end *** - - // we actually upload to a file with a generated filename - uploadCount.incrementAndGet(); - String uploadedFileName = new File(uploadsDir, UUID.randomUUID().toString()).getPath(); - upload.streamToFileSystem(uploadedFileName); - FileUploadImpl fileUpload = new FileUploadImpl(uploadedFileName, upload); - fileUploads.add(fileUpload); - upload.exceptionHandler(context::fail); - upload.endHandler(v -> uploadEnded()); - }); - } - context.request().exceptionHandler(context::fail); - } - - private void makeUploadDir(FileSystem fileSystem) { - // *** cse begin *** - if (uploadsDir == null) { - return; - } - // *** cse end *** - - if (!fileSystem.existsBlocking(uploadsDir)) { - fileSystem.mkdirsBlocking(uploadsDir); - } - } - - @Override - public void handle(Buffer buff) { - if (failed) { - return; - } - uploadSize += buff.length(); - if (bodyLimit != -1 && uploadSize > bodyLimit) { - failed = true; - context.fail(Status.REQUEST_ENTITY_TOO_LARGE.getStatusCode()); - } else { - // multipart requests will not end up in the request body - // url encoded should also not, however jQuery by default - // post in urlencoded even if the payload is something else - if (!isMultipart /* && !isUrlEncoded */) { - body.appendBuffer(buff); - } - } - } - - void uploadEnded() { - int count = uploadCount.decrementAndGet(); - // only if parsing is done and count is 0 then all files have been processed - if (ended && count == 0) { - doEnd(); - } - } - - void end() { - // this marks the end of body parsing, calling doEnd should - // only be possible from this moment onwards - ended = true; - - // only if parsing is done and count is 0 then all files have been processed - if (uploadCount.get() == 0) { - doEnd(); - } - } - - void doEnd() { - if (deleteUploadedFilesOnEnd) { - if (failed) { - deleteFileUploads(); - } else { - context.addBodyEndHandler(x -> deleteFileUploads()); - } - } - - if (failed) { - return; - } - - HttpServerRequest req = context.request(); - if (mergeFormAttributes && req.isExpectMultipart()) { - req.params().addAll(req.formAttributes()); - } - context.setBody(body); - context.next(); - } - - private void deleteFileUploads() { - for (FileUpload fileUpload : context.fileUploads()) { - FileSystem fileSystem = context.vertx().fileSystem(); - String uploadedFileName = fileUpload.uploadedFileName(); - fileSystem.exists(uploadedFileName, existResult -> { - if (existResult.failed()) { - LOGGER.warn("Could not detect if uploaded file exists, not deleting: " + uploadedFileName, - existResult.cause()); - } else if (existResult.result()) { - fileSystem.delete(uploadedFileName, deleteResult -> { - if (deleteResult.failed()) { - LOGGER.warn("Delete of uploaded file failed: " + uploadedFileName, - deleteResult.cause()); - } - }); - } - }); - } - } - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/RestServerVerticle.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/RestServerVerticle.java deleted file mode 100644 index 524a7ce55e6..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/RestServerVerticle.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.config.DynamicPropertyFactory; - -import io.servicecomb.core.Const; -import io.servicecomb.core.CseContext; -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.transport.AbstractTransport; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.foundation.ssl.SSLCustom; -import io.servicecomb.foundation.ssl.SSLOption; -import io.servicecomb.foundation.ssl.SSLOptionFactory; -import io.servicecomb.foundation.vertx.VertxTLSBuilder; -import io.vertx.core.AbstractVerticle; -import io.vertx.core.Context; -import io.vertx.core.Future; -import io.vertx.core.Vertx; -import io.vertx.core.http.HttpServer; -import io.vertx.core.http.HttpServerOptions; -import io.vertx.ext.web.Router; - -public class RestServerVerticle extends AbstractVerticle { - private static final Logger LOGGER = LoggerFactory.getLogger(RestServerVerticle.class); - - private static final String SSL_KEY = "rest.provider"; - - private static final int ACCEPT_BACKLOG = 2048; - - private static final int SEND_BUFFER_SIZE = 4096; - - private static final int RECEIVE_BUFFER_SIZE = 4096; - - private Endpoint endpoint; - - private URIEndpointObject endpointObject; - - private RestBodyHandler bodyHandler = new RestBodyHandler(); - - @Override - public void init(Vertx vertx, Context context) { - super.init(vertx, context); - this.endpoint = (Endpoint) context.config().getValue(AbstractTransport.ENDPOINT_KEY); - this.endpointObject = (URIEndpointObject) endpoint.getAddress(); - - String uploadsDirectory = - DynamicPropertyFactory.getInstance().getStringProperty("cse.uploads.directory", null).get(); - bodyHandler.setUploadsDirectory(uploadsDirectory); - bodyHandler.setDeleteUploadedFilesOnEnd(true); - LOGGER.info("set uploads directory to " + uploadsDirectory); - } - - @Override - public void start(Future startFuture) throws Exception { - super.start(); - - // 如果本地未配置地址,则表示不必监听,只需要作为客户端使用即可 - if (endpointObject == null) { - LOGGER.warn("rest listen address is not configured, will not start."); - startFuture.complete(); - return; - } - - Router mainRouter = Router.router(vertx); - mainRouter.route().handler(bodyHandler); - - VertxRestServer vertxRestServer = new VertxRestServer(mainRouter); - vertxRestServer.setTransport(CseContext.getInstance().getTransportManager().findTransport(Const.RESTFUL)); - - HttpServer httpServer = createHttpServer(); - httpServer.requestHandler(mainRouter::accept); - - startListen(httpServer, startFuture); - } - - private void startListen(HttpServer server, Future startFuture) { - server.listen(endpointObject.getPort(), endpointObject.getHostOrIp(), ar -> { - if (ar.succeeded()) { - LOGGER.info("rest listen success. address={}:{}", - endpointObject.getHostOrIp(), - ar.result().actualPort()); - startFuture.complete(); - return; - } - - String msg = String.format("rest listen failed, address=%s:%d", - endpointObject.getHostOrIp(), - endpointObject.getPort()); - LOGGER.error(msg, ar.cause()); - startFuture.fail(ar.cause()); - }); - } - - private HttpServer createHttpServer() { - HttpServerOptions serverOptions = createDefaultHttpServerOptions(); - return vertx.createHttpServer(serverOptions); - } - - private HttpServerOptions createDefaultHttpServerOptions() { - HttpServerOptions serverOptions = new HttpServerOptions(); - serverOptions.setAcceptBacklog(ACCEPT_BACKLOG); - serverOptions.setSendBufferSize(SEND_BUFFER_SIZE); - serverOptions.setReceiveBufferSize(RECEIVE_BUFFER_SIZE); - serverOptions.setUsePooledBuffers(true); - - if (endpointObject.isSslEnabled()) { - SSLOptionFactory factory = - SSLOptionFactory.createSSLOptionFactory(SSL_KEY, null); - SSLOption sslOption; - if (factory == null) { - sslOption = SSLOption.buildFromYaml(SSL_KEY); - } else { - sslOption = factory.createSSLOption(); - } - SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); - VertxTLSBuilder.buildNetServerOptions(sslOption, sslCustom, serverOptions); - } - - return serverOptions; - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/RestVertxHttpRequest.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/RestVertxHttpRequest.java deleted file mode 100644 index 662e4ff719c..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/RestVertxHttpRequest.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import java.io.InputStream; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import javax.ws.rs.core.HttpHeaders; - -import io.servicecomb.common.rest.codec.RestServerRequestInternal; -import io.servicecomb.foundation.vertx.stream.BufferInputStream; -import io.vertx.core.Future; -import io.vertx.core.http.HttpServerRequest; -import io.vertx.ext.web.Cookie; -import io.vertx.ext.web.RoutingContext; - -/** - * 将HttpServerRequest封装为RestHttpRequest接口的类型,统一多种rest transport - */ -public class RestVertxHttpRequest implements RestServerRequestInternal { - private RoutingContext context; - - private HttpServerRequest request; - - private Future future; - - private Map pathParamMap; - - public RestVertxHttpRequest(RoutingContext context, Future future) { - this.context = context; - this.request = context.request(); - this.future = future; - } - - @Override - public String getPath() { - return request.path(); - } - - @Override - public String getMethod() { - return request.method().name(); - } - - @Override - public String getContentType() { - return request.getHeader(HttpHeaders.CONTENT_TYPE); - } - - public void setPathParamMap(Map pathParamMap) { - this.pathParamMap = pathParamMap; - } - - @Override - public void complete() { - future.complete(); - } - - @Override - public String[] getQueryParam(String key) { - List paramList = request.params().getAll(key); - if (paramList == null) { - return null; - } - - return (String[]) paramList.toArray(new String[paramList.size()]); - } - - @Override - public String getPathParam(String key) { - return this.pathParamMap.get(key); - } - - @Override - public String getHeaderParam(String key) { - Iterator> ite = request.headers().iterator(); - while (ite.hasNext()) { - Entry entry = ite.next(); - if (key.equals(entry.getKey())) { - return entry.getValue(); - } - } - return null; - } - - @Override - public Object getFormParam(String key) { - return context.request().getParam(key); - } - - @Override - public String getCookieParam(String key) { - Cookie cookie = context.getCookie(key); - if (cookie != null) { - return cookie.getValue(); - } - - return null; - } - - @Override - public InputStream getBody() { - return new BufferInputStream(context.getBody().getByteBuf()); - } - - @Override - public Map getQueryParams() { - Map queryMap = new HashMap<>(); - - for (String name : request.params().names()) { - List param = request.params().getAll(name); - queryMap.put(name, param.toArray(new String[param.size()])); - } - - return queryMap; - } - - @SuppressWarnings("unchecked") - @Override - public T getHttpRequest() { - return (T) request; - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/TransportConfig.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/TransportConfig.java deleted file mode 100644 index 53a711973d0..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/TransportConfig.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import com.netflix.config.DynamicIntProperty; -import com.netflix.config.DynamicPropertyFactory; -import com.netflix.config.DynamicStringProperty; - -public final class TransportConfig { - private TransportConfig() { - } - - public static String getAddress() { - DynamicStringProperty address = - DynamicPropertyFactory.getInstance().getStringProperty("cse.rest.address", null); - return address.get(); - } - - public static int getThreadCount() { - DynamicIntProperty address = - DynamicPropertyFactory.getInstance().getIntProperty("cse.rest.server.thread-count", 1); - return address.get(); - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/VertxRestServer.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/VertxRestServer.java deleted file mode 100644 index 3d9bde87e63..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/VertxRestServer.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import java.util.List; -import java.util.Map.Entry; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; -import io.servicecomb.common.rest.AbstractRestServer; -import io.servicecomb.common.rest.RestConst; -import io.servicecomb.common.rest.codec.RestServerRequestInternal; -import io.servicecomb.common.rest.codec.produce.ProduceProcessor; -import io.servicecomb.core.Invocation; -import io.servicecomb.swagger.invocation.Response; -import io.servicecomb.swagger.invocation.exception.InvocationException; -import io.vertx.core.Future; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpServerResponse; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.RoutingContext; -import io.vertx.ext.web.handler.CookieHandler; - -public class VertxRestServer extends AbstractRestServer { - private static final Logger LOGGER = LoggerFactory.getLogger(VertxRestServer.class); - - private static final String REST_REQUEST = "cse.rest.request"; - - public VertxRestServer(Router router) { - super(); - router.route().handler(CookieHandler.create()); - router.route().failureHandler(this::failureHandler).handler(this::onRequest); - } - - private void failureHandler(RoutingContext context) { - LOGGER.error("http server failed.", context.failure()); - - RestServerRequestInternal restRequest = context.get(REST_REQUEST); - Throwable e = context.failure(); - if (ErrorDataDecoderException.class.isInstance(e)) { - Throwable cause = e.getCause(); - if (InvocationException.class.isInstance(cause)) { - e = cause; - } - } - sendFailResponse(restRequest, context.response(), e); - - // 走到这里,应该都是不可控制的异常,直接关闭连接 - context.response().close(); - } - - private void onRequest(RoutingContext context) { - RestServerRequestInternal restRequest = new RestVertxHttpRequest(context, Future.future()); - context.put(REST_REQUEST, restRequest); - handleRequest(restRequest, context.response()); - } - - @Override - protected void doSendResponse(HttpServerResponse httpServerResponse, ProduceProcessor produceProcessor, - Response response) throws Exception { - httpServerResponse.setStatusCode(response.getStatusCode()); - httpServerResponse.setStatusMessage(response.getReasonPhrase()); - httpServerResponse.putHeader("Content-Type", produceProcessor.getName()); - - if (response.getHeaders().getHeaderMap() != null) { - for (Entry> entry : response.getHeaders().getHeaderMap().entrySet()) { - for (Object value : entry.getValue()) { - httpServerResponse.putHeader(entry.getKey(), String.valueOf(value)); - } - } - } - - Object body = response.getResult(); - if (response.isFailed()) { - body = ((InvocationException) body).getErrorData(); - } - Buffer buffer = produceProcessor.encodeResponse(body); - - if (null == buffer) { - httpServerResponse.end(); - return; - } - httpServerResponse.end(buffer); - } - - @Override - protected void setHttpRequestContext(Invocation invocation, RestServerRequestInternal restRequest) { - - invocation.getHandlerContext().put(RestConst.HTTP_REQUEST_CREATOR, - new ProducerVertxHttpRequestArgMapper(restRequest.getHttpRequest())); - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/VertxRestTransport.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/VertxRestTransport.java deleted file mode 100644 index 7f0ec77afd4..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/VertxRestTransport.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import io.servicecomb.core.Const; -import io.servicecomb.core.Invocation; -import io.servicecomb.core.transport.AbstractTransport; -import io.servicecomb.foundation.common.net.NetUtils; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.foundation.vertx.SimpleJsonObject; -import io.servicecomb.foundation.vertx.VertxUtils; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.servicecomb.transport.rest.client.RestTransportClient; -import io.servicecomb.transport.rest.client.RestTransportClientManager; -import io.vertx.core.DeploymentOptions; - -@Component -public class VertxRestTransport extends AbstractTransport { - private static final Logger log = LoggerFactory.getLogger(VertxRestTransport.class); - - @Override - public String getName() { - return Const.RESTFUL; - } - - @Override - public int getOrder() { - return -1000; - } - - @Override - public boolean canInit() { - setListenAddressWithoutSchema(TransportConfig.getAddress()); - - URIEndpointObject ep = (URIEndpointObject) getEndpoint().getAddress(); - if (ep == null) { - return true; - } - - if (!NetUtils.canTcpListen(ep.getSocketAddress().getAddress(), ep.getPort())) { - log.info("can not listen {}, skip {}.", ep.getSocketAddress(), this.getClass().getName()); - return false; - } - - return true; - } - - @Override - public boolean init() throws Exception { - // 部署transport server - DeploymentOptions options = new DeploymentOptions().setInstances(TransportConfig.getThreadCount()); - SimpleJsonObject json = new SimpleJsonObject(); - json.put(ENDPOINT_KEY, getEndpoint()); - options.setConfig(json); - return VertxUtils.blockDeploy(transportVertx, RestServerVerticle.class, options) && deployClient(); - } - - private boolean deployClient() { - return RestTransportClientManager.INSTANCE.getRestTransportClient(true) != null && - RestTransportClientManager.INSTANCE.getRestTransportClient(false) != null; - } - - @Override - public void send(Invocation invocation, AsyncResponse asyncResp) throws Exception { - URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); - RestTransportClient client = - RestTransportClientManager.INSTANCE.getRestTransportClient(endpoint.isSslEnabled()); - log.debug("Sending request by rest to endpoint {}:{}", endpoint.getHostOrIp(), endpoint.getPort()); - client.send(invocation, asyncResp); - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/VertxToServletMockRequest.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/VertxToServletMockRequest.java deleted file mode 100644 index 28376a07430..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/VertxToServletMockRequest.java +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.Principal; -import java.util.Collection; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -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.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import javax.servlet.http.HttpUpgradeHandler; -import javax.servlet.http.Part; - -import io.vertx.core.MultiMap; -import io.vertx.core.http.HttpServerRequest; - -/** - * 使用vertx http request模拟一个Servlet http request - */ -public class VertxToServletMockRequest implements HttpServletRequest { - - private HttpServerRequest vertxRequest; - - public VertxToServletMockRequest(HttpServerRequest vertxRequest) { - this.vertxRequest = vertxRequest; - } - - @Override - public Object getAttribute(String name) { - throw new Error("not supported method"); - } - - @Override - public Enumeration getAttributeNames() { - throw new Error("not supported method"); - } - - @Override - public String getCharacterEncoding() { - throw new Error("not supported method"); - } - - @Override - public void setCharacterEncoding(String env) throws UnsupportedEncodingException { - throw new Error("not supported method"); - } - - @Override - public int getContentLength() { - throw new Error("not supported method"); - } - - @Override - public long getContentLengthLong() { - throw new Error("not supported method"); - } - - @Override - public String getContentType() { - return this.vertxRequest.getHeader("Content-Type"); - } - - @Override - public ServletInputStream getInputStream() throws IOException { - throw new Error("not supported method"); - } - - @Override - public String getParameter(String name) { - return this.vertxRequest.getParam(name); - } - - @Override - public Enumeration getParameterNames() { - throw new Error("not supported method"); - } - - @Override - public String[] getParameterValues(String name) { - List paramList = this.vertxRequest.params().getAll(name); - return (String[]) paramList.toArray(new String[paramList.size()]); - } - - @Override - public Map getParameterMap() { - Map paramMap = new HashMap<>(); - MultiMap map = this.vertxRequest.params(); - for (String name : map.names()) { - List valueList = map.getAll(name); - paramMap.put(name, (String[]) map.getAll(name).toArray(new String[valueList.size()])); - } - return paramMap; - } - - @Override - public String getProtocol() { - throw new Error("not supported method"); - } - - @Override - public String getScheme() { - return this.vertxRequest.scheme(); - } - - @Override - public String getServerName() { - throw new Error("not supported method"); - } - - @Override - public int getServerPort() { - throw new Error("not supported method"); - } - - @Override - public BufferedReader getReader() throws IOException { - throw new Error("not supported method"); - } - - @Override - public String getRemoteAddr() { - throw new Error("not supported method"); - } - - @Override - public String getRemoteHost() { - return this.vertxRequest.remoteAddress().host(); - } - - @Override - public void setAttribute(String name, Object o) { - throw new Error("not supported method"); - } - - @Override - public void removeAttribute(String name) { - throw new Error("not supported method"); - } - - @Override - public Locale getLocale() { - throw new Error("not supported method"); - } - - @Override - public Enumeration getLocales() { - throw new Error("not supported method"); - } - - @Override - public boolean isSecure() { - throw new Error("not supported method"); - } - - @Override - public RequestDispatcher getRequestDispatcher(String path) { - throw new Error("not supported method"); - } - - @Override - public String getRealPath(String path) { - return this.vertxRequest.path(); - } - - @Override - public int getRemotePort() { - return this.vertxRequest.remoteAddress().port(); - } - - @Override - public String getLocalName() { - throw new Error("not supported method"); - } - - @Override - public String getLocalAddr() { - return this.vertxRequest.localAddress().host(); - } - - @Override - public int getLocalPort() { - return this.vertxRequest.localAddress().port(); - } - - @Override - public ServletContext getServletContext() { - throw new Error("not supported method"); - } - - @Override - public AsyncContext startAsync() throws IllegalStateException { - throw new Error("not supported method"); - } - - @Override - public AsyncContext startAsync(ServletRequest servletRequest, - ServletResponse servletResponse) throws IllegalStateException { - throw new Error("not supported method"); - } - - @Override - public boolean isAsyncStarted() { - throw new Error("not supported method"); - } - - @Override - public boolean isAsyncSupported() { - throw new Error("not supported method"); - } - - @Override - public AsyncContext getAsyncContext() { - throw new Error("not supported method"); - } - - @Override - public DispatcherType getDispatcherType() { - throw new Error("not supported method"); - } - - @Override - public String getAuthType() { - throw new Error("not supported method"); - } - - @Override - public Cookie[] getCookies() { - throw new Error("not supported method"); - } - - @Override - public long getDateHeader(String name) { - throw new Error("not supported method"); - } - - @Override - public String getHeader(String name) { - return this.vertxRequest.getHeader(name); - } - - @Override - public Enumeration getHeaders(String name) { - throw new Error("not supported method"); - } - - @Override - public Enumeration getHeaderNames() { - throw new Error("not supported method"); - } - - @Override - public int getIntHeader(String name) { - String header = this.getHeader(name); - int result; - try { - result = Integer.parseInt(header); - } catch (NumberFormatException e) { - return 0; - } - return result; - } - - @Override - public String getMethod() { - return this.vertxRequest.method().name(); - } - - @Override - public String getPathInfo() { - return this.vertxRequest.path(); - } - - @Override - public String getPathTranslated() { - throw new Error("not supported method"); - } - - @Override - public String getContextPath() { - throw new Error("not supported method"); - } - - @Override - public String getQueryString() { - return this.vertxRequest.query(); - } - - @Override - public String getRemoteUser() { - throw new Error("not supported method"); - } - - @Override - public boolean isUserInRole(String role) { - throw new Error("not supported method"); - } - - @Override - public Principal getUserPrincipal() { - throw new Error("not supported method"); - } - - @Override - public String getRequestedSessionId() { - throw new Error("not supported method"); - } - - @Override - public String getRequestURI() { - return this.vertxRequest.uri(); - } - - @Override - public StringBuffer getRequestURL() { - throw new Error("not supported method"); - } - - @Override - public String getServletPath() { - return this.getPathInfo(); - } - - @Override - public HttpSession getSession(boolean create) { - throw new Error("not supported method"); - } - - @Override - public HttpSession getSession() { - throw new Error("not supported method"); - } - - @Override - public String changeSessionId() { - throw new Error("not supported method"); - } - - @Override - public boolean isRequestedSessionIdValid() { - throw new Error("not supported method"); - } - - @Override - public boolean isRequestedSessionIdFromCookie() { - throw new Error("not supported method"); - } - - @Override - public boolean isRequestedSessionIdFromURL() { - throw new Error("not supported method"); - } - - @Override - public boolean isRequestedSessionIdFromUrl() { - throw new Error("not supported method"); - } - - @Override - public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { - throw new Error("not supported method"); - } - - @Override - public void login(String username, String password) throws ServletException { - throw new Error("not supported method"); - } - - @Override - public void logout() throws ServletException { - throw new Error("not supported method"); - } - - @Override - public Collection getParts() throws IOException, ServletException { - throw new Error("not supported method"); - } - - @Override - public Part getPart(String name) throws IOException, ServletException { - throw new Error("not supported method"); - } - - @Override - public T upgrade(Class handlerClass) throws IOException, ServletException { - throw new Error("not supported method"); - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/AbstractVertxHttpDispatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/AbstractVertxHttpDispatcher.java new file mode 100644 index 00000000000..8963c6f8d9b --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/AbstractVertxHttpDispatcher.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.transport.rest.vertx; + +import org.apache.servicecomb.common.rest.UploadConfig; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.ext.web.handler.BodyHandler; + +public abstract class AbstractVertxHttpDispatcher implements VertxHttpDispatcher { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractVertxHttpDispatcher.class); + + protected BodyHandler createBodyHandler() { + RestBodyHandler bodyHandler = new RestBodyHandler(); + + UploadConfig uploadConfig = new UploadConfig(LegacyPropertyFactory.getEnvironment()); + + bodyHandler.setUploadsDirectory(uploadConfig.getLocation()); + bodyHandler.setDeleteUploadedFilesOnEnd(true); + bodyHandler.setBodyLimit(uploadConfig.getMaxSize()); + + if (uploadConfig.toMultipartConfigElement() != null) { + LOGGER.info("set uploads directory to \"{}\".", uploadConfig.getLocation()); + } + + return bodyHandler; + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/GlobalRestFailureHandler.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/GlobalRestFailureHandler.java new file mode 100644 index 00000000000..8bf9351875f --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/GlobalRestFailureHandler.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.transport.rest.vertx; + +import org.apache.servicecomb.foundation.common.utils.SPIOrder; + +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; + +public interface GlobalRestFailureHandler extends Handler, SPIOrder { +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/HttpServerExceptionHandler.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/HttpServerExceptionHandler.java new file mode 100644 index 00000000000..9829f3ca8b1 --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/HttpServerExceptionHandler.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.transport.rest.vertx; + +import io.vertx.core.Handler; +import org.apache.servicecomb.foundation.common.utils.SPIOrder; + +public interface HttpServerExceptionHandler extends Handler, SPIOrder { +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java new file mode 100644 index 00000000000..cc74e6d0a8a --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java @@ -0,0 +1,419 @@ +/* + * Copyright 2014 Red Hat, Inc. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Apache License v2.0 which accompanies this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * The Apache License v2.0 is available at + * http://www.opensource.org/licenses/apache2.0.php + * + * You may elect to redistribute this code under either of these licenses. + * + */ + +/* + * Froked from https://github.com/vert-x3/vertx-web/blob/master/vertx-web/src/main/java/io/vertx/ext/web/handler/impl/BodyHandlerImpl.java + * + */ +package org.apache.servicecomb.transport.rest.vertx; + +import java.io.File; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; + +import com.google.common.annotations.VisibleForTesting; + +import io.netty.handler.codec.DecoderException; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.file.FileSystem; +import io.vertx.core.http.HttpHeaders; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.http.HttpVersion; +import io.vertx.ext.web.FileUpload; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.BodyHandler; +import io.vertx.ext.web.impl.FileUploadImpl; +import io.vertx.ext.web.impl.RoutingContextInternal; + +/** + * copy from io.vertx.ext.web.handler.impl.BodyHandlerImpl + * and modified. + * + * allowed to disable fileupload by setUploadsDirectory(null) + */ +public class RestBodyHandler implements BodyHandler { + + private long bodyLimit = DEFAULT_BODY_LIMIT; + + private boolean handleFileUploads; + + private String uploadsDir; + + private boolean mergeFormAttributes = DEFAULT_MERGE_FORM_ATTRIBUTES; + + private boolean deleteUploadedFilesOnEnd = DEFAULT_DELETE_UPLOADED_FILES_ON_END; + + private boolean isPreallocateBodyBuffer = DEFAULT_PREALLOCATE_BODY_BUFFER; + + private static final int DEFAULT_INITIAL_BODY_BUFFER_SIZE = 1024; //bytes + + public static final String BYPASS_BODY_HANDLER = "__bypass_body_handler"; + + public RestBodyHandler() { + this(true, DEFAULT_UPLOADS_DIRECTORY); + } + + public RestBodyHandler(boolean handleFileUploads) { + this(handleFileUploads, DEFAULT_UPLOADS_DIRECTORY); + } + + public RestBodyHandler(String uploadDirectory) { + this(true, uploadDirectory); + } + + private RestBodyHandler(boolean handleFileUploads, String uploadDirectory) { + this.handleFileUploads = handleFileUploads; + setUploadsDirectory(uploadDirectory); + } + + @VisibleForTesting + boolean isDeleteUploadedFilesOnEnd() { + return deleteUploadedFilesOnEnd; + } + + @Override + public void handle(RoutingContext context) { + final HttpServerRequest request = context.request(); + final HttpServerResponse response = context.response(); + + if (request.headers().contains(HttpHeaders.UPGRADE, HttpHeaders.WEBSOCKET, true)) { + context.next(); + return; + } + + Boolean bypass = context.get(BYPASS_BODY_HANDLER); + if (Boolean.TRUE.equals(bypass)) { + context.next(); + return; + } + + // we need to keep state since we can be called again on reroute + if (!((RoutingContextInternal) context).seenHandler(RoutingContextInternal.BODY_HANDLER)) { + ((RoutingContextInternal) context).visitHandler(RoutingContextInternal.BODY_HANDLER); + + // Check if a request has a request body. + // A request with a body __must__ either have `transfer-encoding` + // or `content-length` headers set. + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3 + final long parsedContentLength = parseContentLengthHeader(request); + // http2 never transmits a `transfer-encoding` as frames are chunks. + final boolean hasTransferEncoding = + request.version() == HttpVersion.HTTP_2 || request.headers().contains(HttpHeaders.TRANSFER_ENCODING); + + if (!hasTransferEncoding && parsedContentLength == -1) { + // there is no "body", so we can skip this handler + context.next(); + return; + } + + // before parsing the body we can already discard a bad request just by inspecting the content-length against + // the body limit, this will reduce load, on the server by totally skipping parsing the request body + if (bodyLimit != -1 && parsedContentLength != -1) { + if (parsedContentLength > bodyLimit) { + context.fail(413); + return; + } + } + + // handle expectations + // https://httpwg.org/specs/rfc7231.html#header.expect + final String expect = request.getHeader(HttpHeaders.EXPECT); + if (expect != null) { + // requirements validation + if (expect.equalsIgnoreCase("100-continue")) { + // A server that receives a 100-continue expectation in an HTTP/1.0 request MUST ignore that expectation. + if (request.version() != HttpVersion.HTTP_1_0) { + // signal the client to continue + response.writeContinue(); + } + } else { + // the server cannot meet the expectation, we only know about 100-continue + context.fail(417); + return; + } + } + + final BHandler handler = new BHandler(context, isPreallocateBodyBuffer ? parsedContentLength : -1); + boolean ended = request.isEnded(); + if (!ended) { + request + // resume the request (if paused) + .handler(handler) + .endHandler(handler::end) + .resume(); + } + } else { + // on reroute we need to re-merge the form params if that was desired + if (mergeFormAttributes && request.isExpectMultipart()) { + request.params().addAll(request.formAttributes()); + } + + context.next(); + } + } + + @Override + public BodyHandler setHandleFileUploads(boolean handleFileUploads) { + this.handleFileUploads = handleFileUploads; + return this; + } + + @Override + public BodyHandler setBodyLimit(long bodyLimit) { + this.bodyLimit = bodyLimit; + return this; + } + + @Override + public BodyHandler setUploadsDirectory(String uploadsDirectory) { + this.uploadsDir = uploadsDirectory; + return this; + } + + @Override + public BodyHandler setMergeFormAttributes(boolean mergeFormAttributes) { + this.mergeFormAttributes = mergeFormAttributes; + return this; + } + + @Override + public BodyHandler setDeleteUploadedFilesOnEnd(boolean deleteUploadedFilesOnEnd) { + this.deleteUploadedFilesOnEnd = deleteUploadedFilesOnEnd; + return this; + } + + @Override + public BodyHandler setPreallocateBodyBuffer(boolean isPreallocateBodyBuffer) { + this.isPreallocateBodyBuffer = isPreallocateBodyBuffer; + return this; + } + + private long parseContentLengthHeader(HttpServerRequest request) { + String contentLength = request.getHeader(HttpHeaders.CONTENT_LENGTH); + if (contentLength == null || contentLength.isEmpty()) { + return -1; + } + try { + long parsedContentLength = Long.parseLong(contentLength); + return parsedContentLength < 0 ? -1 : parsedContentLength; + } catch (NumberFormatException ex) { + return -1; + } + } + + private class BHandler implements Handler { + private static final int MAX_PREALLOCATED_BODY_BUFFER_BYTES = 65535; + + final RoutingContext context; + + final long contentLength; + + Buffer body; + + volatile boolean failed; + + final AtomicInteger uploadCount = new AtomicInteger(); + + volatile boolean ended; + + long uploadSize = 0L; + + final boolean isMultipart; + + final boolean isUrlEncoded; + + public BHandler(RoutingContext context, long contentLength) { + this.context = context; + this.contentLength = contentLength; + // the request clearly states that there should + // be a body, so we respect the client and ensure + // that the body will not be null + if (contentLength != -1) { + initBodyBuffer(); + } + + List fileUploads = context.fileUploads(); + + final String contentType = context.request().getHeader(HttpHeaders.CONTENT_TYPE); + if (contentType == null) { + isMultipart = false; + isUrlEncoded = false; + } else { + final String lowerCaseContentType = contentType.toLowerCase(); + isMultipart = lowerCaseContentType.startsWith(HttpHeaderValues.MULTIPART_FORM_DATA.toString()); + isUrlEncoded = lowerCaseContentType.startsWith(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()); + } + + if (isMultipart || isUrlEncoded) { + context.request().setExpectMultipart(true); + if (handleFileUploads) { + makeUploadDir(context.vertx().fileSystem()); + } + context.request().uploadHandler(upload -> { + // *** cse begin *** + if (uploadsDir == null) { + failed = true; + CommonExceptionData data = new CommonExceptionData("not support file upload."); + context.fail(ExceptionFactory.createProducerException(data)); + return; + } + // *** cse end *** + if (bodyLimit != -1 && upload.isSizeAvailable()) { + // we can try to abort even before the upload starts + long size = uploadSize + upload.size(); + if (size > bodyLimit) { + failed = true; + context.cancelAndCleanupFileUploads(); + context.fail(413); + return; + } + } + if (handleFileUploads) { + // we actually upload to a file with a generated filename + uploadCount.incrementAndGet(); + String uploadedFileName = new File(uploadsDir, UUID.randomUUID().toString()).getPath(); + FileUploadImpl fileUpload = new FileUploadImpl(context.vertx().fileSystem(), uploadedFileName, upload); + fileUploads.add(fileUpload); + Future fut = upload.streamToFileSystem(uploadedFileName); + fut.onComplete(ar -> { + if (fut.succeeded()) { + uploadEnded(); + } else { + context.cancelAndCleanupFileUploads(); + context.fail(ar.cause()); + } + }); + } + }); + } + + context.request().exceptionHandler(t -> { + context.cancelAndCleanupFileUploads(); + int sc = 200; + if (t instanceof DecoderException) { + // bad request + sc = 400; + if (t.getCause() != null) { + t = t.getCause(); + } + } + context.fail(sc, t); + }); + } + + private void initBodyBuffer() { + int initialBodyBufferSize; + if (contentLength < 0) { + initialBodyBufferSize = DEFAULT_INITIAL_BODY_BUFFER_SIZE; + } else if (contentLength > MAX_PREALLOCATED_BODY_BUFFER_BYTES) { + initialBodyBufferSize = MAX_PREALLOCATED_BODY_BUFFER_BYTES; + } else { + initialBodyBufferSize = (int) contentLength; + } + + if (bodyLimit != -1) { + initialBodyBufferSize = (int) Math.min(initialBodyBufferSize, bodyLimit); + } + + this.body = Buffer.buffer(initialBodyBufferSize); + } + + private void makeUploadDir(FileSystem fileSystem) { + // *** cse begin *** + if (uploadsDir == null) { + return; + } + // *** cse end *** + + if (!fileSystem.existsBlocking(uploadsDir)) { + fileSystem.mkdirsBlocking(uploadsDir); + } + } + + @Override + public void handle(Buffer buff) { + if (failed) { + return; + } + uploadSize += buff.length(); + if (bodyLimit != -1 && uploadSize > bodyLimit) { + failed = true; + context.cancelAndCleanupFileUploads(); + context.fail(413); + } else { + // multipart requests will not end up in the request body + // url encoded should also not, however jQuery by default + // post in urlencoded even if the payload is something else + if (!isMultipart /* && !isUrlEncoded */) { + if (body == null) { + initBodyBuffer(); + } + body.appendBuffer(buff); + } + } + } + + void uploadEnded() { + int count = uploadCount.decrementAndGet(); + // only if parsing is done and count is 0 then all files have been processed + if (ended && count == 0) { + doEnd(); + } + } + + void end(Void v) { + // this marks the end of body parsing, calling doEnd should + // only be possible from this moment onwards + ended = true; + + // only if parsing is done and count is 0 then all files have been processed + if (uploadCount.get() == 0) { + doEnd(); + } + } + + void doEnd() { + + if (failed || context.failed()) { + context.cancelAndCleanupFileUploads(); + return; + } + + if (deleteUploadedFilesOnEnd) { + context.addBodyEndHandler(x -> context.cancelAndCleanupFileUploads()); + } + + HttpServerRequest req = context.request(); + if (mergeFormAttributes && req.isExpectMultipart()) { + req.params().addAll(req.formAttributes()); + } + ((RoutingContextInternal) context).setBody(body); + // release body as it may take lots of memory + body = null; + + context.next(); + } + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java new file mode 100644 index 00000000000..70f8e43c765 --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java @@ -0,0 +1,313 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.vertx; + +import java.nio.channels.ClosedChannelException; +import java.util.List; +import java.util.Set; + +import org.apache.servicecomb.common.accessLog.AccessLogConfig; +import org.apache.servicecomb.common.accessLog.core.element.impl.LocalHostAccessItem; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.core.transport.AbstractTransport; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.foundation.common.utils.ExceptionUtils; +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.ssl.SSLOptionFactory; +import org.apache.servicecomb.foundation.vertx.VertxTLSBuilder; +import org.apache.servicecomb.foundation.vertx.metrics.DefaultHttpServerMetrics; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; +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.google.common.annotations.VisibleForTesting; + +import io.vertx.core.AbstractVerticle; +import io.vertx.core.Context; +import io.vertx.core.Future; +import io.vertx.core.Handler; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import io.vertx.core.http.Http2Settings; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServer; +import io.vertx.core.http.HttpServerOptions; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.net.impl.ConnectionBase; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.CorsHandler; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; + +public class RestServerVerticle extends AbstractVerticle { + private static final Logger LOGGER = LoggerFactory.getLogger(RestServerVerticle.class); + + private static final String SSL_KEY = "rest.provider"; + + private URIEndpointObject endpointObject; + + private WebSocketDispatcher webSocketDispatcher; + + @Override + public void init(Vertx vertx, Context context) { + super.init(vertx, context); + Endpoint endpoint = (Endpoint) context.config().getValue(AbstractTransport.ENDPOINT_KEY); + this.endpointObject = (URIEndpointObject) endpoint.getAddress(); + this.webSocketDispatcher = new WebSocketDispatcher(endpoint); + } + + @Override + public void start(Promise startPromise) throws Exception { + try { + super.start(); + // 如果本地未配置地址,则表示不必监听,只需要作为客户端使用即可 + if (endpointObject == null) { + LOGGER.warn("rest listen address is not configured, will not start."); + startPromise.complete(); + return; + } + Router mainRouter = Router.router(vertx); + mountAccessLogHandler(mainRouter); + mountCorsHandler(mainRouter); + initDispatcher(mainRouter); + mountGlobalRestFailureHandler(mainRouter); + HttpServer httpServer = createHttpServer(); + httpServer.requestHandler(httpServerRequest -> { + if (this.endpointObject.isWebsocketEnabled()) { + String path = LegacyPropertyFactory.getStringProperty("servicecomb.rest.server.websocket-prefix"); + if (httpServerRequest.path().startsWith(path)) { + httpServerRequest.toWebSocket().onComplete(w -> webSocketDispatcher.onRequest(w), + e -> LOGGER.error("WebSocket error.", e)); + return; + } + } + mainRouter.handle(httpServerRequest); + }); + httpServer.connectionHandler(connection -> { + DefaultHttpServerMetrics serverMetrics = (DefaultHttpServerMetrics) ((ConnectionBase) connection).metrics(); + DefaultServerEndpointMetric endpointMetric = serverMetrics.getEndpointMetric(); + long connectedCount = endpointMetric.getCurrentConnectionCount(); + int connectionLimit = LegacyPropertyFactory.getIntProperty("servicecomb.rest.server.connection-limit", + Integer.MAX_VALUE); + if (connectedCount > connectionLimit) { + connection.close(); + endpointMetric.onRejectByConnectionLimit(); + } + }); + List httpServerExceptionHandlers = + SPIServiceUtils.getAllService(HttpServerExceptionHandler.class); + httpServer.exceptionHandler(e -> { + if (e instanceof ClosedChannelException) { + // This is quite normal in between browser and edge, so do not print out. + LOGGER.debug("Unexpected error in server.{}", ExceptionUtils.getExceptionMessageWithoutTrace(e)); + } else { + LOGGER.error("Unexpected error in server.{}", ExceptionUtils.getExceptionMessageWithoutTrace(e)); + } + httpServerExceptionHandlers.forEach(httpServerExceptionHandler -> httpServerExceptionHandler.handle(e)); + }); + startListen(httpServer, startPromise); + } catch (Throwable e) { + // vert.x got some states that not print error and execute call back in VertexUtils.blockDeploy, we add a log our self. + LOGGER.error("", e); + throw e; + } + } + + @VisibleForTesting + void mountGlobalRestFailureHandler(Router mainRouter) { + GlobalRestFailureHandler globalRestFailureHandler = + SPIServiceUtils.getPriorityHighestService(GlobalRestFailureHandler.class); + Handler failureHandler = null == globalRestFailureHandler ? + ctx -> { + if (ctx.response().closed() || ctx.response().ended()) { + // response has been sent, do nothing + LOGGER.error("get a failure with closed response", ctx.failure()); + return; + } + HttpServerResponse response = ctx.response(); + if (ctx.failure() instanceof InvocationException exception) { + // ServiceComb defined exception + response.setStatusCode(exception.getStatusCode()); + response.setStatusMessage(exception.getReasonPhrase()); + response.end(exception.getErrorData().toString()); + return; + } + + LOGGER.error("unexpected failure happened", ctx.failure()); + try { + // unknown exception + CommonExceptionData unknownError = new CommonExceptionData("unknown error"); + ctx.response().setStatusCode(500).putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) + .end(RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(unknownError)); + } catch (Exception e) { + LOGGER.error("failed to send error response!", e); + } + } + : globalRestFailureHandler; + + mainRouter.route() + // this handler does nothing, just ensure the failure handler can catch exception + .handler(RoutingContext::next) + .failureHandler(failureHandler); + } + + private void mountAccessLogHandler(Router mainRouter) { + if (!AccessLogConfig.INSTANCE.isServerLogEnabled()) { + return; + } + LOGGER.info("access log enabled, pattern = {}", AccessLogConfig.INSTANCE.getServerLogPattern()); + mainRouter.route().handler(context -> { + ServerAccessLogEvent accessLogEvent = new ServerAccessLogEvent() + .setRoutingContext(context) + .setMilliStartTime(System.currentTimeMillis()) + .setLocalAddress(LocalHostAccessItem.getLocalAddress(context)); + context.response().endHandler(event -> + EventManager.post(accessLogEvent.setMilliEndTime(System.currentTimeMillis()))); + context.next(); + }); + } + + /** + * Support CORS + */ + void mountCorsHandler(Router mainRouter) { + if (!TransportConfig.isCorsEnabled()) { + return; + } + + CorsHandler corsHandler = getCorsHandler(); + // Access-Control-Allow-Credentials + corsHandler.allowCredentials(TransportConfig.isCorsAllowCredentials()); + // Access-Control-Allow-Headers + corsHandler.allowedHeaders(TransportConfig.getCorsAllowedHeaders()); + // Access-Control-Allow-Methods + Set allowedMethods = TransportConfig.getCorsAllowedMethods(); + for (String method : allowedMethods) { + corsHandler.allowedMethod(HttpMethod.valueOf(method)); + } + // Access-Control-Expose-Headers + corsHandler.exposedHeaders(TransportConfig.getCorsExposedHeaders()); + // Access-Control-Max-Age + int maxAge = TransportConfig.getCorsMaxAge(); + if (maxAge >= 0) { + corsHandler.maxAgeSeconds(maxAge); + } + + LOGGER.info("mount CorsHandler"); + mainRouter.route().handler(corsHandler); + } + + private CorsHandler getCorsHandler() { + CorsHandler handler = CorsHandler.create(); + String[] origin = TransportConfig.getCorsAllowedOrigin(); + if (origin == null) { + handler.addOrigin("*"); + } else { + for (String item : origin) { + handler.addOrigin(item); + } + } + return handler; + } + + private void initDispatcher(Router mainRouter) { + List dispatchers = SPIServiceUtils.getOrLoadSortedService(VertxHttpDispatcher.class); + + for (VertxHttpDispatcher dispatcher : dispatchers) { + if (dispatcher.enabled()) { + dispatcher.init(mainRouter); + } + } + } + + private void startListen(HttpServer server, Promise startPromise) { + Future result = server.listen(endpointObject.getPort(), endpointObject.getHostOrIp()); + result.onComplete((s, f) -> { + if (f == null) { + LOGGER.info("rest listen success. address={}:{}", + endpointObject.getHostOrIp(), + s.actualPort()); + startPromise.complete(); + return; + } + + String msg = String.format("rest listen failed, address=%s:%d", + endpointObject.getHostOrIp(), + endpointObject.getPort()); + LOGGER.error(msg, f); + startPromise.fail(f); + }); + } + + private HttpServer createHttpServer() { + HttpServerOptions serverOptions = createDefaultHttpServerOptions(); + return vertx.createHttpServer(serverOptions); + } + + private HttpServerOptions createDefaultHttpServerOptions() { + HttpServerOptions serverOptions = new HttpServerOptions(); + serverOptions.setCompressionSupported(TransportConfig.getCompressed()); + serverOptions.setMaxHeaderSize(TransportConfig.getMaxHeaderSize()); + serverOptions.setMaxFormAttributeSize(TransportConfig.getMaxFormAttributeSize()); + serverOptions.setMaxFormFields(TransportConfig.getMaxFormFields()); + serverOptions.setMaxFormBufferedBytes(TransportConfig.getMaxFormBufferedBytes()); + serverOptions.setCompressionLevel(TransportConfig.getCompressionLevel()); + serverOptions.setMaxChunkSize(TransportConfig.getMaxChunkSize()); + serverOptions.setDecompressionSupported(TransportConfig.getDecompressionSupported()); + serverOptions.setDecoderInitialBufferSize(TransportConfig.getDecoderInitialBufferSize()); + serverOptions.setMaxInitialLineLength(TransportConfig.getMaxInitialLineLength()); + if (endpointObject.isHttp2Enabled()) { + serverOptions.setUseAlpn(TransportConfig.getUseAlpn()) + .setHttp2ConnectionWindowSize(TransportConfig.getHttp2ConnectionWindowSize()) + .setIdleTimeout(TransportConfig.getHttp2ConnectionIdleTimeoutInSeconds()) + .setHttp2RstFloodMaxRstFramePerWindow(TransportConfig.getHttp2RstFloodMaxRstFramePerWindow()) + .setInitialSettings(new Http2Settings().setPushEnabled(TransportConfig.getPushEnabled()) + .setMaxConcurrentStreams(TransportConfig.getMaxConcurrentStreams()) + .setHeaderTableSize(TransportConfig.getHttp2HeaderTableSize()) + .setInitialWindowSize(TransportConfig.getInitialWindowSize()) + .setMaxFrameSize(TransportConfig.getMaxFrameSize()) + .setMaxHeaderListSize(TransportConfig.getMaxHeaderListSize()) + ); + } else { + serverOptions.setIdleTimeout(TransportConfig.getConnectionIdleTimeoutInSeconds()); + } + if (endpointObject.isSslEnabled()) { + SSLOptionFactory factory = + SSLOptionFactory.createSSLOptionFactory(SSL_KEY, LegacyPropertyFactory.getEnvironment()); + SSLOption sslOption; + if (factory == null) { + sslOption = SSLOption.build(SSL_KEY, LegacyPropertyFactory.getEnvironment()); + } else { + sslOption = factory.createSSLOption(); + } + SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); + VertxTLSBuilder.buildNetServerOptions(sslOption, sslCustom, serverOptions); + } + + return serverOptions; + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/TransportConfig.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/TransportConfig.java new file mode 100644 index 00000000000..5c81d75646b --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/TransportConfig.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.vertx; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.transport.common.TransportConfigUtils; + +import io.vertx.core.Verticle; +import io.vertx.core.http.Http2Settings; +import io.vertx.core.http.HttpServerOptions; + +public final class TransportConfig { + public static final int DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND = 180; + + public static final boolean DEFAULT_SERVER_COMPRESSION_SUPPORT = false; + + // 32K + public static final int DEFAULT_SERVER_MAX_HEADER_SIZE = 32 * 1024; + + public static final String SERVICECOMB_CORS_CONFIG_BASE = "servicecomb.cors"; + + private static Class restServerVerticle = RestServerVerticle.class; + + private TransportConfig() { + } + + public static Class getRestServerVerticle() { + return restServerVerticle; + } + + public static void setRestServerVerticle(Class restServerVerticle) { + TransportConfig.restServerVerticle = restServerVerticle; + } + + public static String getAddress() { + return LegacyPropertyFactory.getStringProperty("servicecomb.rest.address", null); + } + + public static int getMaxFormAttributeSize() { + return LegacyPropertyFactory.getIntProperty("servicecomb.rest.server.maxFormAttributeSize", + HttpServerOptions.DEFAULT_MAX_FORM_ATTRIBUTE_SIZE); + } + + public static int getMaxFormFields() { + return LegacyPropertyFactory.getIntProperty("servicecomb.rest.server.maxFormFields", + HttpServerOptions.DEFAULT_MAX_FORM_FIELDS); + } + + public static int getMaxFormBufferedBytes() { + return LegacyPropertyFactory.getIntProperty("servicecomb.rest.server.maxFormBufferedBytes", + HttpServerOptions.DEFAULT_MAX_FORM_BUFFERED_SIZE); + } + + public static int getCompressionLevel() { + return LegacyPropertyFactory.getIntProperty("servicecomb.rest.server.compressionLevel", + HttpServerOptions.DEFAULT_COMPRESSION_LEVEL); + } + + public static int getMaxChunkSize() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.server.maxChunkSize", + HttpServerOptions.DEFAULT_MAX_CHUNK_SIZE); + } + + public static int getDecoderInitialBufferSize() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.server.decoderInitialBufferSize", + HttpServerOptions.DEFAULT_DECODER_INITIAL_BUFFER_SIZE); + } + + public static int getHttp2ConnectionWindowSize() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.server.http2.connectionWindowSize", + HttpServerOptions.DEFAULT_HTTP2_CONNECTION_WINDOW_SIZE); + } + + public static int getHttp2RstFloodMaxRstFramePerWindow() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.server.http2.rstFloodMaxRstFramePerWindow", + HttpServerOptions.DEFAULT_HTTP2_RST_FLOOD_MAX_RST_FRAME_PER_WINDOW); + } + + public static int getThreadCount() { + return TransportConfigUtils.readVerticleCount( + "servicecomb.rest.server.verticle-count", + "servicecomb.rest.server.thread-count"); + } + + public static int getConnectionIdleTimeoutInSeconds() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.server.connection.idleTimeoutInSeconds", + DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND); + } + + public static int getHttp2ConnectionIdleTimeoutInSeconds() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.server.http2.connection.idleTimeoutInSeconds", + DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND); + } + + public static boolean getCompressed() { + return LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.server.compression", DEFAULT_SERVER_COMPRESSION_SUPPORT); + } + + public static boolean getDecompressionSupported() { + return LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.server.decompressionSupported", + HttpServerOptions.DEFAULT_DECOMPRESSION_SUPPORTED); + } + + public static long getMaxConcurrentStreams() { + return LegacyPropertyFactory + .getLongProperty("servicecomb.rest.server.http2.concurrentStreams", + HttpServerOptions.DEFAULT_INITIAL_SETTINGS_MAX_CONCURRENT_STREAMS); + } + + public static long getHttp2HeaderTableSize() { + return LegacyPropertyFactory + .getLongProperty("servicecomb.rest.server.http2.HeaderTableSize", + Http2Settings.DEFAULT_HEADER_TABLE_SIZE); + } + + public static boolean getPushEnabled() { + return LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.server.http2.pushEnabled", + Http2Settings.DEFAULT_ENABLE_PUSH); + } + + public static int getInitialWindowSize() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.server.http2.initialWindowSize", + Http2Settings.DEFAULT_INITIAL_WINDOW_SIZE); + } + + public static int getMaxFrameSize() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.server.http2.maxFrameSize", + Http2Settings.DEFAULT_MAX_FRAME_SIZE); + } + + public static int getMaxHeaderListSize() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.server.http2.maxHeaderListSize", + Http2Settings.DEFAULT_MAX_HEADER_LIST_SIZE); + } + + public static boolean getUseAlpn() { + return LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.server.http2.useAlpnEnabled", true); + } + + public static int getMaxHeaderSize() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.server.maxHeaderSize", DEFAULT_SERVER_MAX_HEADER_SIZE); + } + + public static boolean isCorsEnabled() { + return LegacyPropertyFactory + .getBooleanProperty(SERVICECOMB_CORS_CONFIG_BASE + ".enabled", false); + } + + public static String[] getCorsAllowedOrigin() { + return LegacyPropertyFactory + .getProperty(SERVICECOMB_CORS_CONFIG_BASE + ".origin", String[].class); + } + + public static boolean isCorsAllowCredentials() { + return LegacyPropertyFactory + .getBooleanProperty(SERVICECOMB_CORS_CONFIG_BASE + ".allowCredentials", false); + } + + public static Set getCorsAllowedHeaders() { + String allowedHeaders = LegacyPropertyFactory + .getStringProperty(SERVICECOMB_CORS_CONFIG_BASE + ".allowedHeader"); + return convertToSet(allowedHeaders); + } + + public static Set getCorsAllowedMethods() { + String allowedMethods = LegacyPropertyFactory + .getStringProperty(SERVICECOMB_CORS_CONFIG_BASE + ".allowedMethod"); + return convertToSet(allowedMethods); + } + + public static Set getCorsExposedHeaders() { + String exposedHeaders = LegacyPropertyFactory + .getStringProperty(SERVICECOMB_CORS_CONFIG_BASE + ".exposedHeader"); + return convertToSet(exposedHeaders); + } + + public static int getCorsMaxAge() { + return LegacyPropertyFactory + .getIntProperty(SERVICECOMB_CORS_CONFIG_BASE + ".maxAge", -1); + } + + private static Set convertToSet(String setString) { + Set resultSet = new HashSet<>(); + if (!StringUtils.isEmpty(setString)) { + String[] arrString = setString.split(","); + Stream.of(arrString).map(String::trim).filter(str -> !StringUtils.isEmpty(str)) + .forEach(resultSet::add); + } + return resultSet; + } + + public static int getMaxInitialLineLength() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.server.maxInitialLineLength", + HttpServerOptions.DEFAULT_MAX_INITIAL_LINE_LENGTH); + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxHttpDispatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxHttpDispatcher.java new file mode 100644 index 00000000000..cb96d8c2280 --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxHttpDispatcher.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.transport.rest.vertx; + +import org.apache.servicecomb.foundation.common.utils.SPIEnabled; +import org.apache.servicecomb.foundation.common.utils.SPIOrder; + +import io.vertx.ext.web.Router; + +public interface VertxHttpDispatcher extends SPIOrder, SPIEnabled { + @Override + default boolean enabled() { + return true; + } + + void init(Router router); +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestDispatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestDispatcher.java new file mode 100644 index 00000000000..20f83446e57 --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestDispatcher.java @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.vertx; + +import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; +import org.apache.servicecomb.common.rest.RestVertxProducerInvocationCreator; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.core.invocation.InvocationCreator; +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.swagger.invocation.exception.InvocationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.Status.Family; + +public class VertxRestDispatcher extends AbstractVertxHttpDispatcher { + private static final Logger LOGGER = LoggerFactory.getLogger(VertxRestDispatcher.class); + + private static final String KEY_ORDER = "servicecomb.http.dispatcher.rest.order"; + + private static final String KEY_ENABLED = "servicecomb.http.dispatcher.rest.enabled"; + + public static final String KEY_PATTERN = "servicecomb.http.dispatcher.rest.pattern"; + + private Transport transport; + + private MicroserviceMeta microserviceMeta; + + @Override + public int getOrder() { + return LegacyPropertyFactory.getIntProperty(KEY_ORDER, Integer.MAX_VALUE); + } + + @Override + public boolean enabled() { + return LegacyPropertyFactory.getBooleanProperty(KEY_ENABLED, true); + } + + @Override + public void init(Router router) { + // cookies handler are enabled by default start from 3.8.3 + String pattern = LegacyPropertyFactory.getStringProperty(KEY_PATTERN); + if (pattern == null) { + router.route().handler(createBodyHandler()); + router.route().failureHandler(this::failureHandler).handler(this::onRequest); + } else { + router.routeWithRegex(pattern).handler(createBodyHandler()); + router.routeWithRegex(pattern).failureHandler(this::failureHandler).handler(this::onRequest); + } + } + + protected void failureHandler(RoutingContext context) { + LOGGER.error("http server failed.", context.failure()); + + Throwable e = context.failure(); + if (e instanceof ErrorDataDecoderException) { + Throwable cause = e.getCause(); + if (cause instanceof InvocationException) { + e = cause; + } + } + + // only when unexpected exception happens, it will run into here. + // the connection should be closed. + handleFailureAndClose(context, e); + } + + /** + * Try to find out the failure information and send it in response. + */ + private void handleFailureAndClose(RoutingContext context, Throwable e) { + if (null != e) { + // if there exists exception, try to send this exception by RoutingContext + sendExceptionByRoutingContext(context, e); + return; + } + + // if there is no exception, the response is determined by status code. + sendFailureRespDeterminedByStatus(context); + } + + /** + * Try to determine response by status code, and send response. + */ + private void sendFailureRespDeterminedByStatus(RoutingContext context) { + Family statusFamily = Family.familyOf(context.statusCode()); + if (Family.CLIENT_ERROR.equals(statusFamily) || Family.SERVER_ERROR.equals(statusFamily) || Family.OTHER + .equals(statusFamily)) { + context.response().putHeader(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD) + .setStatusCode(context.statusCode()).end(); + } else { + // it seems the status code is not set properly + context.response().putHeader(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD) + .setStatusCode(Status.INTERNAL_SERVER_ERROR.getStatusCode()) + .setStatusMessage(Status.INTERNAL_SERVER_ERROR.getReasonPhrase()) + .end(wrapResponseBody(Status.INTERNAL_SERVER_ERROR.getReasonPhrase())); + } + } + + /** + * Use routingContext to send failure information in throwable. + */ + private void sendExceptionByRoutingContext(RoutingContext context, Throwable e) { + if (e instanceof InvocationException invocationException) { + context.response().putHeader(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD) + .setStatusCode(invocationException.getStatusCode()).setStatusMessage(invocationException.getReasonPhrase()) + .end(wrapResponseBody(invocationException.getReasonPhrase())); + } else { + context.response().putHeader(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD) + .setStatusCode(Status.INTERNAL_SERVER_ERROR.getStatusCode()) + .end(wrapResponseBody(Status.INTERNAL_SERVER_ERROR.getReasonPhrase())); + } + } + + /** + * Consumer will treat the response body as json by default, so it's necessary to wrap response body as Json string + * to avoid deserialization error. + * + * @param message response body + * @return response body wrapped as Json string + */ + String wrapResponseBody(String message) { + if (isValidJson(message)) { + return message; + } + + JsonObject jsonObject = new JsonObject(); + jsonObject.put("message", message); + + return jsonObject.toString(); + } + + /** + * Check if the message is a valid Json string. + * @param message the message to be checked. + * @return true if message is a valid Json string, otherwise false. + */ + private boolean isValidJson(String message) { + try { + new JsonObject(message); + } catch (Exception ignored) { + return false; + } + return true; + } + + protected void onRequest(RoutingContext context) { + if (transport == null) { + transport = SCBEngine.getInstance().getTransportManager().findTransport(CoreConst.RESTFUL); + microserviceMeta = SCBEngine.getInstance().getProducerMicroserviceMeta(); + } + HttpServletRequestEx requestEx = new VertxServerRequestToHttpServletRequest(context); + HttpServletResponseEx responseEx = new VertxServerResponseToHttpServletResponse(context.response()); + + InvocationCreator creator = new RestVertxProducerInvocationCreator(context, + microserviceMeta, transport.getEndpoint(), + requestEx, responseEx); + new RestProducerInvocationFlow(creator, requestEx, responseEx) + .run(); + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestTransport.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestTransport.java new file mode 100644 index 00000000000..8566a3c3b6b --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestTransport.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.vertx; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.transport.AbstractTransport; +import org.apache.servicecomb.foundation.common.net.NetUtils; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.foundation.common.utils.BeanUtils; +import org.apache.servicecomb.foundation.common.utils.ClassLoaderScopeContext; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.foundation.vertx.SimpleJsonObject; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.DeploymentOptions; +import io.vertx.core.VertxOptions; + +public class VertxRestTransport extends AbstractTransport { + private static final Logger LOGGER = LoggerFactory.getLogger(VertxRestTransport.class); + + @Override + public String getName() { + return CoreConst.RESTFUL; + } + + @Override + public int getOrder() { + return -1000; + } + + @Override + public boolean canInit() { + String pattern = environment.getProperty(VertxRestDispatcher.KEY_PATTERN, String.class); + String urlPrefix = null; + if (pattern == null || pattern.length() <= 5) { + setListenAddressWithoutSchema(TransportConfig.getAddress()); + } else { + // e.g. "/api/(.*)" -> "/api" + urlPrefix = pattern.substring(0, pattern.length() - 5); + setListenAddressWithoutSchema(TransportConfig.getAddress(), + Collections.singletonMap(DefinitionConst.URL_PREFIX, urlPrefix)); + } + + URIEndpointObject ep = (URIEndpointObject) getEndpoint().getAddress(); + if (ep == null) { + return true; + } + + if (!NetUtils.canTcpListen(ep.getSocketAddress().getAddress(), ep.getPort())) { + LOGGER.warn( + "Can not start VertxRestTransport, the port:{} may have been occupied. " + + "You can ignore this message if you are using a web container like tomcat.", + ep.getPort()); + return false; + } + + if (urlPrefix != null) { + ClassLoaderScopeContext.setClassLoaderScopeProperty(DefinitionConst.URL_PREFIX, urlPrefix); + } + + return true; + } + + @Override + public boolean init() throws Exception { + // 部署transport server + DeploymentOptions options = new DeploymentOptions().setInstances(TransportConfig.getThreadCount()); + SimpleJsonObject json = new SimpleJsonObject(); + json.put(ENDPOINT_KEY, getEndpoint()); + options.setConfig(json); + options.setWorkerPoolName("pool-worker-transport-rest"); + options.setWorkerPoolSize(VertxOptions.DEFAULT_WORKER_POOL_SIZE); + + prepareBlockResource(); + Map result = VertxUtils.blockDeploy(transportVertx, TransportConfig.getRestServerVerticle(), + options); + if ((boolean) result.get("code")) { + return true; + } else { + throw new IllegalStateException((String) result.get("message")); + } + } + + private void prepareBlockResource() { + // block deploy will load resources in event loop, but beans auto wire can only be done in main thread + List dispatchers = SPIServiceUtils.getOrLoadSortedService(VertxHttpDispatcher.class); + BeanUtils.addBeans(VertxHttpDispatcher.class, dispatchers); + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketDispatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketDispatcher.java new file mode 100644 index 00000000000..5f8e2cd329d --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketDispatcher.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.vertx; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.EdgeServerWebSocketInvocationCreator; +import org.apache.servicecomb.common.rest.ProviderServerWebSocketInvocationCreator; +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.CoreConst; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +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.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +import com.google.common.eventbus.Subscribe; + +import io.vertx.core.http.ServerWebSocket; +import jakarta.ws.rs.core.Response.Status; + +public class WebSocketDispatcher { + private static final String KEY_MAPPING_PREFIX = "servicecomb.http.dispatcher.edge.websocket.mappings"; + + private final Object LOCK = new Object(); + + private volatile boolean initialized = false; + + private Endpoint endpoint; + + private MicroserviceMeta microserviceMeta; + + private boolean isEdge; + + private Map configurations = new HashMap<>(); + + public WebSocketDispatcher(Endpoint endpoint) { + this.endpoint = endpoint; + EventManager.register(this); + } + + private void loadConfigurations() { + configurations = URLMappedConfigurationLoader.loadConfigurations( + LegacyPropertyFactory.getEnvironment(), 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(ServerWebSocket webSocket) { + if (!initialized) { + synchronized (LOCK) { + if (!initialized) { + Transport transport = SCBEngine.getInstance().getTransportManager().findTransport(CoreConst.WEBSOCKET); + this.microserviceMeta = SCBEngine.getInstance().getProducerMicroserviceMeta(); + this.endpoint = new Endpoint(transport, this.endpoint.getEndpoint()); + this.isEdge = TransportConfig.getRestServerVerticle() + .getName().equals("org.apache.servicecomb.edge.core.EdgeRestServerVerticle"); + if (this.isEdge) { + loadConfigurations(); + } + } + initialized = true; + } + } + + InvocationCreator creator; + if (isEdge) { + URLMappedConfigurationItem configurationItem = findConfigurationItem(webSocket.path()); + if (configurationItem == null) { + throw new InvocationException(Status.NOT_FOUND, new CommonExceptionData( + String.format("path %s not found", webSocket.path()))); + } + String path = Utils.findActualPath(webSocket.path(), configurationItem.getPrefixSegmentCount()); + creator = new EdgeServerWebSocketInvocationCreator( + configurationItem.getMicroserviceName(), path, endpoint, webSocket); + } else { + creator = new ProviderServerWebSocketInvocationCreator(microserviceMeta, + endpoint, webSocket); + } + new WebSocketProducerInvocationFlow(creator, webSocket).run(); + } + + private URLMappedConfigurationItem findConfigurationItem(String path) { + for (URLMappedConfigurationItem item : configurations.values()) { + if (item.getPattern().matcher(path).matches()) { + return item; + } + } + return null; + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketProducerInvocationFlow.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketProducerInvocationFlow.java new file mode 100644 index 00000000000..1297da53a51 --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketProducerInvocationFlow.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.transport.rest.vertx; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.invocation.InvocationCreator; +import org.apache.servicecomb.core.invocation.ProducerInvocationFlow; +import org.apache.servicecomb.swagger.invocation.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.http.ServerWebSocket; + +public class WebSocketProducerInvocationFlow extends ProducerInvocationFlow { + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketProducerInvocationFlow.class); + + private final ServerWebSocket websocket; + + public WebSocketProducerInvocationFlow(InvocationCreator invocationCreator, ServerWebSocket webSocket) { + super(invocationCreator); + this.websocket = webSocket; + } + + @Override + protected Invocation sendCreateInvocationException(Throwable throwable) { + LOGGER.error("Web socket create invocation error.", throwable); + websocket.writeTextMessage("Web socket create invocation error " + throwable.getMessage()); + websocket.close(); + return null; + } + + @Override + protected void endResponse(Invocation invocation, Response response) { + + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketTransport.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketTransport.java new file mode 100644 index 00000000000..9bcd614a374 --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketTransport.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.transport.rest.vertx; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.transport.AbstractTransport; + +public class WebSocketTransport extends AbstractTransport { + @Override + public String getName() { + return CoreConst.WEBSOCKET; + } + + @Override + public int getOrder() { + return -500; + } + + @Override + public boolean init() throws Exception { + return true; + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport b/transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport new file mode 100644 index 00000000000..74ae694b980 --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport @@ -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.transport.rest.vertx.VertxRestTransport +org.apache.servicecomb.transport.rest.vertx.WebSocketTransport diff --git a/transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher b/transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher new file mode 100644 index 00000000000..794ec701a9d --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher @@ -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.transport.rest.vertx.VertxRestDispatcher diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/MockForRestServerVerticle.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/MockForRestServerVerticle.java deleted file mode 100644 index 58e54a1945a..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/MockForRestServerVerticle.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import org.mockito.Mockito; - -import io.servicecomb.foundation.common.net.IpPort; -import io.vertx.core.Future; -import io.vertx.core.http.HttpServer; -import mockit.Mock; -import mockit.MockUp; - -public class MockForRestServerVerticle { - - private static MockForRestServerVerticle instance = new MockForRestServerVerticle(); - - private MockForRestServerVerticle() { - // private constructor for Singleton - } - - public static MockForRestServerVerticle getInstance() { - return instance; - } - - public void mockRestServerVerticle() { - final HttpServer server = Mockito.mock(HttpServer.class); - new MockUp() { - - @Mock - private void startListen(HttpServer server, IpPort ipPort, Future startFuture) { - - } - - @Mock - private HttpServer createHttpServer(boolean isHttp_2) { - - return server; - } - }; - } - - public void mockTransportConfig() { - new MockUp() { - @Mock - public String getAddress() { - return "Address"; - } - }; - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestRestServerVerticle.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestRestServerVerticle.java deleted file mode 100644 index 96190b4e48d..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestRestServerVerticle.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import io.servicecomb.core.CseContext; -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Transport; -import io.servicecomb.core.transport.AbstractTransport; -import io.servicecomb.core.transport.TransportManager; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.vertx.core.Context; -import io.vertx.core.Future; -import io.vertx.core.Vertx; -import io.vertx.core.json.JsonObject; -import mockit.Expectations; -import mockit.Mocked; - -public class TestRestServerVerticle { - - private RestServerVerticle instance = null; - - Future startFuture = null; - - @Before - public void setUp() throws Exception { - instance = new RestServerVerticle(); - startFuture = Future.future(); - - CseContext.getInstance().setTransportManager(new TransportManager()); - } - - @After - public void tearDown() throws Exception { - instance = null; - startFuture = null; - } - - @Test - public void testRestServerVerticleWithRouter(@Mocked Transport transport, @Mocked Vertx vertx, - @Mocked Context context, - @Mocked JsonObject jsonObject, @Mocked Future startFuture) throws Exception { - URIEndpointObject endpointObject = new URIEndpointObject("http://127.0.0.1:8080"); - new Expectations() { - { - transport.parseAddress("http://127.0.0.1:8080"); - result = endpointObject; - } - }; - Endpoint endpiont = new Endpoint(transport, "http://127.0.0.1:8080"); - - new Expectations() { - { - context.config(); - result = jsonObject; - jsonObject.getValue(AbstractTransport.ENDPOINT_KEY); - result = endpiont; - } - }; - RestServerVerticle server = new RestServerVerticle(); - // process stuff done by Expectations - server.init(vertx, context); - server.start(startFuture); - } - - @Test - public void testRestServerVerticleWithRouterSSL(@Mocked Transport transport, @Mocked Vertx vertx, - @Mocked Context context, - @Mocked JsonObject jsonObject, @Mocked Future startFuture) throws Exception { - URIEndpointObject endpointObject = new URIEndpointObject("http://127.0.0.1:8080?sslEnabled=true"); - new Expectations() { - { - transport.parseAddress("http://127.0.0.1:8080?sslEnabled=true"); - result = endpointObject; - } - }; - Endpoint endpiont = new Endpoint(transport, "http://127.0.0.1:8080?sslEnabled=true"); - - new Expectations() { - { - context.config(); - result = jsonObject; - jsonObject.getValue(AbstractTransport.ENDPOINT_KEY); - result = endpiont; - } - }; - RestServerVerticle server = new RestServerVerticle(); - // process stuff done by Expectations - server.init(vertx, context); - server.start(startFuture); - } - - @Test - public void testStartFutureAddressEmpty() { - boolean status = false; - try { - instance.start(startFuture); - } catch (Exception ex) { - status = true; - } - Assert.assertFalse(status); - } - - @Test - public void testStartFutureAddressNotEmpty() { - boolean status = false; - MockForRestServerVerticle.getInstance().mockTransportConfig(); - MockForRestServerVerticle.getInstance().mockRestServerVerticle(); - try { - instance.start(startFuture); - } catch (Exception ex) { - status = true; - } - Assert.assertFalse(status); - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestRestVertxHttpRequest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestRestVertxHttpRequest.java deleted file mode 100644 index e6a2703b71a..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestRestVertxHttpRequest.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.vertx.core.Future; -import io.vertx.core.MultiMap; -import io.vertx.core.http.HttpServerRequest; -import io.vertx.ext.web.Cookie; -import io.vertx.ext.web.RoutingContext; -import mockit.Deencapsulation; - -public class TestRestVertxHttpRequest { - - private RestVertxHttpRequest instance = null; - - @Before - public void init() { - instance = new RestVertxHttpRequest(getRoutingContext(), getFutureObject()); - } - - @Test - public void testRestVertxHttpRequest() { - Assert.assertNotNull(instance); - } - - @Test - public void testGetPath() { - Deencapsulation.setField(instance, "request", getHttpServerRequest()); - Assert.assertNull(instance.getPath()); - } - - @Test - public void testSetPathParamMap() { - Map pathParamMap = new HashMap(); - pathParamMap.put("key", "value"); - instance.setPathParamMap(pathParamMap); - Assert.assertNotNull(instance.getPathParam("key")); - } - - @Test - public void testGetQueryParam() { - boolean status = true; - try { - - HttpServerRequest httpServerRequest = Mockito.mock(HttpServerRequest.class); - Deencapsulation.setField(instance, "request", httpServerRequest); - MultiMap multiMap = Mockito.mock(MultiMap.class); - Mockito.when(httpServerRequest.params()).thenReturn(multiMap); - List stringList = new ArrayList(); - stringList.add("sters"); - Mockito.when(multiMap.getAll("key")).thenReturn(stringList); - String[] str = instance.getQueryParam("key"); - Assert.assertEquals("sters", str[0]); - } catch (Exception ex) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testGetQueryParamisNull() { - boolean status = true; - try { - - HttpServerRequest httpServerRequest = Mockito.mock(HttpServerRequest.class); - Deencapsulation.setField(instance, "request", httpServerRequest); - MultiMap multiMap = Mockito.mock(MultiMap.class); - Mockito.when(httpServerRequest.params()).thenReturn(multiMap); - List stringList = null; - - Mockito.when(multiMap.getAll("key")).thenReturn(stringList); - String[] str = instance.getQueryParam("key"); - Assert.assertNull(str); - } catch (Exception ex) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testGetHeaderParam() { - boolean status = false; - try { - HttpServerRequest httpServerRequest = Mockito.mock(HttpServerRequest.class); - Deencapsulation.setField(instance, "request", httpServerRequest); - MultiMap multiMap = Mockito.mock(MultiMap.class); - Mockito.when(httpServerRequest.headers()).thenReturn(multiMap); - - @SuppressWarnings({"unchecked"}) - Iterator> iterator = Mockito.mock(Iterator.class); - Mockito.when(multiMap.iterator()).thenReturn(iterator); - Mockito.when(iterator.hasNext()).thenReturn(true).thenReturn(false); - Assert.assertNotNull(instance.getHeaderParam("key")); - } catch (Exception ex) { - status = true; - } - Assert.assertTrue(status); - } - - @Test - public void testGetFormParam() { - boolean status = false; - try { - Assert.assertNotNull(instance.getFormParam("key")); - } catch (Exception ex) { - status = true; - } - Assert.assertTrue(status); - } - - @Test - public void testGetCookieParam() { - Cookie cookie = Mockito.mock(Cookie.class); - RoutingContext context = Mockito.mock(RoutingContext.class); - Deencapsulation.setField(instance, "context", context); - Mockito.when(context.getCookie("key")).thenReturn(cookie); - Assert.assertNull(instance.getCookieParam("key")); - } - - @Test - public void testGetBody() { - boolean status = false; - try { - Assert.assertNull(instance.getBody()); - } catch (Exception ex) { - status = true; - } - Assert.assertTrue(status); - } - - @Test - public void testGetQueryParams() { - boolean status = true; - try { - HttpServerRequest httpServerRequest = Mockito.mock(HttpServerRequest.class); - Deencapsulation.setField(instance, "request", httpServerRequest); - MultiMap multiMap = Mockito.mock(MultiMap.class); - Mockito.when(httpServerRequest.params()).thenReturn(multiMap); - List stringList = new ArrayList(); - stringList.add("sters"); - Set stringSet = new HashSet(); - stringSet.add("sters"); - Mockito.when(multiMap.names()).thenReturn(stringSet); - Mockito.when(multiMap.getAll("key")).thenReturn(stringList); - Assert.assertNotNull(instance.getQueryParams()); - } catch (Exception ex) { - status = false; - } - Assert.assertTrue(status); - } - - @Test - public void testGetHttpRequest() { - init(); - Assert.assertNull(instance.getHttpRequest()); - } - - private RoutingContext getRoutingContext() { - return Mockito.mock(RoutingContext.class); - } - - @SuppressWarnings("unchecked") - private Future getFutureObject() { - return Mockito.mock(Future.class); - } - - private HttpServerRequest getHttpServerRequest() { - return Mockito.mock(HttpServerRequest.class); - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestVertxHttpRequestCreator.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestVertxHttpRequestCreator.java deleted file mode 100644 index 804825b0f65..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestVertxHttpRequestCreator.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestVertxHttpRequestCreator { - - private ProducerVertxHttpRequestArgMapper instance = null; - - @Before - public void setUp() throws Exception { - instance = new ProducerVertxHttpRequestArgMapper(null); - } - - @After - public void tearDown() throws Exception { - instance = null; - } - - @Test - public void testVertxHttpRequestCreator() { - Assert.assertNotNull(instance); - } - - @Test - public void testCreateMockParam() { - Object obj = instance.createContextArg(null); - Assert.assertNotNull(obj); - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestVertxRestServer.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestVertxRestServer.java deleted file mode 100644 index e6d2508e435..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestVertxRestServer.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.common.rest.codec.RestServerRequestInternal; -import io.servicecomb.common.rest.codec.produce.ProduceProcessor; -import io.servicecomb.common.rest.definition.RestOperationMeta; -import io.servicecomb.core.Invocation; -import io.servicecomb.swagger.invocation.Response; -import io.vertx.core.http.HttpServerResponse; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.RoutingContext; -import io.vertx.ext.web.handler.BodyHandler; -import mockit.Deencapsulation; - -public class TestVertxRestServer { - - private VertxRestServer instance = null; - - @Before - public void setUp() throws Exception { - Router mainRouter = Router.router(null); - mainRouter.route().handler(BodyHandler.create()); - instance = new VertxRestServer(mainRouter) { - @Override - protected RestOperationMeta findRestOperation(RestServerRequestInternal restRequest) { - return super.findRestOperation(restRequest); - } - - @Override - public void sendFailResponse(RestServerRequestInternal restRequest, HttpServerResponse httpResponse, - Throwable throwable) { - } - }; - } - - @After - public void tearDown() throws Exception { - instance = null; - } - - @Test - public void testSetHttpRequestContext() { - boolean status = false; - try { - Invocation invocation = Mockito.mock(Invocation.class); - RestServerRequestInternal restRequest = Mockito.mock(RestServerRequestInternal.class); - instance.setHttpRequestContext(invocation, restRequest); - Assert.assertNotNull(instance); - } catch (Exception e) { - status = true; - } - Assert.assertFalse(status); - } - - @Test - public void testVertxRestServer() { - Assert.assertNotNull(instance); - } - - @Test - public void testDoSend() { - boolean status = false; - try { - HttpServerResponse httpServerResponse = Mockito.mock(HttpServerResponse.class); - ProduceProcessor produceProcessor = Mockito.mock(ProduceProcessor.class); - Response response = Response.create(0, "reasonPhrase", new Object()); - instance.doSendResponse(httpServerResponse, produceProcessor, response); - } catch (Exception e) { - status = true; - } - Assert.assertFalse(status); - } - - @Test - public void testFailureHandler() { - RoutingContext context = Mockito.mock(RoutingContext.class); - Mockito.when(context.response()).thenReturn(Mockito.mock(HttpServerResponse.class)); - boolean status = false; - try { - Deencapsulation.invoke(instance, "failureHandler", context); - } catch (Exception e) { - status = true; - } - Assert.assertFalse(status); - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestVertxRestTransport.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestVertxRestTransport.java deleted file mode 100644 index f081a51f154..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestVertxRestTransport.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import java.io.IOException; -import java.net.ServerSocket; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import io.servicecomb.core.Endpoint; -import io.servicecomb.core.Invocation; -import io.servicecomb.foundation.common.net.URIEndpointObject; -import io.servicecomb.foundation.vertx.VertxUtils; -import io.servicecomb.swagger.invocation.AsyncResponse; -import io.vertx.core.AbstractVerticle; -import io.vertx.core.DeploymentOptions; -import io.vertx.core.Vertx; -import io.vertx.core.VertxOptions; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; - -public class TestVertxRestTransport { - - private VertxRestTransport instance = new VertxRestTransport(); - - @Test - public void testGetInstance() { - Assert.assertNotNull(instance); - } - - @Test - public void testGetName() { - Assert.assertEquals("rest", instance.getName()); - } - - @Test - public void testInit() { - boolean status = false; - try { - new MockUp() { - @Mock - public Vertx init(VertxOptions vertxOptions) { - return null; - } - - @Mock - public boolean blockDeploy(Vertx vertx, Class cls, - DeploymentOptions options) throws InterruptedException { - return true; - } - }; - instance.init(); - } catch (Exception e) { - status = true; - } - Assert.assertFalse(status); - } - - @Test - public void testSendException() { - boolean validAssert; - Invocation invocation = Mockito.mock(Invocation.class); - AsyncResponse asyncResp = Mockito.mock(AsyncResponse.class); - URIEndpointObject endpoint = Mockito.mock(URIEndpointObject.class); - Endpoint end = Mockito.mock(Endpoint.class); - Mockito.when(invocation.getEndpoint()).thenReturn(end); - Mockito.when(invocation.getEndpoint().getAddress()).thenReturn(endpoint); - try { - validAssert = true; - instance.send(invocation, asyncResp); - } catch (Exception e) { - - validAssert = false; - } - Assert.assertFalse(validAssert); - } - - @Test - public void testGetOrder() { - VertxRestTransport transport = new VertxRestTransport(); - Assert.assertEquals(-1000, transport.getOrder()); - } - - @Test - public void testCanInitNullAddress() throws IOException { - new Expectations(TransportConfig.class) { - { - TransportConfig.getAddress(); - result = null; - } - }; - - VertxRestTransport transport = new VertxRestTransport(); - Assert.assertTrue(transport.canInit()); - } - - @Test - public void testCanInitListened() throws IOException { - ServerSocket ss = new ServerSocket(0); - int port = ss.getLocalPort(); - - new Expectations(TransportConfig.class) { - { - TransportConfig.getAddress(); - result = "0.0.0.0:" + port; - } - }; - - VertxRestTransport transport = new VertxRestTransport(); - Assert.assertFalse(transport.canInit()); - - ss.close(); - } - - @Test - public void testCanInitNotListened() throws IOException { - ServerSocket ss = new ServerSocket(0); - int port = ss.getLocalPort(); - ss.close(); - - new Expectations(TransportConfig.class) { - { - TransportConfig.getAddress(); - result = "0.0.0.0:" + port; - } - }; - - VertxRestTransport transport = new VertxRestTransport(); - Assert.assertTrue(transport.canInit()); - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestVertxToServletMockRequest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestVertxToServletMockRequest.java deleted file mode 100644 index e643ace4185..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/TestVertxToServletMockRequest.java +++ /dev/null @@ -1,939 +0,0 @@ -/* - * Copyright 2017 Huawei Technologies Co., Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.servicecomb.transport.rest.vertx; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -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.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import io.vertx.core.MultiMap; -import io.vertx.core.http.HttpServerRequest; -import io.vertx.core.net.SocketAddress; -import mockit.Deencapsulation; - -public class TestVertxToServletMockRequest { - - private VertxToServletMockRequest instance = null; - - private void init() { - instance = new VertxToServletMockRequest(getHttpServerRequest()); - } - - @Before - public void setUp() throws Exception { - instance = new VertxToServletMockRequest(null); - } - - @After - public void tearDown() throws Exception { - instance = null; - } - - @Test - public void testVertxToServletMockRequest() { - init(); - Assert.assertNotNull(instance); - } - - @Test - public void testGetAttribute() { - try { - init(); - instance.getAttribute("name"); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetAttributeNames() { - try { - init(); - instance.getAttributeNames(); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetCharacterEncoding() { - try { - init(); - instance.getCharacterEncoding(); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testSetCharacterEncoding() { - try { - init(); - try { - instance.setCharacterEncoding("env"); - } catch (UnsupportedEncodingException e) { - Assert.assertNotNull(e); - } - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetContentLength() { - try { - init(); - instance.getContentLength(); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetContentLengthLong() { - try { - init(); - instance.getContentLengthLong(); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetContentType() { - try { - init(); - instance.getContentType(); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetInputStream() { - try { - init(); - try { - instance.getInputStream(); - } catch (IOException e) { - Assert.assertNotNull(e); - } - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetParameter() { - try { - init(); - instance.getParameter("param"); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetParameterNames() { - try { - init(); - instance.getParameterNames(); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetParameterValues() { - boolean validAssert = true; - try { - HttpServerRequest httpServerRequest = Mockito.mock(HttpServerRequest.class); - Deencapsulation.setField(instance, "vertxRequest", httpServerRequest); - MultiMap multiMap = Mockito.mock(MultiMap.class); - Mockito.when(httpServerRequest.params()).thenReturn(multiMap); - List stringList = new ArrayList(); - stringList.add("sters"); - Mockito.when(multiMap.getAll("key")).thenReturn(stringList); - Assert.assertNotNull(instance.getParameterValues("param")); - } catch (Exception e) { - Assert.assertNotNull(e); - validAssert = false; - } catch (Error e) { - Assert.assertNotNull(e); - validAssert = false; - } - Assert.assertTrue(validAssert); - } - - @Test - public void testGetParameterMap() { - boolean validAssert = true; - try { - init(); - @SuppressWarnings("unused") - Map paramMap = new HashMap<>(); - HttpServerRequest httpServerRequest = Mockito.mock(HttpServerRequest.class); - Deencapsulation.setField(instance, "vertxRequest", httpServerRequest); - MultiMap multiMap = Mockito.mock(MultiMap.class); - Mockito.when(httpServerRequest.params()).thenReturn(multiMap); - Set stringSet = new HashSet(); - stringSet.add("sters"); - Mockito.when(multiMap.names()).thenReturn(stringSet); - Assert.assertNotNull(instance.getParameterMap()); - } catch (Exception e) { - Assert.assertNotNull(e); - validAssert = false; - } catch (Error e) { - Assert.assertNotNull(e); - validAssert = false; - } - Assert.assertTrue(validAssert); - } - - @Test - public void testGetProtocol() { - try { - init(); - instance.getProtocol(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetScheme() { - try { - init(); - instance.getScheme(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetServerName() { - try { - init(); - instance.getServerName(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetServerPort() { - try { - init(); - - instance.getServerPort(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetReader() { - try { - init(); - instance.getReader(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetRemoteAddr() { - try { - init(); - instance.getRemoteAddr(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetRemoteHost() { - try { - init(); - HttpServerRequest httpServerRequest = Mockito.mock(HttpServerRequest.class); - Deencapsulation.setField(instance, "vertxRequest", httpServerRequest); - SocketAddress socketAddress = Mockito.mock(SocketAddress.class); - Mockito.when(httpServerRequest.remoteAddress()).thenReturn(socketAddress); - Mockito.when(socketAddress.host()).thenReturn("localhost"); - Assert.assertEquals("localhost", instance.getRemoteHost()); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testSetAttribute() { - try { - init(); - instance.setAttribute("name", null); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testRemoveAttribute() { - try { - init(); - instance.removeAttribute("name"); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetLocale() { - try { - init(); - instance.getLocale(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetLocales() { - try { - init(); - instance.getLocales(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testIsSecure() { - try { - init(); - instance.isSecure(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetRequestDispatcher() { - try { - init(); - instance.getRequestDispatcher("request"); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetRealPath() { - try { - init(); - instance.getRealPath("path"); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetRemotePort() { - try { - init(); - HttpServerRequest httpServerRequest = Mockito.mock(HttpServerRequest.class); - Deencapsulation.setField(instance, "vertxRequest", httpServerRequest); - SocketAddress socketAddress = Mockito.mock(SocketAddress.class); - Mockito.when(httpServerRequest.remoteAddress()).thenReturn(socketAddress); - Mockito.when(socketAddress.port()).thenReturn(8080); - Assert.assertEquals(8080, instance.getRemotePort()); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetLocalName() { - try { - init(); - instance.getLocalName(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetLocalAddr() { - try { - init(); - HttpServerRequest httpServerRequest = Mockito.mock(HttpServerRequest.class); - Deencapsulation.setField(instance, "vertxRequest", httpServerRequest); - SocketAddress socketAddress = Mockito.mock(SocketAddress.class); - Mockito.when(httpServerRequest.localAddress()).thenReturn(socketAddress); - Mockito.when(socketAddress.host()).thenReturn("localhost"); - Assert.assertEquals("localhost", instance.getLocalAddr()); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetLocalPort() { - try { - init(); - HttpServerRequest httpServerRequest = Mockito.mock(HttpServerRequest.class); - Deencapsulation.setField(instance, "vertxRequest", httpServerRequest); - SocketAddress socketAddress = Mockito.mock(SocketAddress.class); - Mockito.when(httpServerRequest.localAddress()).thenReturn(socketAddress); - Mockito.when(socketAddress.port()).thenReturn(8080); - instance.getLocalPort(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetServletContext() { - try { - init(); - instance.getServletContext(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testStartAsync() { - try { - init(); - instance.startAsync(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testStartAsyncServletRequestServletResponse() { - try { - init(); - instance.startAsync(null, null); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testIsAsyncStarted() { - try { - init(); - instance.isAsyncStarted(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testIsAsyncSupported() { - try { - init(); - instance.isAsyncSupported(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetAsyncContext() { - try { - init(); - instance.getAsyncContext(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetDispatcherType() { - try { - init(); - instance.getDispatcherType(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetAuthType() { - try { - init(); - instance.getAuthType(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetCookies() { - try { - init(); - instance.getCookies(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetDateHeader() { - try { - init(); - instance.getDateHeader("name"); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetHeader() { - try { - init(); - instance.getHeader("name"); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetHeaders() { - try { - init(); - instance.getHeaders("name"); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetHeaderNames() { - try { - init(); - instance.getHeaderNames(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetIntHeader() { - try { - init(); - Mockito.when(instance.getHeader("1234")).thenReturn("1234"); - Assert.assertEquals(1234, instance.getIntHeader("1234")); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetIntHeaderException() { - try { - init(); - Mockito.when(instance.getHeader("name")).thenReturn("name"); - Assert.assertEquals(0, instance.getIntHeader("name")); - } catch (Exception e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetMethod() { - try { - init(); - - instance.getMethod(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetPathInfo() { - try { - init(); - instance.getPathInfo(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetPathTranslated() { - try { - init(); - instance.getPathTranslated(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetContextPath() { - try { - init(); - instance.getContextPath(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetQueryString() { - try { - init(); - instance.getQueryString(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetRemoteUser() { - try { - init(); - instance.getRemoteUser(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testIsUserInRole() { - try { - init(); - instance.isUserInRole("role"); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetUserPrincipal() { - try { - init(); - instance.getUserPrincipal(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetRequestedSessionId() { - try { - init(); - instance.getRequestedSessionId(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetRequestURI() { - try { - init(); - instance.getRequestURI(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetRequestURL() { - try { - init(); - instance.getRequestURL(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetServletPath() { - try { - init(); - instance.getServletPath(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetSessionBoolean() { - try { - init(); - instance.getSession(false); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetSession() { - try { - init(); - instance.getSession(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testChangeSessionId() { - try { - init(); - instance.changeSessionId(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testIsRequestedSessionIdValid() { - try { - init(); - instance.isRequestedSessionIdValid(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testIsRequestedSessionIdFromCookie() { - try { - init(); - instance.isRequestedSessionIdFromCookie(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testIsRequestedSessionIdFromURL() { - try { - init(); - instance.isRequestedSessionIdFromUrl(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testIsRequestedSessionIdFromUrl() { - try { - init(); - instance.isRequestedSessionIdFromURL(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testAuthenticate() { - try { - init(); - instance.authenticate(null); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testLogin() { - try { - init(); - instance.login(null, null); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testLogout() { - try { - init(); - instance.logout(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetParts() { - try { - init(); - instance.getParts(); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testGetPart() { - try { - init(); - instance.getPart("name"); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - @Test - public void testUpgrade() { - try { - init(); - instance.upgrade(null); - } catch (Exception e) { - Assert.assertNotNull(e); - } catch (Error e) { - Assert.assertNotNull(e); - } - } - - private HttpServerRequest getHttpServerRequest() { - return Mockito.mock(HttpServerRequest.class); - } -} diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/MockForRestServerVerticle.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/MockForRestServerVerticle.java new file mode 100644 index 00000000000..965f14ed894 --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/MockForRestServerVerticle.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.transport.rest.vertx; + +import org.apache.servicecomb.foundation.common.net.IpPort; +import org.mockito.Mockito; + +import io.vertx.core.Future; +import io.vertx.core.http.HttpServer; +import mockit.Mock; +import mockit.MockUp; + +public class MockForRestServerVerticle { + + private static final MockForRestServerVerticle instance = new MockForRestServerVerticle(); + + private MockForRestServerVerticle() { + // private constructor for Singleton + } + + public static MockForRestServerVerticle getInstance() { + return instance; + } + + public void mockRestServerVerticle() { + final HttpServer server = Mockito.mock(HttpServer.class); + new MockUp() { + + @Mock + private void startListen(HttpServer server, IpPort ipPort, Future startFuture) { + + } + + @Mock + private HttpServer createHttpServer(boolean isHttp_2) { + + return server; + } + }; + } + + public void mockTransportConfig() { + new MockUp() { + @Mock + public String getAddress() { + return "Address"; + } + }; + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/MockHttpServerResponse.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/MockHttpServerResponse.java new file mode 100644 index 00000000000..74a8307262b --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/MockHttpServerResponse.java @@ -0,0 +1,300 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.vertx; + +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import io.vertx.codegen.annotations.Nullable; +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.Cookie; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.net.HostAndPort; + +class MockHttpServerResponse implements HttpServerResponse { + boolean responseEnded; + + Map responseHeader = new HashMap<>(1); + + int responseStatusCode; + + String responseStatusMessage; + + String responseChunk; + + @Override + public HttpServerResponse putHeader(String name, String value) { + responseHeader.put(name, value); + return this; + } + + @Override + public HttpServerResponse setStatusCode(int statusCode) { + responseStatusCode = statusCode; + return this; + } + + @Override + public HttpServerResponse setStatusMessage(String statusMessage) { + responseStatusMessage = statusMessage; + return this; + } + + @Override + public Future end() { + responseEnded = true; + return Future.succeededFuture(); + } + + @Override + public Future end(String chunk) { + responseEnded = true; + responseChunk = chunk; + return Future.succeededFuture(); + } + + @Override + public HttpServerResponse exceptionHandler(Handler handler) { + return null; + } + + @Override + public Future write(Buffer data) { + return Future.succeededFuture(); + } + + @Override + public HttpServerResponse setWriteQueueMaxSize(int maxSize) { + return null; + } + + @Override + public boolean writeQueueFull() { + return false; + } + + @Override + public HttpServerResponse drainHandler(Handler handler) { + return null; + } + + @Override + public int getStatusCode() { + return 0; + } + + @Override + public String getStatusMessage() { + return null; + } + + @Override + public HttpServerResponse setChunked(boolean chunked) { + return null; + } + + @Override + public boolean isChunked() { + return false; + } + + @Override + public MultiMap headers() { + return null; + } + + @Override + public HttpServerResponse putHeader(CharSequence name, CharSequence value) { + return null; + } + + @Override + public HttpServerResponse putHeader(String name, Iterable values) { + return null; + } + + @Override + public HttpServerResponse putHeader(CharSequence name, Iterable values) { + return null; + } + + @Override + public MultiMap trailers() { + return null; + } + + @Override + public HttpServerResponse putTrailer(String name, String value) { + return null; + } + + @Override + public HttpServerResponse putTrailer(CharSequence name, CharSequence value) { + return null; + } + + @Override + public HttpServerResponse putTrailer(String name, Iterable values) { + return null; + } + + @Override + public HttpServerResponse putTrailer(CharSequence name, Iterable value) { + return null; + } + + @Override + public HttpServerResponse closeHandler(Handler handler) { + return null; + } + + @Override + public HttpServerResponse endHandler(Handler handler) { + return null; + } + + @Override + public Future writeHead() { + return null; + } + + @Override + public Future write(String chunk, String enc) { + return Future.succeededFuture(); + } + + + @Override + public Future write(String chunk) { + return Future.succeededFuture(); + } + + @Override + public Future writeContinue() { + return null; + } + + @Override + public Future writeEarlyHints(MultiMap headers) { + return Future.succeededFuture(); + } + + @Override + public Future end(String chunk, String enc) { + return Future.succeededFuture(); + } + + + @Override + public Future end(Buffer chunk) { + return Future.succeededFuture(); + } + + + @Override + public Future sendFile(String filename, long offset, long length) { + return Future.succeededFuture(); + } + + @Override + public Future sendFile(FileChannel channel, long offset, long length) { + return null; + } + + @Override + public Future sendFile(RandomAccessFile file, long offset, long length) { + return null; + } + + @Override + public boolean ended() { + return false; + } + + @Override + public boolean closed() { + return false; + } + + @Override + public boolean headWritten() { + return false; + } + + @Override + public HttpServerResponse headersEndHandler(Handler handler) { + return null; + } + + @Override + public HttpServerResponse bodyEndHandler(Handler handler) { + return null; + } + + @Override + public long bytesWritten() { + return 0; + } + + @Override + public int streamId() { + return 0; + } + + @Override + public Future push(HttpMethod httpMethod, HostAndPort hostAndPort, String s, MultiMap multiMap) { + return null; + } + + @Override + public Future reset(long code) { + return null; + } + + @Override + public Future writeCustomFrame(int type, int flags, Buffer payload) { + return null; + } + + @Override + public HttpServerResponse addCookie(Cookie cookie) { + return null; + } + + @Override + public @Nullable Cookie removeCookie(String name, boolean invalidate) { + return null; + } + + @Override + public Set removeCookies(String name, boolean invalidate) { + return null; + } + + @Override + public @Nullable Cookie removeCookie(String name, String domain, String path, boolean invalidate) { + return null; + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestAbstractVertxHttpDispatcher.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestAbstractVertxHttpDispatcher.java new file mode 100644 index 00000000000..6636b87c468 --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestAbstractVertxHttpDispatcher.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.vertx; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.runners.MethodSorters; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import io.vertx.ext.web.Router; +import mockit.Deencapsulation; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestAbstractVertxHttpDispatcher { + static class AbstractVertxHttpDispatcherForTest extends AbstractVertxHttpDispatcher { + @Override + public int getOrder() { + return 0; + } + + @Override + public void init(Router router) { + } + } + + Environment environment = Mockito.mock(Environment.class); + + @Before + public void setUp() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty( + RestConst.UPLOAD_MAX_SIZE, long.class, -1L)) + .thenReturn(-1L); + Mockito.when(environment.getProperty(RestConst.UPLOAD_MAX_FILE_SIZE, long.class, -1L)) + .thenReturn(-1L); + Mockito.when(environment.getProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, int.class, 0)) + .thenReturn(0); + } + + @Test + public void createBodyHandlerUploadDefault() { + Mockito.when(environment.getProperty( + "servicecomb.uploads.directory", RestConst.UPLOAD_DEFAULT_DIR)) + .thenReturn(RestConst.UPLOAD_DEFAULT_DIR); + + AbstractVertxHttpDispatcher dispatcher = new AbstractVertxHttpDispatcherForTest(); + RestBodyHandler bodyHandler = (RestBodyHandler) dispatcher.createBodyHandler(); + + Assertions.assertTrue(bodyHandler.isDeleteUploadedFilesOnEnd()); + Assertions.assertEquals(RestConst.UPLOAD_DEFAULT_DIR, Deencapsulation.getField(bodyHandler, "uploadsDir")); + } + + @Test + public void createBodyHandlerUploadNormal() { + Mockito.when(environment.getProperty( + "servicecomb.uploads.directory", RestConst.UPLOAD_DEFAULT_DIR)) + .thenReturn("/path"); + + AbstractVertxHttpDispatcher dispatcher = new AbstractVertxHttpDispatcherForTest(); + RestBodyHandler bodyHandler = (RestBodyHandler) dispatcher.createBodyHandler(); + + Assertions.assertTrue(bodyHandler.isDeleteUploadedFilesOnEnd()); + Assertions.assertEquals("/path", Deencapsulation.getField(bodyHandler, "uploadsDir")); + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestRestServerVerticle.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestRestServerVerticle.java new file mode 100644 index 00000000000..4154cc635c5 --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestRestServerVerticle.java @@ -0,0 +1,385 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.vertx; + +import static io.vertx.core.http.HttpServerOptions.DEFAULT_COMPRESSION_LEVEL; +import static io.vertx.core.http.HttpServerOptions.DEFAULT_DECODER_INITIAL_BUFFER_SIZE; +import static io.vertx.core.http.HttpServerOptions.DEFAULT_DECOMPRESSION_SUPPORTED; +import static io.vertx.core.http.HttpServerOptions.DEFAULT_HTTP2_CONNECTION_WINDOW_SIZE; +import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_CHUNK_SIZE; +import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_FORM_ATTRIBUTE_SIZE; +import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_FORM_BUFFERED_SIZE; +import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_FORM_FIELDS; +import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_INITIAL_LINE_LENGTH; +import static org.apache.servicecomb.common.accessLog.AccessLogConfig.CLIENT_LOG_ENABLED; +import static org.apache.servicecomb.common.accessLog.AccessLogConfig.CLIENT_LOG_PATTERN; +import static org.apache.servicecomb.common.accessLog.AccessLogConfig.DEFAULT_CLIENT_PATTERN; +import static org.apache.servicecomb.common.accessLog.AccessLogConfig.DEFAULT_SERVER_PATTERN; +import static org.apache.servicecomb.common.accessLog.AccessLogConfig.SERVER_LOG_ENABLED; +import static org.apache.servicecomb.common.accessLog.AccessLogConfig.SERVER_LOG_PATTERN; +import static org.apache.servicecomb.core.transport.AbstractTransport.PUBLISH_ADDRESS; +import static org.apache.servicecomb.transport.rest.vertx.TransportConfig.DEFAULT_SERVER_COMPRESSION_SUPPORT; +import static org.apache.servicecomb.transport.rest.vertx.TransportConfig.DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND; +import static org.apache.servicecomb.transport.rest.vertx.TransportConfig.DEFAULT_SERVER_MAX_HEADER_SIZE; +import static org.apache.servicecomb.transport.rest.vertx.TransportConfig.SERVICECOMB_CORS_CONFIG_BASE; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.core.Endpoint; +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.AbstractTransport; +import org.apache.servicecomb.foundation.common.Holder; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +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 io.vertx.core.Context; +import io.vertx.core.Handler; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import io.vertx.core.http.Http2Settings; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.impl.SysProps; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.CorsHandler; +import mockit.Expectations; +import mockit.Mock; +import mockit.MockUp; +import mockit.Mocked; + +public class TestRestServerVerticle { + + private RestServerVerticle instance = null; + + Promise startPromise = null; + + Environment environment = Mockito.mock(Environment.class); + + @Before + public void setUp() { + Mockito.when(environment.getProperty( + "servicecomb.request.timeout", long.class, (long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT)) + .thenReturn((long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT); + Mockito.when(environment.getProperty("servicecomb.rest.client.verticle-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.rest.client.thread-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.rest.server.verticle-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.rest.server.thread-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.http.dispatcher.rest.order", int.class, Integer.MAX_VALUE)) + .thenReturn(Integer.MAX_VALUE); + Mockito.when(environment.getProperty("servicecomb.rest.publishPort", int.class, 0)) + .thenReturn(0); + Mockito.when(environment.getProperty("servicecomb.http.dispatcher.rest.enabled", boolean.class, true)) + .thenReturn(true); + Mockito.when(environment.getProperty(SERVICECOMB_CORS_CONFIG_BASE + ".enabled", boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty(PUBLISH_ADDRESS, "")) + .thenReturn(""); + Mockito.when(environment.getProperty( + RestConst.UPLOAD_MAX_SIZE, long.class, -1L)) + .thenReturn(-1L); + Mockito.when(environment.getProperty(RestConst.UPLOAD_MAX_FILE_SIZE, long.class, -1L)) + .thenReturn(-1L); + Mockito.when(environment.getProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, int.class, 0)) + .thenReturn(0); + Mockito.when(environment.getProperty("servicecomb.rest.server.compression", boolean.class, + DEFAULT_SERVER_COMPRESSION_SUPPORT)) + .thenReturn(false); + Mockito.when(environment.getProperty("servicecomb.rest.server.maxHeaderSize", int.class, + DEFAULT_SERVER_MAX_HEADER_SIZE)) + .thenReturn(DEFAULT_SERVER_MAX_HEADER_SIZE); + Mockito.when(environment.getProperty("servicecomb.rest.server.maxFormAttributeSize", int.class, + DEFAULT_MAX_FORM_ATTRIBUTE_SIZE)) + .thenReturn(DEFAULT_MAX_FORM_ATTRIBUTE_SIZE); + Mockito.when(environment.getProperty("servicecomb.rest.server.compressionLevel", int.class, + DEFAULT_COMPRESSION_LEVEL)) + .thenReturn(DEFAULT_COMPRESSION_LEVEL); + Mockito.when(environment.getProperty("servicecomb.rest.server.maxFormFields", int.class, + DEFAULT_MAX_FORM_FIELDS)) + .thenReturn(DEFAULT_MAX_FORM_FIELDS); + Mockito.when(environment.getProperty("servicecomb.rest.server.maxFormBufferedBytes", int.class, + DEFAULT_MAX_FORM_BUFFERED_SIZE)) + .thenReturn(DEFAULT_MAX_FORM_BUFFERED_SIZE); + Mockito.when(environment.getProperty("servicecomb.rest.server.maxChunkSize", int.class, + DEFAULT_MAX_CHUNK_SIZE)) + .thenReturn(DEFAULT_MAX_CHUNK_SIZE); + Mockito.when(environment.getProperty("servicecomb.rest.server.decompressionSupported", boolean.class, + DEFAULT_DECOMPRESSION_SUPPORTED)) + .thenReturn(DEFAULT_DECOMPRESSION_SUPPORTED); + Mockito.when(environment.getProperty("servicecomb.rest.server.decoderInitialBufferSize", int.class, + DEFAULT_DECODER_INITIAL_BUFFER_SIZE)) + .thenReturn(DEFAULT_DECODER_INITIAL_BUFFER_SIZE); + Mockito.when(environment.getProperty("servicecomb.rest.server.maxInitialLineLength", int.class, + DEFAULT_MAX_INITIAL_LINE_LENGTH)) + .thenReturn(DEFAULT_MAX_INITIAL_LINE_LENGTH); + Mockito.when(environment.getProperty("servicecomb.rest.server.connection.idleTimeoutInSeconds", int.class, + DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND)) + .thenReturn(DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND); + Mockito.when(environment.getProperty("servicecomb.rest.server.http2.useAlpnEnabled", boolean.class, + true)) + .thenReturn(true); + Mockito.when(environment.getProperty("servicecomb.rest.server.http2ConnectionWindowSize", int.class, + DEFAULT_HTTP2_CONNECTION_WINDOW_SIZE)) + .thenReturn(DEFAULT_HTTP2_CONNECTION_WINDOW_SIZE); + Mockito.when(environment.getProperty("servicecomb.rest.server.http2.connection.idleTimeoutInSeconds", int.class, + DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND)) + .thenReturn(DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND); + Mockito.when(environment.getProperty("servicecomb.rest.server.http2.pushEnabled", boolean.class, + Http2Settings.DEFAULT_ENABLE_PUSH)) + .thenReturn(Http2Settings.DEFAULT_ENABLE_PUSH); + 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); + Mockito.when(environment.getProperty(CLIENT_LOG_ENABLED, boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty(SERVER_LOG_ENABLED, boolean.class, false)) + .thenReturn(false); + Mockito.when(environment.getProperty(CLIENT_LOG_PATTERN, String.class, DEFAULT_CLIENT_PATTERN)) + .thenReturn(DEFAULT_CLIENT_PATTERN); + Mockito.when(environment.getProperty(SERVER_LOG_PATTERN, String.class, DEFAULT_SERVER_PATTERN)) + .thenReturn(DEFAULT_CLIENT_PATTERN); + LegacyPropertyFactory.setEnvironment(environment); + + instance = new RestServerVerticle(); + startPromise = Promise.promise(); + + SCBBootstrap.createSCBEngineForTest(environment); + } + + @After + public void tearDown() { + instance = null; + startPromise = null; + SCBEngine.getInstance().destroy(); + } + + @Test + public void testRestServerVerticleWithRouter(@Mocked Transport transport, @Mocked Vertx vertx, + @Mocked Context context, + @Mocked JsonObject jsonObject, @Mocked Promise startPromise) throws Exception { + + URIEndpointObject endpointObject = new URIEndpointObject("http://127.0.0.1:8080"); + new Expectations() { + { + transport.parseAddress("http://127.0.0.1:8080"); + result = endpointObject; + } + }; + Endpoint endpoint = new Endpoint(transport, "http://127.0.0.1:8080"); + + new Expectations() { + { + context.config(); + result = jsonObject; + jsonObject.getValue(AbstractTransport.ENDPOINT_KEY); + result = endpoint; + } + }; + RestServerVerticle server = new RestServerVerticle(); + // process stuff done by Expectations + server.init(vertx, context); + server.start(startPromise); + } + + @Test + public void testRestServerVerticleWithRouterSSL(@Mocked Transport transport, @Mocked Vertx vertx, + @Mocked Context context, + @Mocked JsonObject jsonObject, @Mocked Promise startPromise) throws Exception { + URIEndpointObject endpointObject = new URIEndpointObject("http://127.0.0.1:8080?sslEnabled=true"); + new Expectations() { + { + transport.parseAddress("http://127.0.0.1:8080?sslEnabled=true"); + result = endpointObject; + } + }; + Endpoint endpoint = new Endpoint(transport, "http://127.0.0.1:8080?sslEnabled=true"); + + new Expectations() { + { + context.config(); + result = jsonObject; + jsonObject.getValue(AbstractTransport.ENDPOINT_KEY); + result = endpoint; + } + }; + RestServerVerticle server = new RestServerVerticle(); + // process stuff done by Expectations + server.init(vertx, context); + server.start(startPromise); + } + + + @Test + public void testStartFutureAddressEmpty() { + boolean status = false; + try { + instance.start(startPromise); + } catch (Exception ex) { + status = true; + } + Assertions.assertFalse(status); + } + + @Test + public void testStartFutureAddressNotEmpty() { + boolean status = false; + MockForRestServerVerticle.getInstance().mockTransportConfig(); + MockForRestServerVerticle.getInstance().mockRestServerVerticle(); + try { + instance.start(startPromise); + } catch (Exception ex) { + status = true; + } + Assertions.assertFalse(status); + } + + @Test + public void testMountCorsHandler() { + Mockito.when(environment.getProperty("servicecomb.cors.enabled", boolean.class, + false)) + .thenReturn(true); + Mockito.when(environment.getProperty("servicecomb.cors.origin", + String[].class)) + .thenReturn(null); + Mockito.when(environment.getProperty("servicecomb.cors.allowedMethod")) + .thenReturn("GET,PUT,POST"); + Mockito.when(environment.getProperty("servicecomb.cors.allowedHeader")) + .thenReturn("abc,def"); + Mockito.when(environment.getProperty("servicecomb.cors.exposedHeader")) + .thenReturn("abc2,def2"); + Mockito.when(environment.getProperty("servicecomb.cors.maxAge", int.class, -1)) + .thenReturn(1); + Mockito.when(environment.getProperty("servicecomb.cors.allowCredentials", boolean.class, false)) + .thenReturn(false); + + Set methodSet = new HashSet<>(3); + methodSet.add(HttpMethod.GET); + methodSet.add(HttpMethod.PUT); + methodSet.add(HttpMethod.POST); + AtomicInteger counter = new AtomicInteger(0); + + CorsHandler corsHandler = new MockUp() { + @Mock + CorsHandler allowCredentials(boolean allow) { + Assertions.assertFalse(allow); + counter.incrementAndGet(); + return null; + } + + @Mock + CorsHandler allowedHeaders(Set headerNames) { + MatcherAssert.assertThat(headerNames, Matchers.containsInAnyOrder("abc", "def")); + counter.incrementAndGet(); + return null; + } + + @Mock + CorsHandler exposedHeaders(Set headerNames) { + MatcherAssert.assertThat(headerNames, Matchers.containsInAnyOrder("abc2", "def2")); + counter.incrementAndGet(); + return null; + } + + @Mock + CorsHandler allowedMethod(HttpMethod method) { + Assertions.assertTrue(methodSet.contains(method)); + counter.incrementAndGet(); + methodSet.remove(method); + return null; + } + + @Mock + CorsHandler maxAgeSeconds(int maxAgeSeconds) { + Assertions.assertEquals(1, maxAgeSeconds); + counter.incrementAndGet(); + return null; + } + }.getMockInstance(); + + new MockUp() { + @Mock + CorsHandler getCorsHandler() { + return corsHandler; + } + }; + Router router = Mockito.mock(Router.class); + Mockito.when(router.route()).thenReturn(Mockito.mock(Route.class)); + + RestServerVerticle server = new RestServerVerticle(); + + server.mountCorsHandler(router); + Assertions.assertEquals(7, counter.get()); + } + + @Test + public void mountGlobalRestFailureHandler() { + Router mainRouter = Mockito.mock(Router.class); + Holder> handlerHolder = new Holder<>(); + Holder routeHolder = new Holder<>(); + Route route = new MockUp() { + @Mock + Route failureHandler(Handler failureHandler) { + handlerHolder.value = failureHandler; + return null; + } + + @Mock + Route handler(io.vertx.core.Handler requestHandler) { + return routeHolder.value; + } + }.getMockInstance(); + routeHolder.value = route; + + Mockito.when(mainRouter.route()).thenReturn(route); + + RestServerVerticle restServerVerticle = new RestServerVerticle(); + + restServerVerticle.mountGlobalRestFailureHandler(mainRouter); + Assertions.assertNotNull(handlerHolder.value); + + RoutingContext routingContext = Mockito.mock(RoutingContext.class); + HttpServerResponse response = Mockito.mock(HttpServerResponse.class); + Mockito.when(response.setStatusCode(500)).thenReturn(response); + Mockito.when(response.putHeader("Content-Type", "application/json")).thenReturn(response); + Mockito.when(routingContext.response()).thenReturn(response); + + handlerHolder.value.handle(routingContext); + Mockito.verify(response).end("{\"message\":\"unknown error\"}"); + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestVertxRestDispatcher.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestVertxRestDispatcher.java new file mode 100644 index 00000000000..db5edd19f8a --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestVertxRestDispatcher.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.transport.rest.vertx; + +import org.apache.http.HttpHeaders; +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.bootstrap.SCBBootstrap; +import org.apache.servicecomb.core.transport.TransportManager; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +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 io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response.Status; +import mockit.Mocked; + +public class TestVertxRestDispatcher { + @Mocked + Router mainRouter; + + @Mocked + TransportManager transportManager; + + VertxRestDispatcher dispatcher; + + Throwable throwable; + + Environment environment = Mockito.mock(Environment.class); + + @Before + public void setUp() { + LegacyPropertyFactory.setEnvironment(environment); + Mockito.when(environment.getProperty( + RestConst.UPLOAD_MAX_SIZE, long.class, -1L)) + .thenReturn(-1L); + Mockito.when(environment.getProperty(RestConst.UPLOAD_MAX_FILE_SIZE, long.class, -1L)) + .thenReturn(-1L); + Mockito.when(environment.getProperty(RestConst.UPLOAD_FILE_SIZE_THRESHOLD, int.class, 0)) + .thenReturn(0); + + dispatcher = new VertxRestDispatcher(); + dispatcher.init(mainRouter); + + SCBBootstrap.createSCBEngineForTest(environment).setTransportManager(transportManager); + } + + @After + public void teardown() { + SCBEngine.getInstance().destroy(); + } + + @Test + public void getOrder() { + Mockito.when(environment.getProperty( + "servicecomb.http.dispatcher.rest.order", int.class, Integer.MAX_VALUE)) + .thenReturn(Integer.MAX_VALUE); + Assertions.assertEquals(Integer.MAX_VALUE, dispatcher.getOrder()); + } + + @Test + public void failureHandlerWithNoRestProducerInvocationAndInvocationException() { + RoutingContext context = Mockito.mock(RoutingContext.class); + Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(null); + InvocationException e = new InvocationException(Status.REQUEST_ENTITY_TOO_LARGE, "testMsg"); + ErrorDataDecoderException edde = new ErrorDataDecoderException(e); + Mockito.when(context.failure()).thenReturn(edde); + MockHttpServerResponse response = new MockHttpServerResponse(); + Mockito.when(context.response()).thenReturn(response); + + dispatcher.failureHandler(context); + + MatcherAssert.assertThat(response.responseHeader, Matchers.hasEntry(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD)); + MatcherAssert.assertThat(response.responseStatusCode, Matchers.is(Status.REQUEST_ENTITY_TOO_LARGE.getStatusCode())); + MatcherAssert.assertThat(response.responseStatusMessage, + Matchers.is(Status.REQUEST_ENTITY_TOO_LARGE.getReasonPhrase())); + MatcherAssert.assertThat(response.responseChunk, + Matchers.is("{\"message\":\"" + Status.REQUEST_ENTITY_TOO_LARGE.getReasonPhrase() + "\"}")); + Assertions.assertTrue(response.responseEnded); + } + + @Test + public void failureHandlerWithNoRestProducerInvocationAndOtherException() { + RoutingContext context = Mockito.mock(RoutingContext.class); + Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(null); + String exceptionMessage = "Internal Server Error"; + Exception exception = new Exception(exceptionMessage); + Mockito.when(context.failure()).thenReturn(exception); + MockHttpServerResponse response = new MockHttpServerResponse(); + Mockito.when(context.response()).thenReturn(response); + + dispatcher.failureHandler(context); + + MatcherAssert.assertThat(response.responseHeader, Matchers.hasEntry(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD)); + MatcherAssert.assertThat(response.responseStatusCode, Matchers.is(Status.INTERNAL_SERVER_ERROR.getStatusCode())); + MatcherAssert.assertThat(response.responseChunk, + Matchers.is("{\"message\":\"" + exceptionMessage + "\"}")); + Assertions.assertTrue(response.responseEnded); + } + + @Test + public void failureHandlerWithNoExceptionAndStatusCodeIsSet() { + RoutingContext context = Mockito.mock(RoutingContext.class); + Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(null); + Mockito.when(context.failure()).thenReturn(null); + MockHttpServerResponse response = new MockHttpServerResponse(); + Mockito.when(context.response()).thenReturn(response); + Mockito.when(context.statusCode()).thenReturn(Status.REQUEST_ENTITY_TOO_LARGE.getStatusCode()); + + dispatcher.failureHandler(context); + + MatcherAssert.assertThat(response.responseHeader, Matchers.hasEntry(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD)); + MatcherAssert.assertThat(response.responseStatusCode, Matchers.is(Status.REQUEST_ENTITY_TOO_LARGE.getStatusCode())); + Assertions.assertTrue(response.responseEnded); + } + + @Test + public void failureHandlerWithNoExceptionAndStatusCodeIsNotSet() { + RoutingContext context = Mockito.mock(RoutingContext.class); + Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(null); + Mockito.when(context.failure()).thenReturn(null); + MockHttpServerResponse response = new MockHttpServerResponse(); + Mockito.when(context.response()).thenReturn(response); + Mockito.when(context.statusCode()).thenReturn(Status.OK.getStatusCode()); + + dispatcher.failureHandler(context); + + MatcherAssert.assertThat(response.responseHeader, Matchers.hasEntry(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD)); + MatcherAssert.assertThat(response.responseStatusCode, Matchers.is(Status.INTERNAL_SERVER_ERROR.getStatusCode())); + MatcherAssert.assertThat(response.responseStatusMessage, + Matchers.is(Status.INTERNAL_SERVER_ERROR.getReasonPhrase())); + MatcherAssert.assertThat(response.responseChunk, + Matchers.is("{\"message\":\"" + Status.INTERNAL_SERVER_ERROR.getReasonPhrase() + "\"}")); + Assertions.assertTrue(response.responseEnded); + } + + @Test + public void testWrapResponseBody() { + VertxRestDispatcher vertxRestDispatcher = new VertxRestDispatcher(); + String message = "abcd"; + String bodyString = vertxRestDispatcher.wrapResponseBody(message); + Assertions.assertNotNull(bodyString); + Assertions.assertEquals("{\"message\":\"abcd\"}", bodyString); + + message = "\"abcd\""; + bodyString = vertxRestDispatcher.wrapResponseBody(message); + Assertions.assertNotNull(bodyString); + Assertions.assertEquals("{\"message\":\"\\\"abcd\\\"\"}", bodyString); + + message = ".01ab\"!@#$%^&*()'\\cd"; + bodyString = vertxRestDispatcher.wrapResponseBody(message); + Assertions.assertNotNull(bodyString); + Assertions.assertEquals("{\"message\":\".01ab\\\"!@#$%^&*()'\\\\cd\"}", bodyString); + + message = new JsonObject().put("key", new JsonObject().put("k2", "value")).toString(); + bodyString = vertxRestDispatcher.wrapResponseBody(message); + Assertions.assertNotNull(bodyString); + Assertions.assertEquals("{\"key\":{\"k2\":\"value\"}}", bodyString); + + message = "ab\"23\n@!#cd"; + bodyString = vertxRestDispatcher.wrapResponseBody(message); + Assertions.assertNotNull(bodyString); + Assertions.assertEquals("{\"message\":\"ab\\\"23\\n@!#cd\"}", bodyString); + + message = "ab\"23\r\n@!#cd"; + bodyString = vertxRestDispatcher.wrapResponseBody(message); + Assertions.assertNotNull(bodyString); + Assertions.assertEquals("{\"message\":\"ab\\\"23\\r\\n@!#cd\"}", bodyString); + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestVertxRestTransport.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestVertxRestTransport.java new file mode 100644 index 00000000000..fb14f5115de --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestVertxRestTransport.java @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.vertx; + +import static org.apache.servicecomb.core.transport.AbstractTransport.PUBLISH_ADDRESS; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.vertx.VertxUtils; +import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; +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.AbstractVerticle; +import io.vertx.core.DeploymentOptions; +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.impl.SysProps; +import mockit.Expectations; +import mockit.Mock; +import mockit.MockUp; + +public class TestVertxRestTransport { + + private VertxRestTransport instance; + + Environment environment = Mockito.mock(Environment.class); + + @Before + public void setUp() { + Mockito.when(environment.getProperty( + "servicecomb.request.timeout", long.class, (long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT)) + .thenReturn((long) TcpClientConfig.DEFAULT_LOGIN_TIMEOUT); + Mockito.when(environment.getProperty("servicecomb.rest.client.verticle-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.rest.client.thread-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.rest.server.verticle-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.rest.server.thread-count", int.class, -1)) + .thenReturn(-1); + Mockito.when(environment.getProperty("servicecomb.http.dispatcher.rest.order", int.class, Integer.MAX_VALUE)) + .thenReturn(Integer.MAX_VALUE); + Mockito.when(environment.getProperty("servicecomb.rest.publishPort", int.class, 0)) + .thenReturn(0); + Mockito.when(environment.getProperty(PUBLISH_ADDRESS, "")) + .thenReturn(""); + 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); + instance = new VertxRestTransport(); + instance.setEnvironment(environment); + } + + @Test + public void testGetInstance() { + Assertions.assertNotNull(instance); + } + + @Test + public void testGetName() { + Assertions.assertEquals("rest", instance.getName()); + } + + @Test + public void testInit() { + boolean status = false; + try { + new MockUp() { + @Mock + public Vertx init(VertxOptions vertxOptions) { + return null; + } + + @Mock + public Map blockDeploy(Vertx vertx, Class cls, + DeploymentOptions options) throws InterruptedException { + Map result = new HashMap<>(); + result.put("code", true); + return result; + } + }; + instance.init(); + } catch (Exception e) { + e.printStackTrace(); + status = true; + } + Assertions.assertFalse(status); + } + + @Test + public void testGetOrder() { + VertxRestTransport transport = new VertxRestTransport(); + Assertions.assertEquals(-1000, transport.getOrder()); + } + + @Test + public void testCanInitNullAddress() throws IOException { + new Expectations(TransportConfig.class) { + { + TransportConfig.getAddress(); + result = null; + } + }; + + VertxRestTransport transport = new VertxRestTransport(); + Environment environment = Mockito.mock(Environment.class); + transport.setEnvironment(environment); + Assertions.assertTrue(transport.canInit()); + } + + @Test + public void testCanInitListened() throws IOException { + ServerSocket ss = new ServerSocket(0); + int port = ss.getLocalPort(); + + new Expectations(TransportConfig.class) { + { + TransportConfig.getAddress(); + result = "0.0.0.0:" + port; + } + }; + + VertxRestTransport transport = new VertxRestTransport(); + transport.setEnvironment(environment); + Assertions.assertFalse(transport.canInit()); + + ss.close(); + } + + @Test + public void testCanInitNotListened() throws IOException { + ServerSocket ss = new ServerSocket(0); + int port = ss.getLocalPort(); + ss.close(); + + new Expectations(TransportConfig.class) { + { + TransportConfig.getAddress(); + result = "0.0.0.0:" + port; + } + }; + + VertxRestTransport transport = new VertxRestTransport(); + transport.setEnvironment(environment); + Assertions.assertTrue(transport.canInit()); + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/test/resources/log4j.properties b/transports/transport-rest/transport-rest-vertx/src/test/resources/log4j.properties deleted file mode 100644 index 82ef8a6612c..00000000000 --- a/transports/transport-rest/transport-rest-vertx/src/test/resources/log4j.properties +++ /dev/null @@ -1,29 +0,0 @@ -# -# Copyright 2017 Huawei Technologies Co., Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -log4j.rootLogger=INFO, out, stdout - -# CONSOLE appender not used by default -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n - -# File appender -log4j.appender.out=org.apache.log4j.FileAppender -log4j.appender.out.layout=org.apache.log4j.PatternLayout -log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n -log4j.appender.out.file=target/test.log -log4j.appender.out.append=true